Skip to content
Draft
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
15 changes: 6 additions & 9 deletions examples/bootstrap_examples.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class ExampleDataset {
];

public static function getRecord($type, $id) {
if (!isset(self::$records[$type][$id])) {
if (isset(self::$records[$type][$id]) === false) {
throw new \Exception('sorry, we have a limited dataset');
}

Expand Down Expand Up @@ -151,14 +151,11 @@ public function getOfficialLink(): string {
* optionally helpers for the specific profile
*/

/**
* @param ResourceInterface&HasAttributesInterface $resource
*/
public function setTimestamps(ResourceInterface $resource, ?\DateTimeInterface $created=null, ?\DateTimeInterface $updated=null) {
if ($resource instanceof HasAttributesInterface === false) {
throw new \Exception('cannot add attributes to identifier objects');
}

public function setTimestamps(
ResourceInterface & HasAttributesInterface $resource,
?\DateTimeInterface $created=null,
?\DateTimeInterface $updated=null,
) {
$timestamps = [];
if ($created !== null) {
$timestamps['created'] = $created->format(\DateTime::ISO8601);
Expand Down
2 changes: 1 addition & 1 deletion examples/collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
foreach ($users as $user) {
$resource = ResourceObject::fromObject($user, type: 'user', id: $user->id);

if ($user->id == 42) {
if ($user->id === 42) {
$ship = new ResourceObject('ship', 5);
$ship->add('name', 'Heart of Gold');
$resource->addRelationship('ship', $ship);
Expand Down
4 changes: 3 additions & 1 deletion phpcs.bonus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,7 @@
<rule ref="Generic.Metrics.CyclomaticComplexity"/>
<rule ref="Generic.Metrics.NestingLevel"/>

<rule ref="Squiz.Operators.ComparisonOperatorUsage"/>
<rule ref="Squiz.Operators.ComparisonOperatorUsage">
<exclude name="Squiz.Operators.ComparisonOperatorUsage.ImplicitTrue"/>
</rule>
</ruleset>
4 changes: 1 addition & 3 deletions phpstan.bonus.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ includes:

parameters:
level: 10

treatPhpDocTypesAsCertain: true


# @see https://github.com/phpstan/phpstan-strict-rules
strictRules:
allRules: true
4 changes: 2 additions & 2 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ parameters:
- src/
- tests/
- examples/

typeAliases:
PHPStanTypeAlias_InternalOptions: 'array<string, bool|int|null|\alsvanzelf\jsonapi\enums\ContentTypeEnum>'

treatPhpDocTypesAsCertain: false
treatPhpDocTypesAsCertain: true

strictRules:
allRules: false
Expand Down
4 changes: 0 additions & 4 deletions src/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ public function setDescribedByLink(string $href, array $meta=[]): void {
}

/**
* @throws InputException if the $level is unknown
* @throws InputException if the $level is DocumentLevelEnum::Resource
*/
public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=DocumentLevelEnum::Root): void {
Expand All @@ -163,9 +162,6 @@ public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=Docu

case DocumentLevelEnum::Resource:
throw new InputException('level "resource" can only be set on a ResourceDocument');

default:
throw new InputException('unknown level "'.$level->value.'"');
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/helpers/RequestParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function hasIncludePaths(): bool {
* the raw format allows for custom processing
*
* @param PHPStanTypeAlias_InternalOptions $options {@see RequestParser::$defaults}
* @return string[]|array
* @return array<string>|array<array-key, mixed>
*/
public function getIncludePaths(array $options=[]): array {
if ($this->queryParameters['include'] === '') {
Expand Down
14 changes: 8 additions & 6 deletions src/objects/LinkObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,7 @@ public function toArray(): array {
$array['type'] = $this->type;
}
if ($this->hreflang !== []) {
if (count($this->hreflang) === 1) {
$array['hreflang'] = $this->hreflang[0];
}
else {
$array['hreflang'] = $this->hreflang;
}
$array['hreflang'] = $this->getHrefLanguages();
}
if (isset($this->describedby) && $this->describedby->isEmpty() === false) {
$array['describedby'] = $this->describedby->toArray();
Expand All @@ -168,4 +163,11 @@ public function toArray(): array {

return $array;
}

/**
* @return string|string[]
*/
private function getHrefLanguages(): string|array {
return (count($this->hreflang) === 1) ? $this->hreflang[0] : $this->hreflang;
}
}
6 changes: 1 addition & 5 deletions src/objects/RelationshipObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ public function __construct(
* @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation
* @param array<string, ?string> $links
* @param array<string, mixed> $meta
*
* @throws InputException if $relation is not one of the supported formats
*/
public static function fromAnything(
array|CollectionDocument|ResourceInterface|null $relation,
Expand All @@ -66,9 +64,6 @@ public static function fromAnything(
elseif ($relation === null) {
$relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne);
}
else {
throw new InputException('unknown format of relation "'.get_debug_type($relation).'"');
}

return $relationshipObject;
}
Expand Down Expand Up @@ -316,6 +311,7 @@ public function getNestedContainedResourceObjects(): array {
$resourceObjects = [];

foreach ($resources as $resource) {
// @phpstan-ignore instanceof.alwaysTrue, identical.alwaysFalse (we _can_ have both ResourceObject and ResourceIdentifierObject here)
if ($resource->getResource() instanceof ResourceObject === false) {
continue;
}
Expand Down
2 changes: 1 addition & 1 deletion src/objects/ResourceIdentifierObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function setMetaObject(MetaObject $metaObject): void {
*/
public static function fromResourceObject(ResourceObject $resourceObject): static {
if ($resourceObject->hasIdentification() === false) {
throw new InputException('resource has no identification yet<');
throw new InputException('resource has no identification yet');
}

$resourceIdentifierObject = new static($resourceObject->type, $resourceObject->primaryId());
Expand Down
61 changes: 61 additions & 0 deletions tests/ResourceDocumentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use alsvanzelf\jsonapi\ResourceDocument;
use alsvanzelf\jsonapi\enums\DocumentLevelEnum;
use alsvanzelf\jsonapi\enums\RelationshipTypeEnum;
use alsvanzelf\jsonapi\exceptions\Exception;
use alsvanzelf\jsonapi\exceptions\InputException;
use alsvanzelf\jsonapi\interfaces\ExtensionInterface;
Expand Down Expand Up @@ -100,6 +101,66 @@ public function testAddRelationship_DoNotIncludeContainedResources(): void {
parent::assertArrayNotHasKey('included', $array);
}

public function testAddRelationship_IdentifierOnlyObject(): void {
$document = new ResourceDocument();
$document->setPrimaryResource(new ResourceIdentifierObject('user', 42));

$this->expectException(Exception::class);
$this->expectExceptionMessage('the resource is an identifier-only object');

$document->addRelationship('foo', null);
}

public function testAddLink_IdentifierOnlyObject(): void {
$document = new ResourceDocument();
$document->setPrimaryResource(new ResourceIdentifierObject('user', 42));

$this->expectException(Exception::class);
$this->expectExceptionMessage('the resource is an identifier-only object');

$document->addLink('foo', null);
}

public function testSetSelfLink_IdentifierOnlyObject(): void {
$document = new ResourceDocument();
$document->setPrimaryResource(new ResourceIdentifierObject('user', 42));

$this->expectException(Exception::class);
$this->expectExceptionMessage('the resource is an identifier-only object');

$document->setSelfLink('https://jsonapi.org');
}

public function testSetAttributesObject_IdentifierOnlyObject(): void {
$document = new ResourceDocument();
$document->setPrimaryResource(new ResourceIdentifierObject('user', 42));

$this->expectException(Exception::class);
$this->expectExceptionMessage('the resource is an identifier-only object');

$document->setAttributesObject(new AttributesObject());
}

public function testAddRelationshipObject_IdentifierOnlyObject(): void {
$document = new ResourceDocument();
$document->setPrimaryResource(new ResourceIdentifierObject('user', 42));

$this->expectException(Exception::class);
$this->expectExceptionMessage('the resource is an identifier-only object');

$document->addRelationshipObject('foo', new RelationshipObject(RelationshipTypeEnum::ToOne));
}

public function testSetRelationshipsObject_IdentifierOnlyObject(): void {
$document = new ResourceDocument();
$document->setPrimaryResource(new ResourceIdentifierObject('user', 42));

$this->expectException(Exception::class);
$this->expectExceptionMessage('the resource is an identifier-only object');

$document->setRelationshipsObject(new RelationshipsObject());
}

public function testAddMeta_HappyPath(): void {
$document = new ResourceDocument();
$document->addMeta('foo', 'root', DocumentLevelEnum::Root);
Expand Down
13 changes: 5 additions & 8 deletions tests/example_output/ExampleTimestampsProfile.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@ public function getOfficialLink(): string {
return 'https://jsonapi.org/recommendations/#authoring-profiles';
}

/**
* @param ResourceInterface&HasAttributesInterface $resource
*/
public function setTimestamps(ResourceInterface $resource, ?\DateTimeInterface $created=null, ?\DateTimeInterface $updated=null) {
if ($resource instanceof HasAttributesInterface === false) {
throw new InputException('cannot add attributes to identifier objects');
}

public function setTimestamps(
ResourceInterface & HasAttributesInterface $resource,
?\DateTimeInterface $created=null,
?\DateTimeInterface $updated=null,
) {
$timestamps = [];
if ($created !== null) {
$timestamps['created'] = $created->format(\DateTime::ISO8601);
Expand Down
2 changes: 1 addition & 1 deletion tests/example_output/collection/collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static function createJsonapiDocument() {
foreach ($users as $user) {
$resource = ResourceObject::fromObject($user, 'user', $user->id);

if ($user->id == 42) {
if ($user->id === 42) {
$ship = new ResourceObject('ship', 5);
$ship->add('name', 'Heart of Gold');
$resource->addRelationship('ship', $ship);
Expand Down
7 changes: 2 additions & 5 deletions tests/helpers/RequestParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,9 @@ public function testFromSuperglobals_WithPhpInputStream(): void {
$_SERVER['REQUEST_URI'] = '/';
$_SERVER['CONTENT_TYPE'] = ContentTypeEnum::Official->value;

// empty $_POST so we get a bit more test coverage for input stream processing
$_GET = [];
$_POST = [
'meta' => [
'foo' => 'bar',
],
];
$_POST = [];

$requestParser = RequestParser::fromSuperglobals();

Expand Down
48 changes: 48 additions & 0 deletions tests/objects/ResourceIdentifierObjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

use alsvanzelf\jsonapi\exceptions\DuplicateException;
use alsvanzelf\jsonapi\exceptions\Exception;
use alsvanzelf\jsonapi\exceptions\InputException;
use alsvanzelf\jsonapi\interfaces\ExtensionInterface;
use alsvanzelf\jsonapi\objects\ResourceIdentifierObject;
use alsvanzelf\jsonapi\objects\ResourceObject;
use PHPUnit\Framework\TestCase;

class ResourceIdentifierObjectTest extends TestCase {
Expand Down Expand Up @@ -55,6 +57,35 @@ public function testSetLocalId_WithIdAlreadySet(): void {
$resourceIdentifierObject->setLocalId('uuid-1');
}

public function testFromResourceObject_HappyPath(): void {
$resource = new ResourceObject('test', 1);
$resource->addAttribute('foo', 'bar');

$array = $resource->toArray();

parent::assertSame('test', $array['type']);
parent::assertSame('1', $array['id']);
parent::assertArrayHasKey('attributes', $array);

$resourceIdentifierObject = ResourceIdentifierObject::fromResourceObject($resource);

$array = $resourceIdentifierObject->toArray();

parent::assertSame('test', $array['type']);
parent::assertSame('1', $array['id']);
parent::assertArrayNotHasKey('attributes', $array);
}

public function testFromResourceObject_NoFullIdentification(): void {
$resource = new ResourceObject();
$array = $resource->toArray();

$this->expectException(InputException::class);
$this->expectExceptionMessage('resource has no identification yet');

ResourceIdentifierObject::fromResourceObject($resource);
}

public function testEquals_HappyPath(): void {
$one = new ResourceIdentifierObject('test', 1);
$two = new ResourceIdentifierObject('test', 2);
Expand Down Expand Up @@ -168,6 +199,13 @@ public function testGetIdentificationKey_NoFullIdentification(): void {
$resourceIdentifierObject->getIdentificationKey();
}

public function testIsEmpty_IdWithoutType(): void {
$resourceIdentifierObject = new ResourceIdentifierObject();
$resourceIdentifierObject->setId(42);

parent::assertFalse($resourceIdentifierObject->isEmpty());
}

public function testIsEmpty_WithAtMembers(): void {
$resourceIdentifierObject = new ResourceIdentifierObject();

Expand All @@ -190,4 +228,14 @@ public function testIsEmpty_WithExtensionMembers(): void {

parent::assertFalse($resourceIdentifierObject->isEmpty());
}

public function testPrimaryId_NoFullIdentification(): void {
$resourceIdentifierObject = new ResourceIdentifierObject();
$primaryIdMethod = new \ReflectionMethod($resourceIdentifierObject, 'primaryId');

$this->expectException(Exception::class);
$this->expectExceptionMessage('resource has no identification yet');

$primaryIdMethod->invoke($resourceIdentifierObject);
}
}
Loading