Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
},
"require-dev": {
"laminas/laminas-coding-standard": "^3.1",
"phpunit/phpunit": "^12.5",
"phpunit/phpunit": "^12.5.22",
"psalm/plugin-phpunit": "^0.19.5",
"vimeo/psalm": "^6.14"
"vimeo/psalm": "^6.16"
},
"autoload": {
"psr-4": {
Expand Down
339 changes: 172 additions & 167 deletions composer.lock

Large diffs are not rendered by default.

14 changes: 6 additions & 8 deletions psalm.baseline.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="6.14.3@d0b040a91f280f071c1abcb1b77ce3822058725a">
<files psalm-version="6.16.1@f1f5de594dc76faf8784e02d3dc4716c91c6f6ac">
<file src="src/ApiProblem.php">
<MixedAssignment>
<code><![CDATA[$prop]]></code>
Expand All @@ -10,10 +10,6 @@
<PossiblyInvalidPropertyAssignmentValue>
<code><![CDATA[$additional]]></code>
</PossiblyInvalidPropertyAssignmentValue>
<PossiblyUnusedMethod>
<code><![CDATA[__get]]></code>
<code><![CDATA[setDetailIncludesStackTrace]]></code>
</PossiblyUnusedMethod>
</file>
<file src="src/ApiProblemResponse.php">
<FalsableReturnStatement>
Expand All @@ -25,8 +21,10 @@
<PossiblyFalseArgument>
<code><![CDATA[$json]]></code>
</PossiblyFalseArgument>
<UnusedClass>
<code><![CDATA[ApiProblemResponse]]></code>
</UnusedClass>
</file>
<file src="test/ApiProblemTest.php">
<UnusedVariable>
<code><![CDATA[$a]]></code>
</UnusedVariable>
</file>
</files>
3 changes: 0 additions & 3 deletions src/ApiProblem.php
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,6 @@ protected function getTitle(): ?string
return get_class($this->detail);
}

if (null === $this->title) {
return 'Unknown';
}
return 'Unknown';
}

Expand Down
4 changes: 3 additions & 1 deletion src/ApiProblemResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use const JSON_HEX_APOS;
use const JSON_HEX_QUOT;
use const JSON_HEX_TAG;
use const JSON_PARTIAL_OUTPUT_ON_ERROR;
use const JSON_THROW_ON_ERROR;
use const JSON_UNESCAPED_SLASHES;

Expand All @@ -32,7 +33,8 @@ final class ApiProblemResponse extends Response
| JSON_HEX_APOS
| JSON_HEX_AMP
| JSON_HEX_QUOT
| JSON_UNESCAPED_SLASHES;
| JSON_UNESCAPED_SLASHES
| JSON_PARTIAL_OUTPUT_ON_ERROR;

public function __construct(ApiProblem $apiProblem)
{
Expand Down
53 changes: 53 additions & 0 deletions src/Exception/DomainException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Lmc\Api\Problem\Exception;

use Override;
use Traversable;

final class DomainException extends \DomainException implements ExceptionInterface, ProblemExceptionInterface
{
protected ?string $type = null;

protected array $details = [];

protected ?string $title = null;

public function setAdditionalDetails(array $details): self
{
$this->details = $details;
return $this;
}

public function setType(string $uri): self
{
$this->type = $uri;
return $this;
}

public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}

#[Override]
public function getAdditionalDetails(): Traversable|array|null
{
return $this->details;
}

#[Override]
public function getType(): ?string
{
return $this->type;
}

#[Override]
public function getTitle(): ?string
{
return $this->title;
}
}
4 changes: 2 additions & 2 deletions src/Exception/ProblemExceptionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface ProblemExceptionInterface
{
public function getAdditionalDetails(): Traversable|array|null;

public function getType(): string;
public function getType(): ?string;

public function getTitle(): string;
public function getTitle(): ?string;
}
64 changes: 64 additions & 0 deletions test/ApiProblemResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace LmcTest\Api\Problem;

use Lmc\Api\Problem\ApiProblem;
use Lmc\Api\Problem\ApiProblemResponse;
use Lmc\Api\Problem\Exception;
use PHPUnit\Framework\TestCase;

use function fopen;
use function json_decode;
use function strtolower;

final class ApiProblemResponseTest extends TestCase
{
public function testApiProblemResponseSetsStatusCodeAndReasonPhrase(): void
{
$response = new ApiProblemResponse(new ApiProblem(400, 'Random error'));
$this->assertEquals(400, $response->getStatusCode());
$this->assertIsString($response->getReasonPhrase());
$this->assertNotEmpty($response->getReasonPhrase());
$this->assertEquals('bad request', strtolower($response->getReasonPhrase()));
}

public function testApiProblemResponseSetsStatusCodeAndReasonPhraseUsingException(): void
{
$exception = new Exception\DomainException('Random error', 400);
$response = new ApiProblemResponse(new ApiProblem(400, $exception));
$this->assertEquals(400, $response->getStatusCode());
$this->assertIsString($response->getReasonPhrase());
$this->assertNotEmpty($response->getReasonPhrase());
$this->assertEquals('bad request', strtolower($response->getReasonPhrase()));
}

public function testApiProblemResponseBodyIsSerializedApiProblem(): void
{
$additional = [
'foo' => fopen('php://memory', 'r'),
];

$expected = [
'foo' => null,
'type' => 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html',
'title' => 'Bad Request',
'status' => 400,
'detail' => 'Random error',
];

$apiProblem = new ApiProblem(400, 'Random error', null, null, $additional);
$response = new ApiProblemResponse($apiProblem);
$this->assertEquals($expected, json_decode($response->getBody()->getContents(), true));
}

public function testApiProblemResponseSetsContentTypeHeader(): void
{
$response = new ApiProblemResponse(new ApiProblem(400, 'Random error'));
$headers = $response->getHeaders();
$this->assertArrayHasKey('content-type', $headers);
$header = $headers['content-type'][0];
$this->assertEquals(ApiProblem::CONTENT_TYPE, $header);
}
}
Loading