From 773c026acbb0470a0068c6b87cb265fad9186be0 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Mon, 1 Dec 2025 20:49:38 +0100 Subject: [PATCH 01/12] add strict type enforcement everywhere --- examples/atomic_operations_extension.php | 2 ++ examples/bootstrap_examples.php | 2 ++ examples/collection.php | 2 ++ examples/collection_canonical.php | 2 ++ examples/cursor_pagination_profile.php | 2 ++ examples/errors_all_options.php | 2 ++ examples/errors_exception_native.php | 2 ++ examples/extension.php | 2 ++ examples/meta_only.php | 2 ++ examples/null_values.php | 2 ++ examples/output.php | 2 ++ examples/profile.php | 2 ++ examples/relationship_to_many_document.php | 2 ++ examples/relationship_to_one_document.php | 2 ++ examples/relationships.php | 2 ++ examples/request_superglobals.php | 2 ++ examples/resource_human_api.php | 2 ++ examples/resource_links.php | 2 ++ examples/resource_nested_relations.php | 2 ++ examples/resource_spec_api.php | 2 ++ examples/status_only.php | 2 ++ rector.php | 6 +++++- src/CollectionDocument.php | 2 ++ src/DataDocument.php | 2 ++ src/Document.php | 2 ++ src/ErrorsDocument.php | 2 ++ src/MetaDocument.php | 2 ++ src/ResourceDocument.php | 2 ++ src/exceptions/DuplicateException.php | 2 ++ src/exceptions/Exception.php | 2 ++ src/exceptions/InputException.php | 2 ++ src/extensions/AtomicOperationsDocument.php | 2 ++ src/extensions/AtomicOperationsExtension.php | 2 ++ src/helpers/AtMemberManager.php | 2 ++ src/helpers/Converter.php | 2 ++ src/helpers/ExtensionMemberManager.php | 2 ++ src/helpers/HttpStatusCodeManager.php | 2 ++ src/helpers/LinksManager.php | 2 ++ src/helpers/RequestParser.php | 2 ++ src/helpers/Validator.php | 2 ++ src/interfaces/DocumentInterface.php | 2 ++ src/interfaces/ExtensionInterface.php | 2 ++ src/interfaces/ObjectInterface.php | 2 ++ src/interfaces/PaginableInterface.php | 2 ++ src/interfaces/ProfileInterface.php | 2 ++ src/interfaces/RecursiveResourceContainerInterface.php | 2 ++ src/interfaces/ResourceContainerInterface.php | 2 ++ src/interfaces/ResourceInterface.php | 2 ++ src/objects/AttributesObject.php | 2 ++ src/objects/ErrorObject.php | 2 ++ src/objects/JsonapiObject.php | 2 ++ src/objects/LinkObject.php | 2 ++ src/objects/LinksObject.php | 2 ++ src/objects/MetaObject.php | 2 ++ src/objects/RelationshipObject.php | 2 ++ src/objects/RelationshipsObject.php | 2 ++ src/objects/ResourceIdentifierObject.php | 2 ++ src/objects/ResourceObject.php | 2 ++ src/profiles/CursorPaginationProfile.php | 2 ++ tests/CollectionDocumentTest.php | 2 ++ tests/ConverterTest.php | 2 ++ tests/DocumentTest.php | 2 ++ tests/ErrorsDocumentTest.php | 2 ++ tests/ExampleOutputTest.php | 2 ++ tests/MetaDocumentTest.php | 2 ++ tests/ResourceDocumentTest.php | 2 ++ tests/SeparateProcessTest.php | 2 ++ tests/TestableNonAbstractDocument.php | 2 ++ tests/ValidatorTest.php | 2 ++ tests/bootstrap_tests.php | 2 ++ tests/example_output/ExampleEverywhereExtension.php | 2 ++ tests/example_output/ExampleTimestampsProfile.php | 2 ++ tests/example_output/ExampleUser.php | 2 ++ tests/example_output/ExampleVersionExtension.php | 2 ++ .../at_members_everywhere/at_members_everywhere.php | 2 ++ .../at_members_in_errors/at_members_in_errors.php | 2 ++ tests/example_output/collection/collection.php | 2 ++ .../collection_canonical/collection_canonical.php | 2 ++ .../cursor_pagination_profile/cursor_pagination_profile.php | 2 ++ .../errors_all_options/errors_all_options.json | 4 ++-- .../errors_all_options/errors_all_options.php | 2 ++ .../errors_exception_native/errors_exception_native.json | 2 +- .../errors_exception_native/errors_exception_native.php | 2 ++ tests/example_output/extension/extension.php | 2 ++ .../extension_members_everywhere.php | 2 ++ tests/example_output/meta_only/meta_only.php | 2 ++ tests/example_output/null_values/null_values.php | 2 ++ tests/example_output/profile/profile.php | 2 ++ .../relationship_to_many_document.php | 2 ++ .../relationship_to_one_document.php | 2 ++ tests/example_output/relationships/relationships.php | 2 ++ .../resource_document_identifier_only.php | 2 ++ .../resource_human_api/resource_human_api.php | 2 ++ tests/example_output/resource_links/resource_links.php | 2 ++ .../resource_nested_relations/resource_nested_relations.php | 2 ++ .../example_output/resource_spec_api/resource_spec_api.php | 2 ++ tests/example_output/status_only/status_only.php | 2 ++ tests/extensions/AtomicOperationsDocumentTest.php | 2 ++ tests/extensions/TestExtension.php | 2 ++ tests/helpers/AtMemberManagerTest.php | 2 ++ tests/helpers/ExtensionMemberManagerTest.php | 2 ++ tests/helpers/HttpStatusCodeManagerTest.php | 2 ++ tests/helpers/LinksManagerTest.php | 2 ++ tests/helpers/RequestParserTest.php | 2 ++ tests/helpers/TestableNonInterfaceRequestInterface.php | 2 ++ .../helpers/TestableNonInterfaceServerRequestInterface.php | 2 ++ tests/helpers/TestableNonInterfaceStreamInterface.php | 2 ++ tests/helpers/TestableNonInterfaceUriInterface.php | 2 ++ tests/helpers/TestableNonTraitAtMemberManager.php | 2 ++ tests/helpers/TestableNonTraitExtensionMemberManager.php | 2 ++ tests/helpers/TestableNonTraitHttpStatusCodeManager.php | 2 ++ tests/helpers/TestableNonTraitLinksManager.php | 2 ++ tests/objects/AttributesObjectTest.php | 2 ++ tests/objects/ErrorObjectTest.php | 2 ++ tests/objects/JsonapiObjectTest.php | 2 ++ tests/objects/LinkObjectTest.php | 2 ++ tests/objects/LinksObjectTest.php | 2 ++ tests/objects/MetaObjectTest.php | 2 ++ tests/objects/RelationshipObjectTest.php | 2 ++ tests/objects/RelationshipsObjectTest.php | 2 ++ tests/objects/ResourceIdentifierObjectTest.php | 2 ++ tests/objects/ResourceObjectTest.php | 2 ++ tests/profiles/CursorPaginationProfileTest.php | 2 ++ tests/profiles/TestProfile.php | 2 ++ 124 files changed, 250 insertions(+), 4 deletions(-) diff --git a/examples/atomic_operations_extension.php b/examples/atomic_operations_extension.php index 4e97c6da..caa6aaae 100644 --- a/examples/atomic_operations_extension.php +++ b/examples/atomic_operations_extension.php @@ -1,5 +1,7 @@ withRules([ + DeclareStrictTypesRector::class, + ]) + // tab-based indenting ->withIndent(indentChar: "\t", indentSize: 1) diff --git a/src/CollectionDocument.php b/src/CollectionDocument.php index ba7c458b..55cae30a 100644 --- a/src/CollectionDocument.php +++ b/src/CollectionDocument.php @@ -1,5 +1,7 @@ Date: Sun, 21 Dec 2025 16:34:48 +0100 Subject: [PATCH 02/12] convert constants to enums --- UPGRADE_2_TO_3.md | 31 ++++++++ examples/extension.php | 4 +- examples/null_values.php | 5 +- examples/output.php | 4 +- examples/profile.php | 4 +- examples/relationship_to_one_document.php | 4 +- examples/relationships.php | 5 +- examples/request_superglobals.php | 4 +- examples/resource_links.php | 12 +-- examples/resource_spec_api.php | 3 +- src/Document.php | 62 +++++---------- src/ErrorsDocument.php | 1 - src/MetaDocument.php | 4 +- src/ResourceDocument.php | 15 ++-- src/enums/ContentTypeEnum.php | 11 +++ src/enums/DocumentLevelEnum.php | 11 +++ src/enums/JsonapiVersionEnum.php | 11 +++ src/enums/ObjectContainerEnum.php | 16 ++++ src/enums/RelationshipTypeEnum.php | 10 +++ src/enums/SortOrderEnum.php | 10 +++ src/helpers/Converter.php | 6 +- src/helpers/RequestParser.php | 16 ++-- src/helpers/Validator.php | 19 ++--- src/objects/ErrorObject.php | 1 - src/objects/JsonapiObject.php | 15 ++-- src/objects/RelationshipObject.php | 76 ++++++++----------- src/objects/ResourceIdentifierObject.php | 7 +- src/objects/ResourceObject.php | 14 ++-- src/profiles/CursorPaginationProfile.php | 4 +- tests/ConverterTest.php | 9 ++- tests/DocumentTest.php | 27 ++----- tests/ResourceDocumentTest.php | 10 +-- tests/SeparateProcessTest.php | 39 +++++----- tests/ValidatorTest.php | 37 ++++----- .../at_members_everywhere.php | 5 +- .../extension_members_everywhere.php | 5 +- .../null_values/null_values.php | 5 +- .../relationship_to_one_document.php | 4 +- .../relationships/relationships.php | 5 +- .../resource_links/resource_links.json | 6 +- .../resource_links/resource_links.php | 12 +-- .../resource_spec_api/resource_spec_api.php | 3 +- tests/helpers/RequestParserTest.php | 19 ++--- tests/objects/RelationshipObjectTest.php | 45 +++++------ tests/objects/RelationshipsObjectTest.php | 3 +- tests/objects/ResourceObjectTest.php | 5 +- 46 files changed, 333 insertions(+), 291 deletions(-) create mode 100644 UPGRADE_2_TO_3.md create mode 100644 src/enums/ContentTypeEnum.php create mode 100644 src/enums/DocumentLevelEnum.php create mode 100644 src/enums/JsonapiVersionEnum.php create mode 100644 src/enums/ObjectContainerEnum.php create mode 100644 src/enums/RelationshipTypeEnum.php create mode 100644 src/enums/SortOrderEnum.php diff --git a/UPGRADE_2_TO_3.md b/UPGRADE_2_TO_3.md new file mode 100644 index 00000000..0f604425 --- /dev/null +++ b/UPGRADE_2_TO_3.md @@ -0,0 +1,31 @@ +# Upgrade from library v2 to v3 + +## Enums + +Content types: +- `Document::CONTENT_TYPE_OFFICIAL` to `ContentTypeEnum::Official` +- `Document::CONTENT_TYPE_DEBUG` to `ContentTypeEnum::Debug` +- `Document::CONTENT_TYPE_JSONP` to `ContentTypeEnum::Jsonp` + +Jsonapi versions: +- `Document::JSONAPI_VERSION_1_0` to `JsonapiVersionEnum::V_1_0` +- `Document::JSONAPI_VERSION_1_1` to `JsonapiVersionEnum::V_1_1` +- `Document::JSONAPI_VERSION_LATEST` to `JsonapiVersionEnum::Latest` + +Document levels: +- `Document::LEVEL_ROOT` to `DocumentLevelEnum::Root` +- `Document::LEVEL_JSONAPI` to `DocumentLevelEnum::Jsonapi` +- `Document::LEVEL_Resource` to `DocumentLevelEnum::Resource` + +Sorting orders: +- `RequestParser->getSortFields()` returns a `SortOrderEnum` instead of `string` for the `order` field + +Relationship types: +- `RelationshipObject::TO_ONE` to `RelationshipTypeEnum::ToOne` +- `RelationshipObject::TO_MANY` to `RelationshipTypeEnum::ToMany` + +## Internal + +Enums: +- `RequestParser::SORT_*` to `SortOrderEnum::*` +- `Validator::OBJECT_CONTAINER_*` to `ObjectContainerEnum::*` diff --git a/examples/extension.php b/examples/extension.php index 10f117e8..42f87e6e 100644 --- a/examples/extension.php +++ b/examples/extension.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; use alsvanzelf\jsonapi\helpers\Converter; require 'bootstrap_examples.php'; @@ -30,7 +30,7 @@ * get the json */ -$contentType = Converter::prepareContentType(Document::CONTENT_TYPE_OFFICIAL, [$extension], []); +$contentType = Converter::prepareContentType(ContentTypeEnum::Official, [$extension], []); echo 'Content-Type: '.$contentType.''.PHP_EOL; $options = [ diff --git a/examples/null_values.php b/examples/null_values.php index af62fe03..4a7cd92c 100644 --- a/examples/null_values.php +++ b/examples/null_values.php @@ -3,6 +3,7 @@ declare(strict_types=1); use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\LinkObject; use alsvanzelf\jsonapi\objects\RelationshipObject; @@ -24,8 +25,8 @@ // show a relationship is not set $document->addRelationship('bar', null); -$document->addRelationshipObject('baz', new RelationshipObject(RelationshipObject::TO_ONE)); -$document->addRelationshipObject('baf', new RelationshipObject(RelationshipObject::TO_MANY)); +$document->addRelationshipObject('baz', new RelationshipObject(RelationshipTypeEnum::ToOne)); +$document->addRelationshipObject('baf', new RelationshipObject(RelationshipTypeEnum::ToMany)); /** * sending the response diff --git a/examples/output.php b/examples/output.php index a477e8a8..f6726e8d 100644 --- a/examples/output.php +++ b/examples/output.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\MetaDocument; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; require 'bootstrap_examples.php'; @@ -75,4 +75,4 @@ echo '
';
 $document->sendResponse($options);
 echo '
'; -echo '

Also sends http status code ('.$document->getHttpStatusCode().') and headers: [Content-Type: '.Document::CONTENT_TYPE_OFFICIAL.']

'; +echo '

Also sends http status code ('.$document->getHttpStatusCode().') and headers: [Content-Type: '.ContentTypeEnum::Official->value.']

'; diff --git a/examples/profile.php b/examples/profile.php index ce609e12..82458551 100644 --- a/examples/profile.php +++ b/examples/profile.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; use alsvanzelf\jsonapi\helpers\Converter; require 'bootstrap_examples.php'; @@ -32,7 +32,7 @@ * get the json */ -$contentType = Converter::prepareContentType(Document::CONTENT_TYPE_OFFICIAL, [], [$profile]); +$contentType = Converter::prepareContentType(ContentTypeEnum::Official, [], [$profile]); echo 'Content-Type: '.$contentType.''.PHP_EOL; $options = [ diff --git a/examples/relationship_to_one_document.php b/examples/relationship_to_one_document.php index 8223a436..7dfc6ebe 100644 --- a/examples/relationship_to_one_document.php +++ b/examples/relationship_to_one_document.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; require 'bootstrap_examples.php'; @@ -13,7 +13,7 @@ $relationshipDocument = new ResourceDocument('author', 12); -$relationshipDocument->setSelfLink('/articles/1/relationship/author', $meta=[], $level=Document::LEVEL_ROOT); +$relationshipDocument->setSelfLink('/articles/1/relationship/author', $meta=[], $level=DocumentLevelEnum::Root); $relationshipDocument->addLink('related', '/articles/1/author'); /** diff --git a/examples/relationships.php b/examples/relationships.php index c0979041..6755f34d 100644 --- a/examples/relationships.php +++ b/examples/relationships.php @@ -4,6 +4,7 @@ use alsvanzelf\jsonapi\CollectionDocument; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\RelationshipObject; use alsvanzelf\jsonapi\objects\ResourceObject; @@ -47,7 +48,7 @@ * to-many relationship, one-by-one */ -$relationshipObject = new RelationshipObject($type=RelationshipObject::TO_MANY); +$relationshipObject = new RelationshipObject($type=RelationshipTypeEnum::ToMany); $relationshipObject->addResource($friend1Resource); $relationshipObject->addResource($friend2Resource); @@ -67,7 +68,7 @@ * to-many relationship, different types */ -$relationshipObject = new RelationshipObject($type=RelationshipObject::TO_MANY); +$relationshipObject = new RelationshipObject($type=RelationshipTypeEnum::ToMany); $relationshipObject->addResource($ship1Resource); $relationshipObject->addResource($dockResource); diff --git a/examples/request_superglobals.php b/examples/request_superglobals.php index d0ccf58a..cb93d06f 100644 --- a/examples/request_superglobals.php +++ b/examples/request_superglobals.php @@ -2,7 +2,7 @@ declare(strict_types=1); -use alsvanzelf\jsonapi\Document; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; use alsvanzelf\jsonapi\helpers\RequestParser; require 'bootstrap_examples.php'; @@ -46,7 +46,7 @@ $_SERVER['REQUEST_SCHEME'] = 'https'; $_SERVER['HTTP_HOST'] = 'example.org'; $_SERVER['REQUEST_URI'] = '/user/42?'.http_build_query($_GET); -$_SERVER['CONTENT_TYPE'] = Document::CONTENT_TYPE_OFFICIAL; +$_SERVER['CONTENT_TYPE'] = ContentTypeEnum::Official->value; /** * parsing the request diff --git a/examples/resource_links.php b/examples/resource_links.php index b82dd34a..a79e1f82 100644 --- a/examples/resource_links.php +++ b/examples/resource_links.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; require 'bootstrap_examples.php'; @@ -15,13 +15,13 @@ */ $document = ResourceDocument::fromObject($userEntity, $type='user', $userEntity->id); -$selfResourceMeta = ['level' => Document::LEVEL_RESOURCE]; -$partnerMeta = ['level' => Document::LEVEL_RESOURCE]; -$redirectMeta = ['level' => Document::LEVEL_ROOT]; +$selfResourceMeta = ['level' => DocumentLevelEnum::Resource->name]; +$partnerMeta = ['level' => DocumentLevelEnum::Resource->name]; +$redirectMeta = ['level' => DocumentLevelEnum::Root->name]; $document->setSelfLink('/user/42', $selfResourceMeta); -$document->addLink('partner', '/user/1', $partnerMeta, $level=Document::LEVEL_RESOURCE); -$document->addLink('redirect', '/login', $redirectMeta, $level=Document::LEVEL_ROOT); +$document->addLink('partner', '/user/1', $partnerMeta, $level=DocumentLevelEnum::Resource); +$document->addLink('redirect', '/login', $redirectMeta, $level=DocumentLevelEnum::Root); /** * sending the response diff --git a/examples/resource_spec_api.php b/examples/resource_spec_api.php index 2077e116..fa160d44 100644 --- a/examples/resource_spec_api.php +++ b/examples/resource_spec_api.php @@ -3,6 +3,7 @@ declare(strict_types=1); use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\AttributesObject; use alsvanzelf\jsonapi\objects\LinksObject; use alsvanzelf\jsonapi\objects\MetaObject; @@ -43,7 +44,7 @@ $resource->setType('user'); $resource->setAttributesObject($attributes42); -$relationship = new RelationshipObject(RelationshipObject::TO_ONE); +$relationship = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationship->setResource($resource); $relationships = new RelationshipsObject(); $relationships->addRelationshipObject('friend', $relationship); diff --git a/src/Document.php b/src/Document.php index 272f1c75..8c1bea3f 100644 --- a/src/Document.php +++ b/src/Document.php @@ -4,6 +4,8 @@ namespace alsvanzelf\jsonapi; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\Exception; use alsvanzelf\jsonapi\exceptions\InputException; @@ -32,18 +34,6 @@ abstract class Document implements DocumentInterface, \JsonSerializable, HasLink LinksManager::addLink as linkManagerAddLink; } - const JSONAPI_VERSION_1_0 = '1.0'; - const JSONAPI_VERSION_1_1 = '1.1'; - const JSONAPI_VERSION_LATEST = Document::JSONAPI_VERSION_1_1; - - const CONTENT_TYPE_OFFICIAL = 'application/vnd.api+json'; - const CONTENT_TYPE_DEBUG = 'application/json'; - const CONTENT_TYPE_JSONP = 'application/javascript'; - - const LEVEL_ROOT = 'root'; - const LEVEL_JSONAPI = 'jsonapi'; - const LEVEL_RESOURCE = 'resource'; - /** @var MetaObject */ protected $meta; /** @var ?JsonapiObject */ @@ -68,7 +58,7 @@ abstract class Document implements DocumentInterface, \JsonSerializable, HasLink * send out the official jsonapi content-type header * overwrite for jsonp or if clients don't support it */ - 'contentType' => Document::CONTENT_TYPE_OFFICIAL, + 'contentType' => ContentTypeEnum::Official, /** * overwrite the array to encode to json @@ -99,23 +89,15 @@ public function __construct() { * @param string $key * @param string $href * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added - * @param string $level one of the Document::LEVEL_* constants, optional, defaults to Document::LEVEL_ROOT * - * @throws InputException if the $level is Document::LEVEL_JSONAPI, Document::LEVEL_RESOURCE, or unknown + * @throws InputException if the $level is not DocumentLevelEnum::Root */ - public function addLink($key, $href, array $meta=[], $level=Document::LEVEL_ROOT) { - if ($level === Document::LEVEL_ROOT) { - $this->linkManagerAddLink($key, $href, $meta); - } - elseif ($level === Document::LEVEL_JSONAPI) { - throw new InputException('level "jsonapi" can not be used for links'); - } - elseif ($level === Document::LEVEL_RESOURCE) { - throw new InputException('level "resource" can only be set on a ResourceDocument'); - } - else { - throw new InputException('unknown level "'.$level.'"'); - } + public function addLink($key, $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root) { + match ($level) { + DocumentLevelEnum::Root => $this->linkManagerAddLink($key, $href, $meta), + DocumentLevelEnum::Jsonapi => throw new InputException('level "jsonapi" can not be used for links'), + DocumentLevelEnum::Resource => throw new InputException('level "resource" can only be set on a ResourceDocument'), + }; } /** @@ -125,11 +107,10 @@ public function addLink($key, $href, array $meta=[], $level=Document::LEVEL_ROOT * * @param string $href * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added - * @param string $level one of the Document::LEVEL_* constants, optional, defaults to Document::LEVEL_ROOT */ - public function setSelfLink($href, array $meta=[], $level=Document::LEVEL_ROOT) { - if ($level === Document::LEVEL_ROOT && ($this->extensions !== [] || $this->profiles !== [])) { - $contentType = Converter::prepareContentType(Document::CONTENT_TYPE_OFFICIAL, $this->extensions, $this->profiles); + public function setSelfLink($href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root) { + if ($level === DocumentLevelEnum::Root && ($this->extensions !== [] || $this->profiles !== [])) { + $contentType = Converter::prepareContentType(ContentTypeEnum::Official, $this->extensions, $this->profiles); $linkObject = new LinkObject($href, $meta); $linkObject->setMediaType($contentType); @@ -146,43 +127,42 @@ public function setSelfLink($href, array $meta=[], $level=Document::LEVEL_ROOT) * * for example this could link to an OpenAPI or JSON Schema document * - * @note according to the spec, this can only be set to Document::LEVEL_ROOT + * @note according to the spec, this can only be set to DocumentLevelEnum::Root * * @param string $href * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added */ public function setDescribedByLink($href, array $meta=[]) { - $this->addLink('describedby', $href, $meta, $level=Document::LEVEL_ROOT); + $this->addLink('describedby', $href, $meta, $level=DocumentLevelEnum::Root); } /** * @param string $key * @param mixed $value - * @param string $level one of the Document::LEVEL_* constants, optional, defaults to Document::LEVEL_ROOT * * @throws InputException if the $level is unknown - * @throws InputException if the $level is Document::LEVEL_RESOURCE + * @throws InputException if the $level is DocumentLevelEnum::Resource */ - public function addMeta($key, $value, $level=Document::LEVEL_ROOT) { - if ($level === Document::LEVEL_ROOT) { + public function addMeta($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Root) { + if ($level === DocumentLevelEnum::Root) { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); } $this->meta->add($key, $value); } - elseif ($level === Document::LEVEL_JSONAPI) { + elseif ($level === DocumentLevelEnum::Jsonapi) { if ($this->jsonapi === null) { $this->setJsonapiObject(new JsonapiObject()); } $this->jsonapi->addMeta($key, $value); } - elseif ($level === Document::LEVEL_RESOURCE) { + elseif ($level === DocumentLevelEnum::Resource) { throw new InputException('level "resource" can only be set on a ResourceDocument'); } else { - throw new InputException('unknown level "'.$level.'"'); + throw new InputException('unknown level "'.$level->value.'"'); } } diff --git a/src/ErrorsDocument.php b/src/ErrorsDocument.php index d13f60f5..48ef29e3 100644 --- a/src/ErrorsDocument.php +++ b/src/ErrorsDocument.php @@ -5,7 +5,6 @@ namespace alsvanzelf\jsonapi; use alsvanzelf\jsonapi\Document; -use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\objects\ErrorObject; /** diff --git a/src/MetaDocument.php b/src/MetaDocument.php index d49ed229..54f99460 100644 --- a/src/MetaDocument.php +++ b/src/MetaDocument.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapi; use alsvanzelf\jsonapi\Document; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapi\helpers\Converter; use alsvanzelf\jsonapi\objects\MetaObject; @@ -43,9 +44,8 @@ public static function fromObject($meta) { * * @param string $key * @param mixed $value - * @param string $level one of the Document::LEVEL_* constants, optional, defaults to Document::LEVEL_ROOT */ - public function add($key, $value, $level=Document::LEVEL_ROOT) { + public function add($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Root) { parent::addMeta($key, $value, $level); } diff --git a/src/ResourceDocument.php b/src/ResourceDocument.php index 35a57b2a..085ef1a6 100644 --- a/src/ResourceDocument.php +++ b/src/ResourceDocument.php @@ -7,6 +7,7 @@ use alsvanzelf\jsonapi\CollectionDocument; use alsvanzelf\jsonapi\DataDocument; use alsvanzelf\jsonapi\Document; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapi\exceptions\Exception; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\helpers\Converter; @@ -124,14 +125,13 @@ public function addRelationship($key, $relation, array $links=[], array $meta=[] * @param string $key * @param string $href * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added - * @param string $level one of the Document::LEVEL_* constants, optional, defaults to Document::LEVEL_ROOT */ - public function addLink($key, $href, array $meta=[], $level=Document::LEVEL_ROOT) { + public function addLink($key, $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root) { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } - if ($level === Document::LEVEL_RESOURCE) { + if ($level === DocumentLevelEnum::Resource) { $this->resource->addLink($key, $href, $meta); } else { @@ -145,12 +145,12 @@ public function addLink($key, $href, array $meta=[], $level=Document::LEVEL_ROOT * @param string $href * @param array $meta optional */ - public function setSelfLink($href, array $meta=[], $level=Document::LEVEL_RESOURCE) { + public function setSelfLink($href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Resource) { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } - if ($level === Document::LEVEL_RESOURCE) { + if ($level === DocumentLevelEnum::Resource) { $this->resource->setSelfLink($href, $meta); } else { @@ -161,10 +161,9 @@ public function setSelfLink($href, array $meta=[], $level=Document::LEVEL_RESOUR /** * @param string $key * @param mixed $value - * @param string $level one of the Document::LEVEL_* constants, optional, defaults to Document::LEVEL_ROOT */ - public function addMeta($key, $value, $level=Document::LEVEL_ROOT) { - if ($level === Document::LEVEL_RESOURCE) { + public function addMeta($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Root) { + if ($level === DocumentLevelEnum::Resource) { $this->resource->addMeta($key, $value); } else { diff --git a/src/enums/ContentTypeEnum.php b/src/enums/ContentTypeEnum.php new file mode 100644 index 00000000..a3d23d11 --- /dev/null +++ b/src/enums/ContentTypeEnum.php @@ -0,0 +1,11 @@ +value; +} diff --git a/src/enums/ObjectContainerEnum.php b/src/enums/ObjectContainerEnum.php new file mode 100644 index 00000000..d2258c02 --- /dev/null +++ b/src/enums/ObjectContainerEnum.php @@ -0,0 +1,16 @@ +value; + if ($extensions !== []) { $extensionLinks = []; foreach ($extensions as $extension) { diff --git a/src/helpers/RequestParser.php b/src/helpers/RequestParser.php index 5d8535e9..73f63bdf 100644 --- a/src/helpers/RequestParser.php +++ b/src/helpers/RequestParser.php @@ -4,14 +4,12 @@ namespace alsvanzelf\jsonapi\helpers; -use alsvanzelf\jsonapi\Document; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; +use alsvanzelf\jsonapi\enums\SortOrderEnum; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ServerRequestInterface; class RequestParser { - const SORT_ASCENDING = 'ascending'; - const SORT_DESCENDING = 'descending'; - /** @var array */ protected static $defaults = [ /** @@ -51,8 +49,8 @@ public static function fromSuperglobals() { $document = $_POST; if ($document === [] && isset($_SERVER['CONTENT_TYPE'])) { - $documentIsJsonapi = (str_contains((string) $_SERVER['CONTENT_TYPE'], Document::CONTENT_TYPE_OFFICIAL)); - $documentIsJson = (str_contains((string) $_SERVER['CONTENT_TYPE'], Document::CONTENT_TYPE_DEBUG)); + $documentIsJsonapi = (str_contains((string) $_SERVER['CONTENT_TYPE'], ContentTypeEnum::Official->value)); + $documentIsJson = (str_contains((string) $_SERVER['CONTENT_TYPE'], ContentTypeEnum::Debug->value)); if ($documentIsJsonapi || $documentIsJson) { $document = json_decode(file_get_contents('php://input'), true); @@ -188,7 +186,7 @@ public function hasSortFields() { * @param array $options optional {@see RequestParser::$defaults} * @return string[]|array */ public function getSortFields(array $options=[]) { @@ -205,11 +203,11 @@ public function getSortFields(array $options=[]) { $sort = []; foreach ($fields as $field) { - $order = RequestParser::SORT_ASCENDING; + $order = SortOrderEnum::Ascending; if (str_starts_with($field, '-')) { $field = substr($field, 1); - $order = RequestParser::SORT_DESCENDING; + $order = SortOrderEnum::Descending; } $sort[] = [ diff --git a/src/helpers/Validator.php b/src/helpers/Validator.php index 33af6cc6..a31d1d5c 100644 --- a/src/helpers/Validator.php +++ b/src/helpers/Validator.php @@ -4,6 +4,7 @@ namespace alsvanzelf\jsonapi\helpers; +use alsvanzelf\jsonapi\enums\ObjectContainerEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\interfaces\ResourceInterface; @@ -12,12 +13,6 @@ * @internal */ class Validator { - const OBJECT_CONTAINER_TYPE = 'type'; - const OBJECT_CONTAINER_ID = 'id'; - const OBJECT_CONTAINER_LID = 'lid'; - const OBJECT_CONTAINER_ATTRIBUTES = 'attributes'; - const OBJECT_CONTAINER_RELATIONSHIPS = 'relationships'; - /** @var array */ protected $usedFields = []; /** @var array */ @@ -38,12 +33,11 @@ class Validator { * @see https://jsonapi.org/format/1.1/#document-resource-object-fields * * @param string[] $fieldNames - * @param string $objectContainer one of the Validator::OBJECT_CONTAINER_* constants * @param array $options optional {@see Validator::$defaults} * * @throws DuplicateException */ - public function claimUsedFields(array $fieldNames, $objectContainer, array $options=[]) { + public function claimUsedFields(array $fieldNames, ObjectContainerEnum $objectContainer, array $options=[]) { $options = array_merge(self::$defaults, $options); foreach ($fieldNames as $fieldName) { @@ -58,18 +52,15 @@ public function claimUsedFields(array $fieldNames, $objectContainer, array $opti /** * @note this is not allowed by the specification */ - if ($this->usedFields[$fieldName] === Validator::OBJECT_CONTAINER_TYPE && $options['enforceTypeFieldNamespace'] === false) { + if ($this->usedFields[$fieldName] === ObjectContainerEnum::Type && $options['enforceTypeFieldNamespace'] === false) { continue; } - throw new DuplicateException('field name "'.$fieldName.'" already in use at "data.'.$this->usedFields[$fieldName].'"'); + throw new DuplicateException('field name "'.$fieldName.'" already in use at "data.'.$this->usedFields[$fieldName]->value.'"'); } } - /** - * @param string $objectContainerToClear one of the Validator::OBJECT_CONTAINER_* constants - */ - public function clearUsedFields($objectContainerToClear) { + public function clearUsedFields(ObjectContainerEnum $objectContainerToClear) { foreach ($this->usedFields as $fieldName => $containerFound) { if ($containerFound !== $objectContainerToClear) { continue; diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index 625beeac..d8e55511 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -5,7 +5,6 @@ namespace alsvanzelf\jsonapi\objects; use alsvanzelf\jsonapi\Document; -use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\helpers\Converter; use alsvanzelf\jsonapi\helpers\HttpStatusCodeManager; use alsvanzelf\jsonapi\helpers\LinksManager; diff --git a/src/objects/JsonapiObject.php b/src/objects/JsonapiObject.php index 06974cd2..a9ff8d9a 100644 --- a/src/objects/JsonapiObject.php +++ b/src/objects/JsonapiObject.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapi\objects; use alsvanzelf\jsonapi\Document; +use alsvanzelf\jsonapi\enums\JsonapiVersionEnum; use alsvanzelf\jsonapi\interfaces\ExtensionInterface; use alsvanzelf\jsonapi\interfaces\HasMetaInterface; use alsvanzelf\jsonapi\interfaces\ProfileInterface; @@ -12,7 +13,7 @@ use alsvanzelf\jsonapi\objects\MetaObject; class JsonapiObject extends AbstractObject implements HasMetaInterface { - /** @var string */ + /** @var JsonapiVersionEnum */ protected $version; /** @var ExtensionInterface[] */ protected $extensions = []; @@ -21,10 +22,7 @@ class JsonapiObject extends AbstractObject implements HasMetaInterface { /** @var MetaObject */ protected $meta; - /** - * @param ?string $version one of the Document::JSONAPI_VERSION_* constants, optional, defaults to Document::JSONAPI_VERSION_LATEST - */ - public function __construct($version=Document::JSONAPI_VERSION_LATEST) { + public function __construct(?JsonapiVersionEnum $version=JsonapiVersionEnum::Latest) { if ($version !== null) { $this->setVersion($version); } @@ -50,10 +48,7 @@ public function addMeta($key, $value) { * spec api */ - /** - * @param string $version - */ - public function setVersion($version) { + public function setVersion(JsonapiVersionEnum $version) { $this->version = $version; } @@ -115,7 +110,7 @@ public function toArray() { $array = array_merge($array, $this->getExtensionMembers()); } if ($this->version !== null) { - $array['version'] = $this->version; + $array['version'] = $this->version->value; } if ($this->extensions !== []) { $array['ext'] = []; diff --git a/src/objects/RelationshipObject.php b/src/objects/RelationshipObject.php index d29c02e6..af2202e4 100644 --- a/src/objects/RelationshipObject.php +++ b/src/objects/RelationshipObject.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapi\objects; use alsvanzelf\jsonapi\CollectionDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\helpers\LinksManager; use alsvanzelf\jsonapi\interfaces\HasLinksInterface; @@ -20,30 +21,16 @@ class RelationshipObject extends AbstractObject implements PaginableInterface, RecursiveResourceContainerInterface, HasLinksInterface, HasMetaInterface { use LinksManager; - const TO_ONE = 'one'; - const TO_MANY = 'many'; - /** @var MetaObject */ protected $meta; /** @var ResourceInterface */ protected $resource; - /** @var string one of the RelationshipObject::TO_* constants */ - protected $type; /** @var ResourceInterface[] */ protected $resources = []; - /** - * @param string $type one of the RelationshipObject::TO_* constants - * - * @throws InputException if $type is unknown - */ - public function __construct($type) { - if (in_array($type, [RelationshipObject::TO_ONE, RelationshipObject::TO_MANY], $strict=true) === false) { - throw new InputException('unknown relationship type "'.$type.'"'); - } - - $this->type = $type; - } + public function __construct( + protected RelationshipTypeEnum $type, + ) {} /** * human api @@ -71,7 +58,7 @@ public static function fromAnything($relation, array $links=[], array $meta=[]) $relationshipObject = self::fromCollectionDocument($relation, $links, $meta); } elseif ($relation === null) { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); } else { throw new InputException('unknown format of relation "'.gettype($relation).'"'); @@ -84,18 +71,15 @@ public static function fromAnything($relation, array $links=[], array $meta=[]) * @param ResourceInterface $resource * @param array $links optional * @param array $meta optional - * @param string $type optional, one of the RelationshipObject::TO_* constants, defaults to RelationshipObject::TO_ONE * @return RelationshipObject */ - public static function fromResource(ResourceInterface $resource, array $links=[], array $meta=[], $type=RelationshipObject::TO_ONE) { + public static function fromResource(ResourceInterface $resource, array $links=[], array $meta=[], RelationshipTypeEnum $type=RelationshipTypeEnum::ToOne) { $relationshipObject = new self($type); - if ($type === RelationshipObject::TO_ONE) { - $relationshipObject->setResource($resource); - } - elseif ($type === RelationshipObject::TO_MANY) { - $relationshipObject->addResource($resource); - } + match ($type) { + RelationshipTypeEnum::ToOne => $relationshipObject->setResource($resource), + RelationshipTypeEnum::ToMany => $relationshipObject->addResource($resource), + }; if ($links !== []) { $relationshipObject->setLinksObject(LinksObject::fromArray($links)); @@ -114,7 +98,7 @@ public static function fromResource(ResourceInterface $resource, array $links=[] * @return RelationshipObject */ public static function fromCollectionDocument(CollectionDocument $collectionDocument, array $links=[], array $meta=[]) { - $relationshipObject = new self(RelationshipObject::TO_MANY); + $relationshipObject = new self(RelationshipTypeEnum::ToMany); foreach ($collectionDocument->getContainedResources() as $resource) { $relationshipObject->addResource($resource); @@ -150,7 +134,7 @@ public function setRelatedLink($href, array $meta=[]) { * @throws InputException if used on a to-one relationship */ public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null) { - if ($this->type === RelationshipObject::TO_ONE) { + if ($this->type === RelationshipTypeEnum::ToOne) { throw new InputException('can not add pagination links to a to-one relationship'); } @@ -192,7 +176,7 @@ public function addMeta($key, $value) { * @throws InputException if used on a to-many relationship, use {@see ->addResource()} instead */ public function setResource(ResourceInterface $resource) { - if ($this->type === RelationshipObject::TO_MANY) { + if ($this->type === RelationshipTypeEnum::ToMany) { throw new InputException('can not set a resource on a to-many relationship, use ->addResource()'); } @@ -207,7 +191,7 @@ public function setResource(ResourceInterface $resource) { * @throws InputException if used on a to-one relationship, use {@see ->setResource()} instead */ public function addResource(ResourceInterface $resource) { - if ($this->type === RelationshipObject::TO_ONE) { + if ($this->type === RelationshipTypeEnum::ToOne) { throw new InputException('can not add a resource to a to-one relationship, use ->setResource()'); } @@ -237,18 +221,20 @@ public function hasResource(ResourceInterface $otherResource) { if ($this->isEmpty()) { return false; } - if ($this->type === RelationshipObject::TO_ONE) { - return $this->resource->getResource()->equals($otherResource->getResource()); - } - if ($this->type === RelationshipObject::TO_MANY) { - foreach ($this->resources as $ownResource) { - if ($ownResource->getResource()->equals($otherResource->getResource())) { - return true; + + switch ($this->type) { + case RelationshipTypeEnum::ToOne: + return $this->resource->getResource()->equals($otherResource->getResource()); + + case RelationshipTypeEnum::ToMany: + foreach ($this->resources as $ownResource) { + if ($ownResource->getResource()->equals($otherResource->getResource())) { + return true; + } } - } + + return false; } - - return false; } /** @@ -256,10 +242,10 @@ public function hasResource(ResourceInterface $otherResource) { */ public function isEmpty() { - if ($this->type === RelationshipObject::TO_ONE && $this->resource !== null) { + if ($this->type === RelationshipTypeEnum::ToOne && $this->resource !== null) { return false; } - if ($this->type === RelationshipObject::TO_MANY && $this->resources !== []) { + if ($this->type === RelationshipTypeEnum::ToMany && $this->resources !== []) { return false; } if ($this->links !== null && $this->links->isEmpty() === false) { @@ -291,13 +277,13 @@ public function toArray() { if ($this->links !== null && $this->links->isEmpty() === false) { $array['links'] = $this->links->toArray(); } - if ($this->type === RelationshipObject::TO_ONE) { + if ($this->type === RelationshipTypeEnum::ToOne) { $array['data'] = null; if ($this->resource !== null) { $array['data'] = $this->resource->getResource($identifierOnly=true)->toArray(); } } - if ($this->type === RelationshipObject::TO_MANY) { + if ($this->type === RelationshipTypeEnum::ToMany) { $array['data'] = []; foreach ($this->resources as $resource) { $array['data'][] = $resource->getResource($identifierOnly=true)->toArray(); @@ -319,7 +305,7 @@ public function getNestedContainedResourceObjects() { return []; } - $resources = ($this->type === RelationshipObject::TO_ONE) ? [$this->resource] : $this->resources; + $resources = ($this->type === RelationshipTypeEnum::ToOne) ? [$this->resource] : $this->resources; $resourceObjects = []; foreach ($resources as $resource) { diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index 46720035..d9d12e7c 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -4,6 +4,7 @@ namespace alsvanzelf\jsonapi\objects; +use alsvanzelf\jsonapi\enums\ObjectContainerEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\Exception; use alsvanzelf\jsonapi\helpers\Validator; @@ -43,9 +44,9 @@ public function __construct($type=null, $id=null) { } // always mark as used, as these keys are reserved - $this->validator->claimUsedFields($fieldNames=['type'], Validator::OBJECT_CONTAINER_TYPE); - $this->validator->claimUsedFields($fieldNames=['id'], Validator::OBJECT_CONTAINER_ID); - $this->validator->claimUsedFields($fieldNames=['lid'], Validator::OBJECT_CONTAINER_LID); + $this->validator->claimUsedFields($fieldNames=['type'], ObjectContainerEnum::Type); + $this->validator->claimUsedFields($fieldNames=['id'], ObjectContainerEnum::Id); + $this->validator->claimUsedFields($fieldNames=['lid'], ObjectContainerEnum::Lid); } /** diff --git a/src/objects/ResourceObject.php b/src/objects/ResourceObject.php index 5264b8b3..eff9d304 100644 --- a/src/objects/ResourceObject.php +++ b/src/objects/ResourceObject.php @@ -5,10 +5,10 @@ namespace alsvanzelf\jsonapi\objects; use alsvanzelf\jsonapi\CollectionDocument; +use alsvanzelf\jsonapi\enums\ObjectContainerEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\helpers\Converter; use alsvanzelf\jsonapi\helpers\LinksManager; -use alsvanzelf\jsonapi\helpers\Validator; use alsvanzelf\jsonapi\interfaces\HasAttributesInterface; use alsvanzelf\jsonapi\interfaces\HasLinksInterface; use alsvanzelf\jsonapi\interfaces\RecursiveResourceContainerInterface; @@ -92,7 +92,7 @@ public function add($key, $value, array $options=[]) { $this->attributes = new AttributesObject(); } - $this->validator->claimUsedFields([$key], Validator::OBJECT_CONTAINER_ATTRIBUTES, $options); + $this->validator->claimUsedFields([$key], ObjectContainerEnum::Attributes, $options); $this->attributes->add($key, $value); } @@ -131,8 +131,8 @@ public function setSelfLink($href, array $meta=[]) { */ public function setAttributesObject(AttributesObject $attributesObject, array $options=[]) { $newKeys = $attributesObject->getKeys(); - $this->validator->clearUsedFields(Validator::OBJECT_CONTAINER_ATTRIBUTES); - $this->validator->claimUsedFields($newKeys, Validator::OBJECT_CONTAINER_ATTRIBUTES, $options); + $this->validator->clearUsedFields(ObjectContainerEnum::Attributes); + $this->validator->claimUsedFields($newKeys, ObjectContainerEnum::Attributes, $options); $this->attributes = $attributesObject; } @@ -153,7 +153,7 @@ public function addRelationshipObject($key, RelationshipObject $relationshipObje $this->setRelationshipsObject(new RelationshipsObject()); } - $this->validator->claimUsedFields([$key], Validator::OBJECT_CONTAINER_RELATIONSHIPS, $options); + $this->validator->claimUsedFields([$key], ObjectContainerEnum::Relationships, $options); $this->relationships->addRelationshipObject($key, $relationshipObject); } @@ -163,8 +163,8 @@ public function addRelationshipObject($key, RelationshipObject $relationshipObje */ public function setRelationshipsObject(RelationshipsObject $relationshipsObject) { $newKeys = $relationshipsObject->getKeys(); - $this->validator->clearUsedFields(Validator::OBJECT_CONTAINER_RELATIONSHIPS); - $this->validator->claimUsedFields($newKeys, Validator::OBJECT_CONTAINER_RELATIONSHIPS); + $this->validator->clearUsedFields(ObjectContainerEnum::Relationships); + $this->validator->claimUsedFields($newKeys, ObjectContainerEnum::Relationships); $this->relationships = $relationshipsObject; } diff --git a/src/profiles/CursorPaginationProfile.php b/src/profiles/CursorPaginationProfile.php index 146da7c9..5140cb76 100644 --- a/src/profiles/CursorPaginationProfile.php +++ b/src/profiles/CursorPaginationProfile.php @@ -4,8 +4,8 @@ namespace alsvanzelf\jsonapi\profiles; -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\interfaces\HasLinksInterface; use alsvanzelf\jsonapi\interfaces\HasMetaInterface; @@ -204,7 +204,7 @@ public function setItemMeta(ResourceInterface $resource, $cursor) { ]; if ($resource instanceof ResourceDocument) { - $resource->addMeta('page', $metadata, $level=Document::LEVEL_RESOURCE); + $resource->addMeta('page', $metadata, $level=DocumentLevelEnum::Resource); } else { $resource->addMeta('page', $metadata); diff --git a/tests/ConverterTest.php b/tests/ConverterTest.php index c169c698..57f41b6f 100644 --- a/tests/ConverterTest.php +++ b/tests/ConverterTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\TestCase; use alsvanzelf\jsonapiTests\extensions\TestExtension; use alsvanzelf\jsonapiTests\profiles\TestProfile; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; use alsvanzelf\jsonapi\helpers\Converter; use alsvanzelf\jsonapi\objects\AttributesObject; @@ -70,7 +71,7 @@ public static function dataProviderCamelCaseToWords_HappyPath() { * @group Profiles */ public function testPrepareContentType_HappyPath() { - $this->assertSame('foo', Converter::prepareContentType('foo', [], [])); + $this->assertSame(ContentTypeEnum::Official->value, Converter::prepareContentType(ContentTypeEnum::Official, [], [])); } /** @@ -80,7 +81,7 @@ public function testPrepareContentType_WithExtensionStringLink() { $extension = new TestExtension(); $extension->setOfficialLink('bar'); - $this->assertSame('foo; ext="bar"', Converter::prepareContentType('foo', [$extension], [])); + $this->assertSame(ContentTypeEnum::Official->value.'; ext="bar"', Converter::prepareContentType(ContentTypeEnum::Official, [$extension], [])); } /** @@ -90,7 +91,7 @@ public function testPrepareContentType_WithProfileStringLink() { $profile = new TestProfile(); $profile->setOfficialLink('bar'); - $this->assertSame('foo; profile="bar"', Converter::prepareContentType('foo', [], [$profile])); + $this->assertSame(ContentTypeEnum::Official->value.'; profile="bar"', Converter::prepareContentType(ContentTypeEnum::Official, [], [$profile])); } /** @@ -110,7 +111,7 @@ public function testPrepareContentType_WithMultipleExtensionsAndProfiles() { $profile2 = new TestProfile(); $profile2->setOfficialLink('baz'); - $this->assertSame('foo; ext="bar baz"; profile="bar baz"', Converter::prepareContentType('foo', [$extension1, $extension2], [$profile1, $profile2])); + $this->assertSame(ContentTypeEnum::Official->value.'; ext="bar baz"; profile="bar baz"', Converter::prepareContentType(ContentTypeEnum::Official, [$extension1, $extension2], [$profile1, $profile2])); } } diff --git a/tests/DocumentTest.php b/tests/DocumentTest.php index a15c267b..024f989d 100644 --- a/tests/DocumentTest.php +++ b/tests/DocumentTest.php @@ -4,6 +4,7 @@ namespace alsvanzelf\jsonapiTests; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\Exception; use alsvanzelf\jsonapi\exceptions\InputException; @@ -70,7 +71,7 @@ public function testAddLink_BlocksJsonapiLevel() { $this->expectException(InputException::class); $this->expectExceptionMessage('level "jsonapi" can not be used for links'); - $document->addLink('foo', 'https://jsonapi.org', $meta=[], $level=Document::LEVEL_JSONAPI); + $document->addLink('foo', 'https://jsonapi.org', $meta=[], $level=DocumentLevelEnum::Jsonapi); } public function testAddLink_BlocksResourceLevel() { @@ -79,16 +80,7 @@ public function testAddLink_BlocksResourceLevel() { $this->expectException(InputException::class); $this->expectExceptionMessage('level "resource" can only be set on a ResourceDocument'); - $document->addLink('foo', 'https://jsonapi.org', $meta=[], $level=Document::LEVEL_RESOURCE); - } - - public function testAddLink_BlocksUnknownLevel() { - $document = new Document(); - - $this->expectException(InputException::class); - $this->expectExceptionMessage('unknown level "foo"'); - - $document->addLink('foo', 'https://jsonapi.org', $meta=[], $level='foo'); + $document->addLink('foo', 'https://jsonapi.org', $meta=[], $level=DocumentLevelEnum::Resource); } public function testSetSelfLink_HappyPath() { @@ -162,7 +154,7 @@ public function testAddMeta_AtJsonapiLevel() { $this->assertArrayHasKey('jsonapi', $array); $this->assertArrayNotHasKey('meta', $array['jsonapi']); - $document->addMeta('foo', 'bar', $level=Document::LEVEL_JSONAPI); + $document->addMeta('foo', 'bar', $level=DocumentLevelEnum::Jsonapi); $array = $document->toArray(); @@ -180,16 +172,7 @@ public function testAddMeta_BlocksResourceLevel() { $this->expectException(InputException::class); $this->expectExceptionMessage('level "resource" can only be set on a ResourceDocument'); - $document->addMeta('foo', 'bar', $level=Document::LEVEL_RESOURCE); - } - - public function testAddMeta_BlocksUnknownLevel() { - $document = new Document(); - - $this->expectException(InputException::class); - $this->expectExceptionMessage('unknown level "foo"'); - - $document->addMeta('foo', 'bar', $level='foo'); + $document->addMeta('foo', 'bar', $level=DocumentLevelEnum::Resource); } public function testAddLinkObject_HappyPath() { diff --git a/tests/ResourceDocumentTest.php b/tests/ResourceDocumentTest.php index 2d1586af..01da41d6 100644 --- a/tests/ResourceDocumentTest.php +++ b/tests/ResourceDocumentTest.php @@ -4,8 +4,8 @@ namespace alsvanzelf\jsonapiTests; -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapi\exceptions\Exception; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\objects\AttributesObject; @@ -88,9 +88,9 @@ public function testAddRelationship_DoNotIncludeContainedResources() { public function testAddMeta_HappyPath() { $document = new ResourceDocument(); - $document->addMeta('foo', 'root', $level=Document::LEVEL_ROOT); - $document->addMeta('bar', 'resource', $level=Document::LEVEL_RESOURCE); - $document->addMeta('baz', 'jsonapi', $level=Document::LEVEL_JSONAPI); + $document->addMeta('foo', 'root', $level=DocumentLevelEnum::Root); + $document->addMeta('bar', 'resource', $level=DocumentLevelEnum::Resource); + $document->addMeta('baz', 'jsonapi', $level=DocumentLevelEnum::Jsonapi); $array = $document->toArray(); @@ -124,7 +124,7 @@ public function testAddMeta_RecreateJsonapiObject() { $this->assertArrayNotHasKey('jsonapi', $array); - $document->addMeta('baz', 'jsonapi', $level=Document::LEVEL_JSONAPI); + $document->addMeta('baz', 'jsonapi', $level=DocumentLevelEnum::Jsonapi); $array = $document->toArray(); diff --git a/tests/SeparateProcessTest.php b/tests/SeparateProcessTest.php index 1f8b30da..95f8290a 100644 --- a/tests/SeparateProcessTest.php +++ b/tests/SeparateProcessTest.php @@ -4,7 +4,8 @@ namespace alsvanzelf\jsonapiTests; -use alsvanzelf\jsonapiTests\TestableNonAbstractDocument as Document; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; +use alsvanzelf\jsonapiTests\TestableNonAbstractDocument; use alsvanzelf\jsonapiTests\extensions\TestExtension; use alsvanzelf\jsonapiTests\profiles\TestProfile; use PHPUnit\Framework\TestCase; @@ -17,7 +18,7 @@ class SeparateProcessTest extends TestCase { * @runInSeparateProcess */ public function testSendResponse_HappyPath() { - $document = new Document(); + $document = new TestableNonAbstractDocument(); ob_start(); $document->sendResponse(); @@ -30,7 +31,7 @@ public function testSendResponse_HappyPath() { * @runInSeparateProcess */ public function testSendResponse_NoContent() { - $document = new Document(); + $document = new TestableNonAbstractDocument(); $document->setHttpStatusCode(204); ob_start(); @@ -49,30 +50,30 @@ public function testSendResponse_ContentTypeHeader() { $this->markTestSkipped('can not run without xdebug'); } - $document = new Document(); + $document = new TestableNonAbstractDocument(); ob_start(); $document->sendResponse(); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_OFFICIAL], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Official->value], xdebug_get_headers()); - $options = ['contentType' => Document::CONTENT_TYPE_OFFICIAL]; + $options = ['contentType' => ContentTypeEnum::Official]; ob_start(); $document->sendResponse($options); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_OFFICIAL], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Official->value], xdebug_get_headers()); - $options = ['contentType' => Document::CONTENT_TYPE_DEBUG]; + $options = ['contentType' => ContentTypeEnum::Debug]; ob_start(); $document->sendResponse($options); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_DEBUG], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Debug->value], xdebug_get_headers()); - $options = ['contentType' => Document::CONTENT_TYPE_JSONP]; + $options = ['contentType' => ContentTypeEnum::Jsonp]; ob_start(); $document->sendResponse($options); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_JSONP], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Jsonp->value], xdebug_get_headers()); } /** @@ -88,13 +89,13 @@ public function testSendResponse_ContentTypeHeaderWithExtensions() { $extension->setNamespace('one'); $extension->setOfficialLink('https://jsonapi.org'); - $document = new Document(); + $document = new TestableNonAbstractDocument(); $document->applyExtension($extension); ob_start(); $document->sendResponse(); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_OFFICIAL.'; ext="https://jsonapi.org"'], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Official->value.'; ext="https://jsonapi.org"'], xdebug_get_headers()); $extension = new TestExtension(); $extension->setNamespace('two'); @@ -104,7 +105,7 @@ public function testSendResponse_ContentTypeHeaderWithExtensions() { ob_start(); $document->sendResponse(); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_OFFICIAL.'; ext="https://jsonapi.org https://jsonapi.org/2"'], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Official->value.'; ext="https://jsonapi.org https://jsonapi.org/2"'], xdebug_get_headers()); } /** @@ -119,13 +120,13 @@ public function testSendResponse_ContentTypeHeaderWithProfiles() { $profile = new TestProfile(); $profile->setOfficialLink('https://jsonapi.org'); - $document = new Document(); + $document = new TestableNonAbstractDocument(); $document->applyProfile($profile); ob_start(); $document->sendResponse(); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_OFFICIAL.'; profile="https://jsonapi.org"'], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Official->value.'; profile="https://jsonapi.org"'], xdebug_get_headers()); $profile = new TestProfile(); $profile->setOfficialLink('https://jsonapi.org/2'); @@ -134,14 +135,14 @@ public function testSendResponse_ContentTypeHeaderWithProfiles() { ob_start(); $document->sendResponse(); ob_end_clean(); - $this->assertSame(['Content-Type: '.Document::CONTENT_TYPE_OFFICIAL.'; profile="https://jsonapi.org https://jsonapi.org/2"'], xdebug_get_headers()); + $this->assertSame(['Content-Type: '.ContentTypeEnum::Official->value.'; profile="https://jsonapi.org https://jsonapi.org/2"'], xdebug_get_headers()); } /** * @runInSeparateProcess */ public function testSendResponse_StatusCodeHeader() { - $document = new Document(); + $document = new TestableNonAbstractDocument(); ob_start(); $document->sendResponse(); @@ -171,7 +172,7 @@ public function testSendResponse_StatusCodeHeader() { * @runInSeparateProcess */ public function testSendResponse_CustomJson() { - $document = new Document(); + $document = new TestableNonAbstractDocument(); $options = ['json' => '{"foo":42}']; ob_start(); diff --git a/tests/ValidatorTest.php b/tests/ValidatorTest.php index 654333fa..79286a7b 100644 --- a/tests/ValidatorTest.php +++ b/tests/ValidatorTest.php @@ -4,13 +4,14 @@ namespace alsvanzelf\jsonapiTests; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; -use PHPUnit\Framework\TestCase; +use alsvanzelf\jsonapi\enums\ObjectContainerEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\helpers\Validator; use alsvanzelf\jsonapi\objects\ResourceObject; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\TestCase; class ValidatorTest extends TestCase { #[DoesNotPerformAssertions] @@ -18,11 +19,11 @@ public function testClaimUsedFields_HappyPath() { $validator = new Validator(); $fieldNames = ['foo']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); $fieldNames = ['bar']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); } @@ -30,12 +31,12 @@ public function testClaimUsedFields_EnforceNamespace() { $validator = new Validator(); $fieldNames = ['foo']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); $this->expectException(DuplicateException::class); - $objectContainer = Validator::OBJECT_CONTAINER_RELATIONSHIPS; + $objectContainer = ObjectContainerEnum::Relationships; $validator->claimUsedFields($fieldNames, $objectContainer); } @@ -44,10 +45,10 @@ public function testClaimUsedFields_AllowSameContainer() { $validator = new Validator(); $fieldNames = ['foo']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); } @@ -56,10 +57,10 @@ public function testClaimUsedFields_OptionForReusingTypeField() { $validator = new Validator(); $fieldNames = ['type']; - $objectContainer = Validator::OBJECT_CONTAINER_TYPE; + $objectContainer = ObjectContainerEnum::Type; $validator->claimUsedFields($fieldNames, $objectContainer); - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $options = ['enforceTypeFieldNamespace' => false]; $validator->claimUsedFields($fieldNames, $objectContainer, $options); } @@ -69,7 +70,7 @@ public function testClearUsedFields_HappyPath() { $validator = new Validator(); $fieldNames = ['foo']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); $validator->clearUsedFields($objectContainer); @@ -79,13 +80,13 @@ public function testClearUsedFields_FreesForAnotherNamespace() { $validator = new Validator(); $fieldNames = ['foo', 'bar']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); $thrown = false; try { $fieldNames = ['bar']; - $objectContainer = Validator::OBJECT_CONTAINER_RELATIONSHIPS; + $objectContainer = ObjectContainerEnum::Relationships; $validator->claimUsedFields($fieldNames, $objectContainer); } catch (DuplicateException) { @@ -93,21 +94,21 @@ public function testClearUsedFields_FreesForAnotherNamespace() { } $this->assertTrue($thrown); - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->clearUsedFields($objectContainer); $fieldNames = ['foo']; - $objectContainer = Validator::OBJECT_CONTAINER_ATTRIBUTES; + $objectContainer = ObjectContainerEnum::Attributes; $validator->claimUsedFields($fieldNames, $objectContainer); $fieldNames = ['bar']; - $objectContainer = Validator::OBJECT_CONTAINER_RELATIONSHIPS; + $objectContainer = ObjectContainerEnum::Relationships; $validator->claimUsedFields($fieldNames, $objectContainer); $this->expectException(DuplicateException::class); $fieldNames = ['foo']; - $objectContainer = Validator::OBJECT_CONTAINER_RELATIONSHIPS; + $objectContainer = ObjectContainerEnum::Relationships; $validator->claimUsedFields($fieldNames, $objectContainer); } diff --git a/tests/example_output/at_members_everywhere/at_members_everywhere.php b/tests/example_output/at_members_everywhere/at_members_everywhere.php index c099ca03..a6774a0d 100644 --- a/tests/example_output/at_members_everywhere/at_members_everywhere.php +++ b/tests/example_output/at_members_everywhere/at_members_everywhere.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapiTests\example_output\at_members_everywhere; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\AttributesObject; use alsvanzelf\jsonapi\objects\JsonapiObject; use alsvanzelf\jsonapi\objects\LinkObject; @@ -89,7 +90,7 @@ public static function createJsonapiDocument() { $metaObject = new MetaObject(); $metaObject->addAtMember('context', '/data/relationships/foo/meta/@context'); - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->addAtMember('context', '/data/relationships/foo/@context'); $relationshipObject->setResource($resourceObject); $relationshipObject->setLinksObject($linksObject); @@ -104,7 +105,7 @@ public static function createJsonapiDocument() { $resourceIdentifierObject->addAtMember('context', '/data/relationships/bar/data/@context'); $resourceIdentifierObject->setMetaObject($metaObject); - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->addAtMember('context', '/data/relationships/bar/@context'); $relationshipObject->setResource($resourceIdentifierObject); diff --git a/tests/example_output/extension_members_everywhere/extension_members_everywhere.php b/tests/example_output/extension_members_everywhere/extension_members_everywhere.php index e4a6432b..6790a1f3 100644 --- a/tests/example_output/extension_members_everywhere/extension_members_everywhere.php +++ b/tests/example_output/extension_members_everywhere/extension_members_everywhere.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapiTests\example_output\extension_members_everywhere; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\AttributesObject; use alsvanzelf\jsonapi\objects\JsonapiObject; use alsvanzelf\jsonapi\objects\LinkObject; @@ -94,7 +95,7 @@ public static function createJsonapiDocument() { $metaObject = new MetaObject(); $metaObject->addExtensionMember($extension, 'key', '/data/relationships/foo/meta/key'); - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->addExtensionMember($extension, 'key', '/data/relationships/foo/key'); $relationshipObject->setResource($resourceObject); $relationshipObject->setLinksObject($linksObject); @@ -109,7 +110,7 @@ public static function createJsonapiDocument() { $resourceIdentifierObject->addExtensionMember($extension, 'key', '/data/relationships/bar/data/key'); $resourceIdentifierObject->setMetaObject($metaObject); - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->addExtensionMember($extension, 'key', '/data/relationships/bar/key'); $relationshipObject->setResource($resourceIdentifierObject); diff --git a/tests/example_output/null_values/null_values.php b/tests/example_output/null_values/null_values.php index 3cdc0fe4..488910b9 100644 --- a/tests/example_output/null_values/null_values.php +++ b/tests/example_output/null_values/null_values.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapiTests\example_output\null_values; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\LinkObject; use alsvanzelf\jsonapi\objects\RelationshipObject; @@ -19,8 +20,8 @@ public static function createJsonapiDocument() { $document->addLinkObject('bar', new LinkObject()); $document->addRelationship('bar', null); - $document->addRelationshipObject('baz', new RelationshipObject(RelationshipObject::TO_ONE)); - $document->addRelationshipObject('baf', new RelationshipObject(RelationshipObject::TO_MANY)); + $document->addRelationshipObject('baz', new RelationshipObject(RelationshipTypeEnum::ToOne)); + $document->addRelationshipObject('baf', new RelationshipObject(RelationshipTypeEnum::ToMany)); return $document; } diff --git a/tests/example_output/relationship_to_one_document/relationship_to_one_document.php b/tests/example_output/relationship_to_one_document/relationship_to_one_document.php index ba463ee1..4da6f756 100644 --- a/tests/example_output/relationship_to_one_document/relationship_to_one_document.php +++ b/tests/example_output/relationship_to_one_document/relationship_to_one_document.php @@ -4,14 +4,14 @@ namespace alsvanzelf\jsonapiTests\example_output\relationship_to_one_document; -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; class relationship_to_one_document { public static function createJsonapiDocument() { $document = new ResourceDocument('author', 12); - $document->setSelfLink('/articles/1/relationship/author', $meta=[], $level=Document::LEVEL_ROOT); + $document->setSelfLink('/articles/1/relationship/author', $meta=[], $level=DocumentLevelEnum::Root); $document->addLink('related', '/articles/1/author'); return $document; diff --git a/tests/example_output/relationships/relationships.php b/tests/example_output/relationships/relationships.php index 17b6652e..49f833f2 100644 --- a/tests/example_output/relationships/relationships.php +++ b/tests/example_output/relationships/relationships.php @@ -6,6 +6,7 @@ use alsvanzelf\jsonapi\CollectionDocument; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\RelationshipObject; use alsvanzelf\jsonapi\objects\ResourceObject; @@ -45,7 +46,7 @@ public static function createJsonapiDocument() { * to-many relationship, one-by-one */ - $relationshipObject = new RelationshipObject($type=RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject($type=RelationshipTypeEnum::ToMany); $relationshipObject->addResource($friend1Resource); $relationshipObject->addResource($friend2Resource); @@ -65,7 +66,7 @@ public static function createJsonapiDocument() { * to-many relationship, different types */ - $relationshipObject = new RelationshipObject($type=RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject($type=RelationshipTypeEnum::ToMany); $relationshipObject->addResource($ship1Resource); $relationshipObject->addResource($dockResource); diff --git a/tests/example_output/resource_links/resource_links.json b/tests/example_output/resource_links/resource_links.json index 95cfa069..5942883d 100644 --- a/tests/example_output/resource_links/resource_links.json +++ b/tests/example_output/resource_links/resource_links.json @@ -6,7 +6,7 @@ "redirect": { "href": "/login", "meta": { - "level": "root" + "level": "Root" } } }, @@ -22,13 +22,13 @@ "self": { "href": "/user/42", "meta": { - "level": "resource" + "level": "Resource" } }, "partner": { "href": "/user/1", "meta": { - "level": "resource" + "level": "Resource" } } } diff --git a/tests/example_output/resource_links/resource_links.php b/tests/example_output/resource_links/resource_links.php index 10f1eef5..2da44728 100644 --- a/tests/example_output/resource_links/resource_links.php +++ b/tests/example_output/resource_links/resource_links.php @@ -4,8 +4,8 @@ namespace alsvanzelf\jsonapiTests\example_output\resource_links; -use alsvanzelf\jsonapi\Document; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\DocumentLevelEnum; use alsvanzelf\jsonapiTests\example_output\ExampleUser; class resource_links { @@ -16,13 +16,13 @@ public static function createJsonapiDocument() { $document = ResourceDocument::fromObject($user42, $type='user', $user42->id); - $selfResourceMeta = ['level' => Document::LEVEL_RESOURCE]; - $partnerMeta = ['level' => Document::LEVEL_RESOURCE]; - $redirectMeta = ['level' => Document::LEVEL_ROOT]; + $selfResourceMeta = ['level' => DocumentLevelEnum::Resource->name]; + $partnerMeta = ['level' => DocumentLevelEnum::Resource->name]; + $redirectMeta = ['level' => DocumentLevelEnum::Root->name]; $document->setSelfLink('/user/42', $selfResourceMeta); - $document->addLink('partner', '/user/1', $partnerMeta, $level=Document::LEVEL_RESOURCE); - $document->addLink('redirect', '/login', $redirectMeta, $level=Document::LEVEL_ROOT); + $document->addLink('partner', '/user/1', $partnerMeta, $level=DocumentLevelEnum::Resource); + $document->addLink('redirect', '/login', $redirectMeta, $level=DocumentLevelEnum::Root); return $document; } diff --git a/tests/example_output/resource_spec_api/resource_spec_api.php b/tests/example_output/resource_spec_api/resource_spec_api.php index 5414b862..62bcc5e6 100644 --- a/tests/example_output/resource_spec_api/resource_spec_api.php +++ b/tests/example_output/resource_spec_api/resource_spec_api.php @@ -5,6 +5,7 @@ namespace alsvanzelf\jsonapiTests\example_output\resource_spec_api; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\objects\AttributesObject; use alsvanzelf\jsonapi\objects\LinksObject; use alsvanzelf\jsonapi\objects\MetaObject; @@ -45,7 +46,7 @@ public static function createJsonapiDocument() { $resource->setType('user'); $resource->setAttributesObject($attributes42); - $relationship = new RelationshipObject(RelationshipObject::TO_ONE); + $relationship = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationship->setResource($resource); $relationships = new RelationshipsObject(); $relationships->addRelationshipObject('friend', $relationship); diff --git a/tests/helpers/RequestParserTest.php b/tests/helpers/RequestParserTest.php index 3155bb40..529696e1 100644 --- a/tests/helpers/RequestParserTest.php +++ b/tests/helpers/RequestParserTest.php @@ -4,7 +4,8 @@ namespace alsvanzelf\jsonapiTests\helpers; -use alsvanzelf\jsonapi\Document; +use alsvanzelf\jsonapi\enums\ContentTypeEnum; +use alsvanzelf\jsonapi\enums\SortOrderEnum; use alsvanzelf\jsonapi\helpers\RequestParser; use alsvanzelf\jsonapiTests\helpers\TestableNonInterfaceRequestInterface; use alsvanzelf\jsonapiTests\helpers\TestableNonInterfaceServerRequestInterface; @@ -28,7 +29,7 @@ public function testFromSuperglobals_HappyPath() { $_SERVER['REQUEST_SCHEME'] = 'https'; $_SERVER['HTTP_HOST'] = 'example.org'; $_SERVER['REQUEST_URI'] = '/user/42?'.http_build_query($_GET); - $_SERVER['CONTENT_TYPE'] = Document::CONTENT_TYPE_OFFICIAL; + $_SERVER['CONTENT_TYPE'] = ContentTypeEnum::Official->value; $_POST = [ 'data' => [ @@ -63,7 +64,7 @@ public function testFromSuperglobals_HappyPath() { $this->assertSame(['ship' => ['wing' => []]], $requestParser->getIncludePaths()); $this->assertSame(['name', 'location'], $requestParser->getSparseFieldset('user')); - $this->assertSame([['field' => 'name', 'order' => RequestParser::SORT_ASCENDING], ['field' => 'location', 'order' => RequestParser::SORT_DESCENDING]], $requestParser->getSortFields()); + $this->assertSame([['field' => 'name', 'order' => SortOrderEnum::Ascending], ['field' => 'location', 'order' => SortOrderEnum::Descending]], $requestParser->getSortFields()); $this->assertSame(['number' => '2', 'size' => '10'], $requestParser->getPagination()); $this->assertSame('42', $requestParser->getFilter()); @@ -82,7 +83,7 @@ public function testFromSuperglobals_WithPhpInputStream() { $_SERVER['REQUEST_SCHEME'] = 'https'; $_SERVER['HTTP_HOST'] = 'example.org'; $_SERVER['REQUEST_URI'] = '/'; - $_SERVER['CONTENT_TYPE'] = Document::CONTENT_TYPE_OFFICIAL; + $_SERVER['CONTENT_TYPE'] = ContentTypeEnum::Official->value; $_GET = []; $_POST = []; @@ -154,7 +155,7 @@ public function testFromPsrRequest_WithRequestInterface() { $this->assertSame(['ship' => ['wing' => []]], $requestParser->getIncludePaths()); $this->assertSame(['name', 'location'], $requestParser->getSparseFieldset('user')); - $this->assertSame([['field' => 'name', 'order' => RequestParser::SORT_ASCENDING], ['field' => 'location', 'order' => RequestParser::SORT_DESCENDING]], $requestParser->getSortFields()); + $this->assertSame([['field' => 'name', 'order' => SortOrderEnum::Ascending], ['field' => 'location', 'order' => SortOrderEnum::Descending]], $requestParser->getSortFields()); $this->assertSame(['number' => '2', 'size' => '10'], $requestParser->getPagination()); $this->assertSame('42', $requestParser->getFilter()); @@ -192,7 +193,7 @@ public function testFromPsrRequest_WithServerRequestInterface() { $this->assertSame('https://example.org/user/42?'.http_build_query($queryParameters), $requestParser->getSelfLink()); $this->assertTrue($requestParser->hasSortFields()); - $this->assertSame([['field' => 'name', 'order' => RequestParser::SORT_ASCENDING], ['field' => 'location', 'order' => RequestParser::SORT_DESCENDING]], $requestParser->getSortFields()); + $this->assertSame([['field' => 'name', 'order' => SortOrderEnum::Ascending], ['field' => 'location', 'order' => SortOrderEnum::Descending]], $requestParser->getSortFields()); } public function testGetSelfLink() { @@ -294,15 +295,15 @@ public function testHasSortFields() { public function testGetSortFields_Reformatted() { $queryParameters = ['sort' => 'foo']; $requestParser = new RequestParser($selfLink='', $queryParameters); - $this->assertSame([['field' => 'foo', 'order' => RequestParser::SORT_ASCENDING]], $requestParser->getSortFields()); + $this->assertSame([['field' => 'foo', 'order' => SortOrderEnum::Ascending]], $requestParser->getSortFields()); $queryParameters = ['sort' => '-bar']; $requestParser = new RequestParser($selfLink='', $queryParameters); - $this->assertSame([['field' => 'bar', 'order' => RequestParser::SORT_DESCENDING]], $requestParser->getSortFields()); + $this->assertSame([['field' => 'bar', 'order' => SortOrderEnum::Descending]], $requestParser->getSortFields()); $queryParameters = ['sort' => 'foo,-bar']; $requestParser = new RequestParser($selfLink='', $queryParameters); - $this->assertSame([['field' => 'foo', 'order' => RequestParser::SORT_ASCENDING], ['field' => 'bar', 'order' => RequestParser::SORT_DESCENDING]], $requestParser->getSortFields()); + $this->assertSame([['field' => 'foo', 'order' => SortOrderEnum::Ascending], ['field' => 'bar', 'order' => SortOrderEnum::Descending]], $requestParser->getSortFields()); } public function testGetSortFields_Raw() { diff --git a/tests/objects/RelationshipObjectTest.php b/tests/objects/RelationshipObjectTest.php index ce09ae57..b941feaa 100644 --- a/tests/objects/RelationshipObjectTest.php +++ b/tests/objects/RelationshipObjectTest.php @@ -6,6 +6,7 @@ use alsvanzelf\jsonapi\CollectionDocument; use alsvanzelf\jsonapi\ResourceDocument; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\objects\LinkObject; use alsvanzelf\jsonapi\objects\RelationshipObject; @@ -16,25 +17,19 @@ class RelationshipObjectTest extends TestCase { public function testConstructor_ToOne() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->setResource(new ResourceObject('user', 42)); $this->validateToOneRelationshipArray($relationshipObject->toArray()); } public function testConstructor_ToMany() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $relationshipObject->addResource(new ResourceObject('user', 42)); $this->validateToManyRelationshipArray($relationshipObject->toArray()); } - public function testConstructor_UnknownType() { - $this->expectException(InputException::class); - - $relationshipObject = new RelationshipObject('foo'); - } - public function testFromAnything_WithResourceObject() { $resourceObject = new ResourceObject('user', 42); $resourceObject->addMeta('foo', 'bar'); @@ -82,7 +77,7 @@ public function testFromAnything_WithUnknownType() { public function testFromResource_ToMany() { $resourceObject = new ResourceObject('user', 42); - $type = RelationshipObject::TO_MANY; + $type = RelationshipTypeEnum::ToMany; $relationshipObject = RelationshipObject::fromResource($resourceObject, $links=[], $meta=[], $type); @@ -139,7 +134,7 @@ public function testFromCollectionDocument_WithMeta() { } public function testSetSelfLink_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->setSelfLink('https://jsonapi.org'); $array = $relationshipObject->toArray(); @@ -150,7 +145,7 @@ public function testSetSelfLink_HappyPath() { } public function testSetRelatedLink_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->setRelatedLink('https://jsonapi.org'); $array = $relationshipObject->toArray(); @@ -161,7 +156,7 @@ public function testSetRelatedLink_HappyPath() { } public function testSetPaginationLinks_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $baseUrl = 'https://jsonapi.org/?page='; $relationshipObject->setPaginationLinks($baseUrl.'prev', $baseUrl.'next', $baseUrl.'first', $baseUrl.'last'); @@ -181,7 +176,7 @@ public function testSetPaginationLinks_HappyPath() { } public function testSetPaginationLinks_BlockedOnToOne() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $this->expectException(InputException::class); @@ -189,7 +184,7 @@ public function testSetPaginationLinks_BlockedOnToOne() { } public function testAddMeta_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $this->assertTrue($relationshipObject->isEmpty()); @@ -207,7 +202,7 @@ public function testAddMeta_HappyPath() { public function testHasResource_ToMany() { $resourceObject = new ResourceObject('user', 42); - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $relationshipObject->addResource($resourceObject); $this->assertTrue($relationshipObject->hasResource($resourceObject)); @@ -216,7 +211,7 @@ public function testHasResource_ToMany() { } public function testGetContainedResources_SkipsResourceIdentifierObjects() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $resourceIdentifierObject = new ResourceIdentifierObject('user', 24); $resourceObjectIdentifierOnly = new ResourceObject('user', 42); $resourceObjectWithAttributes = new ResourceObject('user', 42); @@ -238,14 +233,14 @@ public function testGetContainedResources_SkipsResourceIdentifierObjects() { } public function testSetResource_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->setResource(new ResourceObject('user', 42)); $this->validateToOneRelationshipArray($relationshipObject->toArray()); } public function testSetResource_RequiresToOneType() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $this->expectException(InputException::class); @@ -253,14 +248,14 @@ public function testSetResource_RequiresToOneType() { } public function testAddResource_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $relationshipObject->addResource(new ResourceObject('user', 42)); $this->validateToManyRelationshipArray($relationshipObject->toArray()); } public function testAddResource_RequiresToOneType() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $this->expectException(InputException::class); @@ -268,7 +263,7 @@ public function testAddResource_RequiresToOneType() { } public function testAddLinkObject_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $this->assertTrue($relationshipObject->isEmpty()); @@ -286,7 +281,7 @@ public function testAddLinkObject_HappyPath() { } public function testToArray_EmptyResource() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $array = $relationshipObject->toArray(); @@ -295,7 +290,7 @@ public function testToArray_EmptyResource() { } public function testToArray_EmptyResources() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_MANY); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToMany); $array = $relationshipObject->toArray(); @@ -304,7 +299,7 @@ public function testToArray_EmptyResources() { } public function testIsEmpty_WithAtMembers() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $this->assertTrue($relationshipObject->isEmpty()); @@ -317,7 +312,7 @@ public function testIsEmpty_WithAtMembers() { * @group Extensions */ public function testIsEmpty_WithExtensionMembers() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $this->assertTrue($relationshipObject->isEmpty()); diff --git a/tests/objects/RelationshipsObjectTest.php b/tests/objects/RelationshipsObjectTest.php index deddba14..9c98b613 100644 --- a/tests/objects/RelationshipsObjectTest.php +++ b/tests/objects/RelationshipsObjectTest.php @@ -4,6 +4,7 @@ namespace alsvanzelf\jsonapiTests\objects; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\objects\RelationshipObject; @@ -96,7 +97,7 @@ public function testAddRelationshipObject_MultipleReusingKeys() { } public function testToArray_EmptyRelationship() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipsObject = new RelationshipsObject(); $relationshipsObject->addRelationshipObject($key='foo', $relationshipObject); diff --git a/tests/objects/ResourceObjectTest.php b/tests/objects/ResourceObjectTest.php index ff4583c4..693c8595 100644 --- a/tests/objects/ResourceObjectTest.php +++ b/tests/objects/ResourceObjectTest.php @@ -4,6 +4,7 @@ namespace alsvanzelf\jsonapiTests\objects; +use alsvanzelf\jsonapi\enums\RelationshipTypeEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\objects\AttributesObject; use alsvanzelf\jsonapi\objects\LinkObject; @@ -129,7 +130,7 @@ public function testHasIdentifierPropertiesOnly_No() { } public function testAddRelationshipObject_HappyPath() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->setResource(new ResourceObject('user', 42)); $resourceObject = new ResourceObject('user', 24); @@ -153,7 +154,7 @@ public function testAddRelationshipObject_HappyPath() { } public function testAddRelationshipObject_BlockDrosteEffect() { - $relationshipObject = new RelationshipObject(RelationshipObject::TO_ONE); + $relationshipObject = new RelationshipObject(RelationshipTypeEnum::ToOne); $relationshipObject->setResource(new ResourceObject('user', 42)); $resourceObject = new ResourceObject('user', 42); From 30c3047a37bd00bc4f8fadb44213c2161a14ae15 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Mon, 22 Dec 2025 21:14:45 +0100 Subject: [PATCH 03/12] add strict type to all interfaces --- README.md | 4 ++-- UPGRADE_2_TO_3.md | 6 ++++++ examples/bootstrap_examples.php | 6 +++--- phpstan.neon | 2 ++ src/CollectionDocument.php | 9 +++++++-- src/Document.php | 13 +++++------- src/ResourceDocument.php | 20 ++++++++----------- src/extensions/AtomicOperationsExtension.php | 4 ++-- src/helpers/ExtensionMemberManager.php | 19 ++++++------------ src/helpers/LinksManager.php | 17 ++++++---------- src/interfaces/ExtensionInterface.php | 8 ++------ src/interfaces/HasAttributesInterface.php | 5 ++--- .../HasExtensionMembersInterface.php | 15 ++++---------- src/interfaces/HasLinksInterface.php | 11 ++++------ src/interfaces/HasMetaInterface.php | 6 +----- src/interfaces/ObjectInterface.php | 8 +++----- src/interfaces/PaginableInterface.php | 13 ++++++------ src/interfaces/ProfileInterface.php | 4 +--- .../RecursiveResourceContainerInterface.php | 2 +- src/interfaces/ResourceContainerInterface.php | 4 ++-- src/interfaces/ResourceInterface.php | 5 ++--- src/objects/AttributesObject.php | 4 ++-- src/objects/ErrorObject.php | 10 +++------- src/objects/JsonapiObject.php | 10 +++------- src/objects/LinkObject.php | 10 +++------- src/objects/LinksObject.php | 4 ++-- src/objects/MetaObject.php | 4 ++-- src/objects/RelationshipObject.php | 19 +++++++++--------- src/objects/RelationshipsObject.php | 6 +++--- src/objects/ResourceIdentifierObject.php | 12 ++++------- src/objects/ResourceObject.php | 12 +++++------ src/profiles/CursorPaginationProfile.php | 2 +- .../ExampleEverywhereExtension.php | 4 ++-- .../ExampleTimestampsProfile.php | 2 +- .../ExampleVersionExtension.php | 4 ++-- tests/extensions/TestExtension.php | 12 +++++------ tests/objects/AttributesObjectTest.php | 17 ++++++++++++++++ tests/objects/MetaObjectTest.php | 17 ++++++++++++++++ tests/profiles/TestProfile.php | 2 +- 39 files changed, 160 insertions(+), 172 deletions(-) diff --git a/README.md b/README.md index f6ba963c..1eba693f 100644 --- a/README.md +++ b/README.md @@ -175,11 +175,11 @@ use alsvanzelf\jsonapi\ResourceDocument; use alsvanzelf\jsonapi\interfaces\ExtensionInterface; class ExampleExtension implements ExtensionInterface { - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://example.org/extension-documentation'; } - public function getNamespace() { + public function getNamespace(): string { return 'foo'; } } diff --git a/UPGRADE_2_TO_3.md b/UPGRADE_2_TO_3.md index 0f604425..d2e331ef 100644 --- a/UPGRADE_2_TO_3.md +++ b/UPGRADE_2_TO_3.md @@ -1,5 +1,11 @@ # Upgrade from library v2 to v3 +## Interfaces + +When extending interfaces, you'll have to add method argument types and return types. + +Check [all interfaces](/src/interfaces) for the correct typing. + ## Enums Content types: diff --git a/examples/bootstrap_examples.php b/examples/bootstrap_examples.php index a0733574..acf810cc 100644 --- a/examples/bootstrap_examples.php +++ b/examples/bootstrap_examples.php @@ -112,11 +112,11 @@ class ExampleVersionExtension implements ExtensionInterface { * the required method */ - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://jsonapi.org/format/1.1/#extension-rules'; } - public function getNamespace() { + public function getNamespace(): string { return 'version'; } @@ -143,7 +143,7 @@ class ExampleTimestampsProfile implements ProfileInterface { * the required method */ - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://jsonapi.org/recommendations/#authoring-profiles'; } diff --git a/phpstan.neon b/phpstan.neon index cab47881..992b5b7d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -6,6 +6,8 @@ parameters: - tests/ - examples/ + typeAliases: + TypeAlias_InternalOptions: 'array' treatPhpDocTypesAsCertain: false strictRules: diff --git a/src/CollectionDocument.php b/src/CollectionDocument.php index 55cae30a..9cb2c1eb 100644 --- a/src/CollectionDocument.php +++ b/src/CollectionDocument.php @@ -65,7 +65,12 @@ public function add($type, $id, array $attributes=[]) { } } - public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null) { + public function setPaginationLinks( + ?string $previousHref=null, + ?string $nextHref=null, + ?string $firstHref=null, + ?string $lastHref=null, + ): void { if ($previousHref !== null) { $this->addLink('prev', $previousHref); } @@ -129,7 +134,7 @@ public function toArray() { * ResourceContainerInterface */ - public function getContainedResources() { + public function getContainedResources(): array { return $this->resources; } } diff --git a/src/Document.php b/src/Document.php index 8c1bea3f..caf83c60 100644 --- a/src/Document.php +++ b/src/Document.php @@ -86,13 +86,13 @@ public function __construct() { */ /** - * @param string $key - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * if $meta is given, a LinkObject is added, otherwise a link string is added + * + * @param array $meta * * @throws InputException if the $level is not DocumentLevelEnum::Root */ - public function addLink($key, $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root) { + public function addLink(string $key, ?string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root): void { match ($level) { DocumentLevelEnum::Root => $this->linkManagerAddLink($key, $href, $meta), DocumentLevelEnum::Jsonapi => throw new InputException('level "jsonapi" can not be used for links'), @@ -137,13 +137,10 @@ public function setDescribedByLink($href, array $meta=[]) { } /** - * @param string $key - * @param mixed $value - * * @throws InputException if the $level is unknown * @throws InputException if the $level is DocumentLevelEnum::Resource */ - public function addMeta($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Root) { + public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($level === DocumentLevelEnum::Root) { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); diff --git a/src/ResourceDocument.php b/src/ResourceDocument.php index 085ef1a6..29f3845b 100644 --- a/src/ResourceDocument.php +++ b/src/ResourceDocument.php @@ -122,11 +122,11 @@ public function addRelationship($key, $relation, array $links=[], array $meta=[] } /** - * @param string $key - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * if $meta is given, a LinkObject is added, otherwise a link string is added + * + * @param array $meta */ - public function addLink($key, $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root) { + public function addLink(string $key, ?string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -158,11 +158,7 @@ public function setSelfLink($href, array $meta=[], DocumentLevelEnum $level=Docu } } - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Root) { + public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($level === DocumentLevelEnum::Resource) { $this->resource->addMeta($key, $value); } @@ -306,15 +302,15 @@ public function toArray() { * HasAttributesInterface */ - public function addAttribute($key, $value, array $options=[]) { - return $this->add($key, $value); + public function addAttribute(string $key, mixed $value, array $options=[]): void { + $this->add($key, $value); } /** * ResourceInterface */ - public function getResource($identifierOnly=false) { + public function getResource(bool $identifierOnly=false): ResourceIdentifierObject|ResourceObject { return $this->resource->getResource($identifierOnly); } } diff --git a/src/extensions/AtomicOperationsExtension.php b/src/extensions/AtomicOperationsExtension.php index e2d1d42d..f5f3f86b 100644 --- a/src/extensions/AtomicOperationsExtension.php +++ b/src/extensions/AtomicOperationsExtension.php @@ -18,11 +18,11 @@ class AtomicOperationsExtension implements ExtensionInterface { * ExtensionInterface */ - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://jsonapi.org/ext/atomic/'; } - public function getNamespace() { + public function getNamespace(): string { return 'atomic'; } } diff --git a/src/helpers/ExtensionMemberManager.php b/src/helpers/ExtensionMemberManager.php index f971fc8a..7d9c2c9a 100644 --- a/src/helpers/ExtensionMemberManager.php +++ b/src/helpers/ExtensionMemberManager.php @@ -9,8 +9,8 @@ use alsvanzelf\jsonapi\interfaces\ExtensionInterface; trait ExtensionMemberManager { - /** @var array */ - protected $extensionMembers = []; + /** @var array */ + protected array $extensionMembers = []; /** * human api @@ -20,12 +20,7 @@ trait ExtensionMemberManager { * spec api */ - /** - * @param ExtensionInterface $extension - * @param string $key - * @param mixed $value - */ - public function addExtensionMember(ExtensionInterface $extension, $key, $value) { + public function addExtensionMember(ExtensionInterface $extension, string $key, mixed $value): void { $namespace = $extension->getNamespace(); if (str_starts_with($key, $namespace.':')) { @@ -47,19 +42,17 @@ public function addExtensionMember(ExtensionInterface $extension, $key, $value) /** * @internal - * - * @return boolean */ - public function hasExtensionMembers() { + public function hasExtensionMembers(): bool { return ($this->extensionMembers !== []); } /** * @internal * - * @return array + * @return array */ - public function getExtensionMembers() { + public function getExtensionMembers(): array { return $this->extensionMembers; } } diff --git a/src/helpers/LinksManager.php b/src/helpers/LinksManager.php index 844ba9f0..ec785452 100644 --- a/src/helpers/LinksManager.php +++ b/src/helpers/LinksManager.php @@ -18,11 +18,11 @@ trait LinksManager { /** * set a key containing a link * - * @param string $key - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * if $meta is given, a LinkObject is added, otherwise a link string is added + * + * @param array $meta */ - public function addLink($key, $href, array $meta=[]) { + public function addLink(string $key, ?string $href, array $meta=[]): void { $this->ensureLinksObject(); $this->links->add($key, $href, $meta); } @@ -33,21 +33,16 @@ public function addLink($key, $href, array $meta=[]) { /** * set a key containing a LinkObject - * - * @param string $key - * @param LinkObject $linkObject */ - public function addLinkObject($key, LinkObject $linkObject) { + public function addLinkObject(string $key, LinkObject $linkObject): void { $this->ensureLinksObject(); $this->links->addLinkObject($key, $linkObject); } /** * set a LinksObject containing all links - * - * @param LinksObject $linksObject */ - public function setLinksObject(LinksObject $linksObject) { + public function setLinksObject(LinksObject $linksObject): void { $this->links = $linksObject; } diff --git a/src/interfaces/ExtensionInterface.php b/src/interfaces/ExtensionInterface.php index 57242b0f..1c8eae67 100644 --- a/src/interfaces/ExtensionInterface.php +++ b/src/interfaces/ExtensionInterface.php @@ -9,15 +9,11 @@ interface ExtensionInterface { * the unique link identifying and describing the extension * * @internal - * - * @return string */ - public function getOfficialLink(); + public function getOfficialLink(): string; /** * get the extension's namespace - * - * @return string */ - public function getNamespace(); + public function getNamespace(): string; } diff --git a/src/interfaces/HasAttributesInterface.php b/src/interfaces/HasAttributesInterface.php index 889190d2..4b3fd32f 100644 --- a/src/interfaces/HasAttributesInterface.php +++ b/src/interfaces/HasAttributesInterface.php @@ -10,8 +10,7 @@ interface HasAttributesInterface { * * @see ResourceObject::$defaults * - * @param string $key - * @param mixed $value + * @param TypeAlias_InternalOptions $options */ - public function addAttribute($key, $value, array $options=[]); + public function addAttribute(string $key, mixed $value, array $options=[]): void; } diff --git a/src/interfaces/HasExtensionMembersInterface.php b/src/interfaces/HasExtensionMembersInterface.php index 81f71a57..16e6ce49 100644 --- a/src/interfaces/HasExtensionMembersInterface.php +++ b/src/interfaces/HasExtensionMembersInterface.php @@ -7,24 +7,17 @@ use alsvanzelf\jsonapi\interfaces\ExtensionInterface; interface HasExtensionMembersInterface { - /** - * @param ExtensionInterface $extension - * @param string $key - * @param mixed $value - */ - public function addExtensionMember(ExtensionInterface $extension, $key, $value); + public function addExtensionMember(ExtensionInterface $extension, string $key, mixed $value): void; /** * @internal - * - * @return boolean */ - public function hasExtensionMembers(); + public function hasExtensionMembers(): bool; /** * @internal * - * @return array + * @return array */ - public function getExtensionMembers(); + public function getExtensionMembers(): array; } diff --git a/src/interfaces/HasLinksInterface.php b/src/interfaces/HasLinksInterface.php index d867ada5..ef0ecee7 100644 --- a/src/interfaces/HasLinksInterface.php +++ b/src/interfaces/HasLinksInterface.php @@ -13,20 +13,17 @@ interface HasLinksInterface { * * if $meta is given, a LinkObject is added, otherwise a link string is added * - * @param string $key - * @param string $href + * @param array $meta */ - public function addLink($key, $href, array $meta=[]); + public function addLink(string $key, ?string $href, array $meta=[]): void; /** * set a key containing a LinkObject - * - * @param string $key */ - public function addLinkObject($key, LinkObject $linkObject); + public function addLinkObject(string $key, LinkObject $linkObject): void; /** * set a LinksObject containing all links */ - public function setLinksObject(LinksObject $linksObject); + public function setLinksObject(LinksObject $linksObject): void; } diff --git a/src/interfaces/HasMetaInterface.php b/src/interfaces/HasMetaInterface.php index 52ac6b46..2c868397 100644 --- a/src/interfaces/HasMetaInterface.php +++ b/src/interfaces/HasMetaInterface.php @@ -5,9 +5,5 @@ namespace alsvanzelf\jsonapi\interfaces; interface HasMetaInterface { - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value); + public function addMeta(string $key, mixed $value): void; } diff --git a/src/interfaces/ObjectInterface.php b/src/interfaces/ObjectInterface.php index 9766f839..0ff24856 100644 --- a/src/interfaces/ObjectInterface.php +++ b/src/interfaces/ObjectInterface.php @@ -9,17 +9,15 @@ interface ObjectInterface { * whether the object contains something for output * * @internal - * - * @return boolean */ - public function isEmpty(); + public function isEmpty(): bool; /** * generate array with the contents of the object * * @internal * - * @return array + * @return array */ - public function toArray(); + public function toArray(): array; } diff --git a/src/interfaces/PaginableInterface.php b/src/interfaces/PaginableInterface.php index 32f4b770..fbde5fb9 100644 --- a/src/interfaces/PaginableInterface.php +++ b/src/interfaces/PaginableInterface.php @@ -5,11 +5,10 @@ namespace alsvanzelf\jsonapi\interfaces; interface PaginableInterface { - /** - * @param string $previousHref optional - * @param string $nextHref optional - * @param string $firstHref optional - * @param string $lastHref optional - */ - public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null); + public function setPaginationLinks( + ?string $previousHref=null, + ?string $nextHref=null, + ?string $firstHref=null, + ?string $lastHref=null, + ): void; } diff --git a/src/interfaces/ProfileInterface.php b/src/interfaces/ProfileInterface.php index 5f2f6543..c95c6257 100644 --- a/src/interfaces/ProfileInterface.php +++ b/src/interfaces/ProfileInterface.php @@ -9,8 +9,6 @@ interface ProfileInterface { * the unique link identifying and describing the profile * * @internal - * - * @return string */ - public function getOfficialLink(); + public function getOfficialLink(): string; } diff --git a/src/interfaces/RecursiveResourceContainerInterface.php b/src/interfaces/RecursiveResourceContainerInterface.php index c77041fb..a31689c7 100644 --- a/src/interfaces/RecursiveResourceContainerInterface.php +++ b/src/interfaces/RecursiveResourceContainerInterface.php @@ -20,5 +20,5 @@ interface RecursiveResourceContainerInterface { * * @return ResourceObject[] */ - public function getNestedContainedResourceObjects(); + public function getNestedContainedResourceObjects(): array; } diff --git a/src/interfaces/ResourceContainerInterface.php b/src/interfaces/ResourceContainerInterface.php index 6e136caf..3c38c364 100644 --- a/src/interfaces/ResourceContainerInterface.php +++ b/src/interfaces/ResourceContainerInterface.php @@ -19,7 +19,7 @@ interface ResourceContainerInterface { * * @internal * - * @return array with a mix of ResourceIdentifierObject and ResourceObject + * @return ResourceInterface[] */ - public function getContainedResources(); + public function getContainedResources(): array; } diff --git a/src/interfaces/ResourceInterface.php b/src/interfaces/ResourceInterface.php index 65f202c9..50de3a57 100644 --- a/src/interfaces/ResourceInterface.php +++ b/src/interfaces/ResourceInterface.php @@ -11,8 +11,7 @@ interface ResourceInterface { /** * @internal * - * @param boolean $identifierOnly optional, defaults to false - * @return ResourceIdentifierObject|ResourceObject + * @return ($identifierOnly is true ? ResourceIdentifierObject : ResourceObject) */ - public function getResource($identifierOnly=false); + public function getResource(bool $identifierOnly=false): ResourceIdentifierObject|ResourceObject; } diff --git a/src/objects/AttributesObject.php b/src/objects/AttributesObject.php index c90f9894..2a0c3b39 100644 --- a/src/objects/AttributesObject.php +++ b/src/objects/AttributesObject.php @@ -80,7 +80,7 @@ public function getKeys() { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->attributes !== []) { return false; } @@ -94,7 +94,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index d8e55511..a13c2124 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -186,11 +186,7 @@ public function blameHeader($headerName) { $this->addSource('header', $headerName); } - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value) { + public function addMeta(string $key, mixed $value): void { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); } @@ -262,7 +258,7 @@ public function setMetaObject(MetaObject $metaObject) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->id !== null) { return false; } @@ -297,7 +293,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { diff --git a/src/objects/JsonapiObject.php b/src/objects/JsonapiObject.php index a9ff8d9a..e8ebe383 100644 --- a/src/objects/JsonapiObject.php +++ b/src/objects/JsonapiObject.php @@ -32,11 +32,7 @@ public function __construct(?JsonapiVersionEnum $version=JsonapiVersionEnum::Lat * human api */ - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value) { + public function addMeta(string $key, mixed $value): void { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); } @@ -77,7 +73,7 @@ public function setMetaObject(MetaObject $metaObject) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->version !== null) { return false; } @@ -100,7 +96,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { diff --git a/src/objects/LinkObject.php b/src/objects/LinkObject.php index 9e6286e1..d0df1593 100644 --- a/src/objects/LinkObject.php +++ b/src/objects/LinkObject.php @@ -60,11 +60,7 @@ public function addLanguage($language) { } } - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value) { + public function addMeta(string $key, mixed $value): void { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); } @@ -133,7 +129,7 @@ public function setMetaObject(MetaObject $metaObject) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->href !== null) { return false; } @@ -165,7 +161,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { diff --git a/src/objects/LinksObject.php b/src/objects/LinksObject.php index aa178af8..eb222401 100644 --- a/src/objects/LinksObject.php +++ b/src/objects/LinksObject.php @@ -96,7 +96,7 @@ public function addLinkObject($key, LinkObject $linkObject) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->links !== []) { return false; } @@ -110,7 +110,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { diff --git a/src/objects/MetaObject.php b/src/objects/MetaObject.php index bee46b28..2ed72d19 100644 --- a/src/objects/MetaObject.php +++ b/src/objects/MetaObject.php @@ -62,7 +62,7 @@ public function add($key, $value) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->meta !== []) { return false; } @@ -76,7 +76,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { diff --git a/src/objects/RelationshipObject.php b/src/objects/RelationshipObject.php index af2202e4..ddd05fa6 100644 --- a/src/objects/RelationshipObject.php +++ b/src/objects/RelationshipObject.php @@ -133,7 +133,12 @@ public function setRelatedLink($href, array $meta=[]) { /** * @throws InputException if used on a to-one relationship */ - public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null) { + public function setPaginationLinks( + ?string $previousHref=null, + ?string $nextHref=null, + ?string $firstHref=null, + ?string $lastHref=null, + ): void { if ($this->type === RelationshipTypeEnum::ToOne) { throw new InputException('can not add pagination links to a to-one relationship'); } @@ -152,11 +157,7 @@ public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHre } } - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value) { + public function addMeta(string $key, mixed $value): void { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); } @@ -241,7 +242,7 @@ public function hasResource(ResourceInterface $otherResource) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->type === RelationshipTypeEnum::ToOne && $this->resource !== null) { return false; } @@ -264,7 +265,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { @@ -300,7 +301,7 @@ public function toArray() { * RecursiveResourceContainerInterface */ - public function getNestedContainedResourceObjects() { + public function getNestedContainedResourceObjects(): array { if ($this->isEmpty()) { return []; } diff --git a/src/objects/RelationshipsObject.php b/src/objects/RelationshipsObject.php index a9a684e7..a1aae78d 100644 --- a/src/objects/RelationshipsObject.php +++ b/src/objects/RelationshipsObject.php @@ -72,7 +72,7 @@ public function getKeys() { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->relationships !== []) { return false; } @@ -86,7 +86,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { @@ -107,7 +107,7 @@ public function toArray() { * RecursiveResourceContainerInterface */ - public function getNestedContainedResourceObjects() { + public function getNestedContainedResourceObjects(): array { $resourceObjects = []; foreach ($this->relationships as $relationship) { diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index d9d12e7c..e9760daa 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -53,11 +53,7 @@ public function __construct($type=null, $id=null) { * human api */ - /** - * @param string $key - * @param mixed $value - */ - public function addMeta($key, $value) { + public function addMeta(string $key, mixed $value): void { if ($this->meta === null) { $this->setMetaObject(new MetaObject()); } @@ -179,7 +175,7 @@ public function getIdentificationKey() { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if ($this->type !== null || $this->primaryId() !== null) { return false; } @@ -196,7 +192,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = []; $array['type'] = $this->type; @@ -226,7 +222,7 @@ public function toArray() { * ResourceInterface */ - public function getResource($identifierOnly=false) { + public function getResource(bool $identifierOnly=false): ResourceIdentifierObject { return $this; } diff --git a/src/objects/ResourceObject.php b/src/objects/ResourceObject.php index eff9d304..72c1aaa2 100644 --- a/src/objects/ResourceObject.php +++ b/src/objects/ResourceObject.php @@ -200,15 +200,15 @@ public function hasIdentifierPropertiesOnly() { * HasAttributesInterface */ - public function addAttribute($key, $value, array $options=[]) { - return $this->add($key, $value); + public function addAttribute(string $key, mixed $value, array $options=[]): void { + $this->add($key, $value); } /** * ResourceInterface */ - public function getResource($identifierOnly=false) { + public function getResource(bool $identifierOnly=false): ResourceIdentifierObject|ResourceObject { if ($identifierOnly) { return ResourceIdentifierObject::fromResourceObject($this); } @@ -220,7 +220,7 @@ public function getResource($identifierOnly=false) { * ObjectInterface */ - public function isEmpty() { + public function isEmpty(): bool { if (parent::isEmpty() === false) { return false; } @@ -237,7 +237,7 @@ public function isEmpty() { return true; } - public function toArray() { + public function toArray(): array { $array = parent::toArray(); if ($this->attributes !== null && $this->attributes->isEmpty() === false) { @@ -257,7 +257,7 @@ public function toArray() { * RecursiveResourceContainerInterface */ - public function getNestedContainedResourceObjects() { + public function getNestedContainedResourceObjects(): array { if ($this->relationships === null) { return []; } diff --git a/src/profiles/CursorPaginationProfile.php b/src/profiles/CursorPaginationProfile.php index 5140cb76..63aea9d1 100644 --- a/src/profiles/CursorPaginationProfile.php +++ b/src/profiles/CursorPaginationProfile.php @@ -402,7 +402,7 @@ private function setQueryParameter($url, $key, $value) { * ProfileInterface */ - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://jsonapi.org/profiles/ethanresnick/cursor-pagination/'; } } diff --git a/tests/example_output/ExampleEverywhereExtension.php b/tests/example_output/ExampleEverywhereExtension.php index 17a05b1d..6e37c814 100644 --- a/tests/example_output/ExampleEverywhereExtension.php +++ b/tests/example_output/ExampleEverywhereExtension.php @@ -7,11 +7,11 @@ use alsvanzelf\jsonapi\interfaces\ExtensionInterface; class ExampleEverywhereExtension implements ExtensionInterface { - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://example.org/everywhere-extension'; } - public function getNamespace() { + public function getNamespace(): string { return 'everywhere'; } } diff --git a/tests/example_output/ExampleTimestampsProfile.php b/tests/example_output/ExampleTimestampsProfile.php index f8174acb..210af099 100644 --- a/tests/example_output/ExampleTimestampsProfile.php +++ b/tests/example_output/ExampleTimestampsProfile.php @@ -10,7 +10,7 @@ use alsvanzelf\jsonapi\interfaces\ResourceInterface; class ExampleTimestampsProfile implements ProfileInterface { - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://jsonapi.org/recommendations/#authoring-profiles'; } diff --git a/tests/example_output/ExampleVersionExtension.php b/tests/example_output/ExampleVersionExtension.php index 40987c77..dea5cde6 100644 --- a/tests/example_output/ExampleVersionExtension.php +++ b/tests/example_output/ExampleVersionExtension.php @@ -11,11 +11,11 @@ use alsvanzelf\jsonapi\interfaces\ResourceInterface; class ExampleVersionExtension implements ExtensionInterface { - public function getOfficialLink() { + public function getOfficialLink(): string { return 'https://jsonapi.org/format/1.1/#extension-rules'; } - public function getNamespace() { + public function getNamespace(): string { return 'version'; } diff --git a/tests/extensions/TestExtension.php b/tests/extensions/TestExtension.php index 42c72b0b..3c7b814d 100644 --- a/tests/extensions/TestExtension.php +++ b/tests/extensions/TestExtension.php @@ -7,22 +7,22 @@ use alsvanzelf\jsonapi\interfaces\ExtensionInterface; class TestExtension implements ExtensionInterface { - private $namespace; - private $officialLink; + private string $namespace = ''; + private string $officialLink = ''; - public function setNamespace($namespace) { + public function setNamespace(string $namespace) { $this->namespace = $namespace; } - public function setOfficialLink($officialLink) { + public function setOfficialLink(string $officialLink) { $this->officialLink = $officialLink; } - public function getNamespace() { + public function getNamespace(): string { return $this->namespace; } - public function getOfficialLink() { + public function getOfficialLink(): string { return $this->officialLink; } } diff --git a/tests/objects/AttributesObjectTest.php b/tests/objects/AttributesObjectTest.php index 68f49da5..90e69c0f 100644 --- a/tests/objects/AttributesObjectTest.php +++ b/tests/objects/AttributesObjectTest.php @@ -34,6 +34,23 @@ public function testAdd_HappyPath() { $this->assertSame('bar', $array['foo']); } + public function testAdd_AllowsMixedValue() { + $attributesObject = new AttributesObject(); + $attributesObject->add('array-list', ['foo']); + $attributesObject->add('array-int-key', [42 => 'foo']); + $attributesObject->add('array-string-key', ['foo' => 'bar']); + $attributesObject->add('bool', true); + $attributesObject->add('int', 42); + $attributesObject->add('float', 4.2); + $attributesObject->add('null', null); + $attributesObject->add('object', new \stdClass); + $attributesObject->add('string', 'foo'); + + $array = $attributesObject->toArray(); + + $this->assertCount(9, $array); + } + public function testAdd_WithObject() { $object = new \stdClass(); $object->bar = 'baz'; diff --git a/tests/objects/MetaObjectTest.php b/tests/objects/MetaObjectTest.php index 186f97b9..15c762ab 100644 --- a/tests/objects/MetaObjectTest.php +++ b/tests/objects/MetaObjectTest.php @@ -9,6 +9,23 @@ use PHPUnit\Framework\TestCase; class MetaObjectTest extends TestCase { + public function testAdd_AllowsMixedValue() { + $metaObject = new MetaObject(); + $metaObject->add('array-list', ['foo']); + $metaObject->add('array-int-key', [42 => 'foo']); + $metaObject->add('array-string-key', ['foo' => 'bar']); + $metaObject->add('bool', true); + $metaObject->add('int', 42); + $metaObject->add('float', 4.2); + $metaObject->add('null', null); + $metaObject->add('object', new \stdClass); + $metaObject->add('string', 'foo'); + + $array = $metaObject->toArray(); + + $this->assertCount(9, $array); + } + public function testFromObject_HappyPath() { $object = new \stdClass(); $object->foo = 'bar'; diff --git a/tests/profiles/TestProfile.php b/tests/profiles/TestProfile.php index 0e7453b4..6b0b5d27 100644 --- a/tests/profiles/TestProfile.php +++ b/tests/profiles/TestProfile.php @@ -13,7 +13,7 @@ public function setOfficialLink($officialLink) { $this->officialLink = $officialLink; } - public function getOfficialLink() { + public function getOfficialLink(): string { return $this->officialLink; } } From 3e32d69c972c13d633ffd93fc4a6e9426e414026 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Mon, 22 Dec 2025 21:31:09 +0100 Subject: [PATCH 04/12] basic typing for documents --- src/CollectionDocument.php | 2 +- src/DataDocument.php | 2 +- src/Document.php | 6 +++--- src/ErrorsDocument.php | 2 +- src/MetaDocument.php | 2 +- src/ResourceDocument.php | 2 +- src/extensions/AtomicOperationsDocument.php | 2 +- src/interfaces/DocumentInterface.php | 13 +++++-------- 8 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/CollectionDocument.php b/src/CollectionDocument.php index 9cb2c1eb..57364841 100644 --- a/src/CollectionDocument.php +++ b/src/CollectionDocument.php @@ -119,7 +119,7 @@ public function addResource(ResourceInterface $resource, array $options=[]) { * DocumentInterface */ - public function toArray() { + public function toArray(): array { $array = parent::toArray(); $array['data'] = []; diff --git a/src/DataDocument.php b/src/DataDocument.php index 6d10d2e2..ed6f550b 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -60,7 +60,7 @@ public function addIncludedResourceObject(ResourceObject ...$resourceObjects) { * DocumentInterface */ - public function toArray() { + public function toArray(): array { $array = parent::toArray(); $array['data'] = null; diff --git a/src/Document.php b/src/Document.php index caf83c60..25f6eef8 100644 --- a/src/Document.php +++ b/src/Document.php @@ -241,7 +241,7 @@ public function applyProfile(ProfileInterface $profile) { * DocumentInterface */ - public function toArray() { + public function toArray(): array { $array = []; if ($this->hasAtMembers()) { @@ -264,7 +264,7 @@ public function toArray() { return $array; } - public function toJson(array $options=[]) { + public function toJson(array $options=[]): string { $options = array_merge(self::$defaults, $options); $array = $options['array'] ?? $this->toArray(); @@ -285,7 +285,7 @@ public function toJson(array $options=[]) { return $json; } - public function sendResponse(array $options=[]) { + public function sendResponse(array $options=[]): void { $options = array_merge(self::$defaults, $options); if ($this->httpStatusCode === 204) { diff --git a/src/ErrorsDocument.php b/src/ErrorsDocument.php index 48ef29e3..503df3cd 100644 --- a/src/ErrorsDocument.php +++ b/src/ErrorsDocument.php @@ -114,7 +114,7 @@ public function addErrorObject(ErrorObject $errorObject) { * DocumentInterface */ - public function toArray() { + public function toArray(): array { $array = parent::toArray(); $array['errors'] = []; diff --git a/src/MetaDocument.php b/src/MetaDocument.php index 54f99460..1eda9711 100644 --- a/src/MetaDocument.php +++ b/src/MetaDocument.php @@ -57,7 +57,7 @@ public function add($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Ro * DocumentInterface */ - public function toArray() { + public function toArray(): array { $array = parent::toArray(); // force meta to be set, and be an object when converting to json diff --git a/src/ResourceDocument.php b/src/ResourceDocument.php index 29f3845b..e3f569ed 100644 --- a/src/ResourceDocument.php +++ b/src/ResourceDocument.php @@ -287,7 +287,7 @@ public function setPrimaryResource(ResourceInterface $resource, array $options=[ * DocumentInterface */ - public function toArray() { + public function toArray(): array { $array = parent::toArray(); $array['data'] = null; diff --git a/src/extensions/AtomicOperationsDocument.php b/src/extensions/AtomicOperationsDocument.php index 0ece1442..73d4702e 100644 --- a/src/extensions/AtomicOperationsDocument.php +++ b/src/extensions/AtomicOperationsDocument.php @@ -41,7 +41,7 @@ public function addResults(ResourceInterface ...$resources) { * DocumentInterface */ - public function toArray() { + public function toArray(): array { $results = []; foreach ($this->results as $result) { $results[] = [ diff --git a/src/interfaces/DocumentInterface.php b/src/interfaces/DocumentInterface.php index b776845d..dada3058 100644 --- a/src/interfaces/DocumentInterface.php +++ b/src/interfaces/DocumentInterface.php @@ -9,27 +9,24 @@ interface DocumentInterface { /** * generate array with the contents of the document, used by {@see ->toJson()} - * - * @return array */ - public function toArray(); + public function toArray(): array; /** * generate json with the contents of the document, used by {@see ->sendResponse()} * - * @param array $options optional - * @return string json + * @param TypeAlias_InternalOptions $options * * @throws Exception if generating json fails */ - public function toJson(array $options=[]); + public function toJson(array $options=[]): string; /** * send jsonapi response to the browser * * @note will set http status code and content type, and echo json * - * @param array $options optional + * @param TypeAlias_InternalOptions $options */ - public function sendResponse(array $options=[]); + public function sendResponse(array $options=[]): void; } From 20d2784a1f5900daaf8948b2f18111a436eebf93 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Mon, 22 Dec 2025 22:25:19 +0100 Subject: [PATCH 05/12] add strict type to all helpers --- src/Document.php | 2 +- src/extensions/AtomicOperationsDocument.php | 7 +- src/helpers/AtMemberManager.php | 18 +-- src/helpers/Converter.php | 18 +-- src/helpers/HttpStatusCodeManager.php | 17 +-- src/helpers/LinksManager.php | 22 ++- src/helpers/RequestParser.php | 125 +++++------------- src/helpers/Validator.php | 34 ++--- src/interfaces/DocumentInterface.php | 2 + src/objects/ErrorObject.php | 4 +- src/objects/RelationshipObject.php | 4 +- src/objects/ResourceObject.php | 6 +- tests/helpers/HttpStatusCodeManagerTest.php | 1 - .../helpers/TestableNonTraitLinksManager.php | 2 +- 14 files changed, 97 insertions(+), 165 deletions(-) diff --git a/src/Document.php b/src/Document.php index 25f6eef8..b44a24f9 100644 --- a/src/Document.php +++ b/src/Document.php @@ -254,7 +254,7 @@ public function toArray(): array { if ($this->jsonapi !== null && $this->jsonapi->isEmpty() === false) { $array['jsonapi'] = $this->jsonapi->toArray(); } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { $array['links'] = $this->links->toArray(); } if ($this->meta !== null && $this->meta->isEmpty() === false) { diff --git a/src/extensions/AtomicOperationsDocument.php b/src/extensions/AtomicOperationsDocument.php index 73d4702e..99ad54ad 100644 --- a/src/extensions/AtomicOperationsDocument.php +++ b/src/extensions/AtomicOperationsDocument.php @@ -13,10 +13,9 @@ * document to send results of an atomic operations API */ class AtomicOperationsDocument extends Document { - /** @var AtomicOperationsExtension */ - private $extension; + private readonly AtomicOperationsExtension $extension; /** @var ResourceInterface[] */ - private $results = []; + private array $results = []; /** * start the document, auto applies the extension @@ -33,7 +32,7 @@ public function __construct() { * * @param ResourceInterface[] ...$resources */ - public function addResults(ResourceInterface ...$resources) { + public function addResults(ResourceInterface ...$resources): void { $this->results = array_merge($this->results, $resources); } diff --git a/src/helpers/AtMemberManager.php b/src/helpers/AtMemberManager.php index 87272b98..c81ce519 100644 --- a/src/helpers/AtMemberManager.php +++ b/src/helpers/AtMemberManager.php @@ -8,8 +8,8 @@ use alsvanzelf\jsonapi\helpers\Validator; trait AtMemberManager { - /** @var array */ - protected $atMembers = []; + /** @var array */ + protected array $atMembers = []; /** * human api @@ -19,11 +19,7 @@ trait AtMemberManager { * spec api */ - /** - * @param string $key - * @param mixed $value - */ - public function addAtMember($key, $value) { + public function addAtMember(string $key, mixed $value): void { if (str_starts_with($key, '@')) { $key = substr($key, 1); } @@ -43,19 +39,17 @@ public function addAtMember($key, $value) { /** * @internal - * - * @return boolean */ - public function hasAtMembers() { + public function hasAtMembers(): bool { return ($this->atMembers !== []); } /** * @internal * - * @return array + * @return array */ - public function getAtMembers() { + public function getAtMembers(): array { return $this->atMembers; } } diff --git a/src/helpers/Converter.php b/src/helpers/Converter.php index ef092036..b0c0edd3 100644 --- a/src/helpers/Converter.php +++ b/src/helpers/Converter.php @@ -13,11 +13,7 @@ * @internal */ class Converter { - /** - * @param object $object - * @return array - */ - public static function objectToArray($object) { + public static function objectToArray(object $object): array { if ($object instanceof ObjectInterface) { return $object->toArray(); } @@ -27,11 +23,8 @@ public static function objectToArray($object) { /** * @see https://stackoverflow.com/questions/7593969/regex-to-split-camelcase-or-titlecase-advanced/7599674#7599674 - * - * @param string $camelCase - * @return string */ - public static function camelCaseToWords($camelCase) { + public static function camelCaseToWords(string $camelCase): string { $parts = preg_split('/(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/', $camelCase); return implode(' ', $parts); @@ -40,11 +33,10 @@ public static function camelCaseToWords($camelCase) { /** * generates the value for a content type header, with extensions and profiles merged in if available * - * @param ExtensionInterface[] $extensions - * @param ProfileInterface[] $profiles - * @return string + * @param ExtensionInterface[] $extensions + * @param ProfileInterface[] $profiles */ - public static function prepareContentType(ContentTypeEnum $contentType, array $extensions, array $profiles) { + public static function prepareContentType(ContentTypeEnum $contentType, array $extensions, array $profiles): string { $contentType = $contentType->value; if ($extensions !== []) { diff --git a/src/helpers/HttpStatusCodeManager.php b/src/helpers/HttpStatusCodeManager.php index 857f7e77..67af3697 100644 --- a/src/helpers/HttpStatusCodeManager.php +++ b/src/helpers/HttpStatusCodeManager.php @@ -8,19 +8,16 @@ use alsvanzelf\jsonapi\helpers\Validator; trait HttpStatusCodeManager { - /** @var int */ - protected $httpStatusCode; + protected int $httpStatusCode; /** * spec api */ /** - * @param int $httpStatusCode - * * @throws InputException if an invalid code is used */ - public function setHttpStatusCode($httpStatusCode) { + public function setHttpStatusCode(int $httpStatusCode): void { if (Validator::checkHttpStatusCode($httpStatusCode) === false) { throw new InputException('can not use an invalid http status code'); } @@ -34,19 +31,15 @@ public function setHttpStatusCode($httpStatusCode) { /** * @internal - * - * @return boolean */ - public function hasHttpStatusCode() { - return ($this->httpStatusCode !== null); + public function hasHttpStatusCode(): bool { + return isset($this->httpStatusCode); } /** * @internal - * - * @return int */ - public function getHttpStatusCode() { + public function getHttpStatusCode(): int { return $this->httpStatusCode; } } diff --git a/src/helpers/LinksManager.php b/src/helpers/LinksManager.php index ec785452..feb0d5f6 100644 --- a/src/helpers/LinksManager.php +++ b/src/helpers/LinksManager.php @@ -8,8 +8,7 @@ use alsvanzelf\jsonapi\objects\LinksObject; trait LinksManager { - /** @var LinksObject */ - protected $links; + protected LinksObject $links; /** * human api @@ -53,8 +52,23 @@ public function setLinksObject(LinksObject $linksObject): void { /** * @internal */ - private function ensureLinksObject() { - if ($this->links === null) { + protected function hasLinks(): bool { + if (isset($this->links) === false) { + return false; + } + + if ($this->links->isEmpty()) { + return false; + } + + return true; + } + + /** + * @internal + */ + private function ensureLinksObject(): void { + if (isset($this->links) === false) { $this->setLinksObject(new LinksObject()); } } diff --git a/src/helpers/RequestParser.php b/src/helpers/RequestParser.php index 73f63bdf..e250dafa 100644 --- a/src/helpers/RequestParser.php +++ b/src/helpers/RequestParser.php @@ -10,8 +10,8 @@ use Psr\Http\Message\ServerRequestInterface; class RequestParser { - /** @var array */ - protected static $defaults = [ + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * reformat the include query parameter paths to nested arrays * this allows easier processing on each step of the chain @@ -26,20 +26,17 @@ class RequestParser { ]; /** - * @param string $selfLink the uri used to make this request {@see getSelfLink()} - * @param array $queryParameters all query parameters defined by the specification - * @param array $document the request jsonapi document + * @param string $selfLink the uri used to make this request {@see getSelfLink()} + * @param array> $queryParameters all query parameters defined by the specification + * @param array $document the request jsonapi document */ public function __construct( - private $selfLink='', - private array $queryParameters=[], - private array $document=[], + private readonly string $selfLink='', + private readonly array $queryParameters=[], + private readonly array $document=[], ) {} - /** - * @return self - */ - public static function fromSuperglobals() { + public static function fromSuperglobals(): self { $selfLink = ''; if (isset($_SERVER['REQUEST_SCHEME']) && isset($_SERVER['HTTP_HOST']) && isset($_SERVER['REQUEST_URI'])) { $selfLink = $_SERVER['REQUEST_SCHEME'].'://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; @@ -64,11 +61,7 @@ public static function fromSuperglobals() { return new self($selfLink, $queryParameters, $document); } - /** - * @param ServerRequestInterface|RequestInterface $request - * @return self - */ - public static function fromPsrRequest(RequestInterface $request) { + public static function fromPsrRequest(ServerRequestInterface|RequestInterface $request): self { $selfLink = (string) $request->getUri(); if ($request instanceof ServerRequestInterface) { @@ -97,17 +90,12 @@ public static function fromPsrRequest(RequestInterface $request) { * the full link used to make this request * * this is not a bare self link of a resource and includes query parameters if used - * - * @return string */ - public function getSelfLink() { + public function getSelfLink(): string { return $this->selfLink; } - /** - * @return boolean - */ - public function hasIncludePaths() { + public function hasIncludePaths(): bool { return isset($this->queryParameters['include']); } @@ -117,10 +105,10 @@ public function hasIncludePaths() { * the nested format allows easier processing on each step of the chain * the raw format allows for custom processing * - * @param array $options optional {@see RequestParser::$defaults} + * @param TypeAlias_InternalOptions $options {@see RequestParser::$defaults} * @return string[]|array */ - public function getIncludePaths(array $options=[]) { + public function getIncludePaths(array $options=[]): array { if ($this->queryParameters['include'] === '') { return []; } @@ -148,19 +136,14 @@ public function getIncludePaths(array $options=[]) { return $restructured; } - /** - * @param string $type - * @return boolean - */ - public function hasSparseFieldset($type) { + public function hasSparseFieldset(string $type): bool { return isset($this->queryParameters['fields'][$type]); } /** - * @param string $type * @return string[] */ - public function getSparseFieldset($type) { + public function getSparseFieldset(string $type): array { if ($this->queryParameters['fields'][$type] === '') { return []; } @@ -168,10 +151,7 @@ public function getSparseFieldset($type) { return explode(',', (string) $this->queryParameters['fields'][$type]); } - /** - * @return boolean - */ - public function hasSortFields() { + public function hasSortFields(): bool { return isset($this->queryParameters['sort']); } @@ -183,13 +163,13 @@ public function hasSortFields() { * * @todo return some kind of SortFieldObject * - * @param array $options optional {@see RequestParser::$defaults} + * @param TypeAlias_InternalOptions $options {@see RequestParser::$defaults} * @return string[]|array */ - public function getSortFields(array $options=[]) { + public function getSortFields(array $options=[]): array { if ($this->queryParameters['sort'] === '') { return []; } @@ -219,10 +199,7 @@ public function getSortFields(array $options=[]) { return $sort; } - /** - * @return boolean - */ - public function hasPagination() { + public function hasPagination(): bool { return isset($this->queryParameters['page']); } @@ -230,45 +207,32 @@ public function hasPagination() { * @todo return some kind of PaginatorObject which recognizes the strategy of pagination used * e.g. page-based, offset-based, cursor-based, or unknown * - * @return array + * @return array */ - public function getPagination() { + public function getPagination(): array { return $this->queryParameters['page']; } - /** - * @return boolean - */ - public function hasFilter() { + public function hasFilter(): bool { return isset($this->queryParameters['filter']); } /** - * @return array + * @return string|array */ - public function getFilter() { + public function getFilter(): string|array { return $this->queryParameters['filter']; } - /** - * @return boolean - */ - public function hasLocalId() { + public function hasLocalId(): bool { return (isset($this->document['data']['lid'])); } - /** - * @return string - */ - public function getLocalId() { + public function getLocalId(): string { return $this->document['data']['lid']; } - /** - * @param string $attributeName - * @return boolean - */ - public function hasAttribute($attributeName) { + public function hasAttribute(string $attributeName): bool { if (isset($this->document['data']['attributes']) === false) { return false; } @@ -279,19 +243,11 @@ public function hasAttribute($attributeName) { return true; } - /** - * @param string $attributeName - * @return mixed - */ - public function getAttribute($attributeName) { + public function getAttribute(string $attributeName): mixed { return $this->document['data']['attributes'][$attributeName]; } - /** - * @param string $relationshipName - * @return boolean - */ - public function hasRelationship($relationshipName) { + public function hasRelationship(string $relationshipName): bool { if (isset($this->document['data']['relationships']) === false) { return false; } @@ -305,18 +261,13 @@ public function hasRelationship($relationshipName) { /** * @todo return some kind of read-only ResourceIdentifierObject * - * @param string $relationshipName - * @return array + * @return ?array */ - public function getRelationship($relationshipName) { + public function getRelationship(string $relationshipName): ?array { return $this->document['data']['relationships'][$relationshipName]; } - /** - * @param string $metaKey - * @return boolean - */ - public function hasMeta($metaKey) { + public function hasMeta(string $metaKey): bool { if (isset($this->document['meta']) === false) { return false; } @@ -327,18 +278,14 @@ public function hasMeta($metaKey) { return true; } - /** - * @param string $metaKey - * @return mixed - */ - public function getMeta($metaKey) { + public function getMeta(string $metaKey): mixed { return $this->document['meta'][$metaKey]; } /** - * @return array + * @return array */ - public function getDocument() { + public function getDocument(): array { return $this->document; } } diff --git a/src/helpers/Validator.php b/src/helpers/Validator.php index a31d1d5c..780d2701 100644 --- a/src/helpers/Validator.php +++ b/src/helpers/Validator.php @@ -13,12 +13,12 @@ * @internal */ class Validator { - /** @var array */ - protected $usedFields = []; - /** @var array */ - protected $usedResourceIdentifiers = []; - /** @var array */ - protected static $defaults = [ + /** @var array */ + protected array $usedFields = []; + /** @var array */ + protected array $usedResourceIdentifiers = []; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * blocks 'type' as a keyword inside attributes or relationships * the specification doesn't allow this as 'type' is already set at the root of a resource @@ -32,12 +32,12 @@ class Validator { * * @see https://jsonapi.org/format/1.1/#document-resource-object-fields * - * @param string[] $fieldNames - * @param array $options optional {@see Validator::$defaults} + * @param string[] $fieldNames + * @param TypeAlias_InternalOptions $options {@see Validator::$defaults} * * @throws DuplicateException */ - public function claimUsedFields(array $fieldNames, ObjectContainerEnum $objectContainer, array $options=[]) { + public function claimUsedFields(array $fieldNames, ObjectContainerEnum $objectContainer, array $options=[]): void { $options = array_merge(self::$defaults, $options); foreach ($fieldNames as $fieldName) { @@ -60,7 +60,7 @@ public function claimUsedFields(array $fieldNames, ObjectContainerEnum $objectCo } } - public function clearUsedFields(ObjectContainerEnum $objectContainerToClear) { + public function clearUsedFields(ObjectContainerEnum $objectContainerToClear): void { foreach ($this->usedFields as $fieldName => $containerFound) { if ($containerFound !== $objectContainerToClear) { continue; @@ -71,12 +71,10 @@ public function clearUsedFields(ObjectContainerEnum $objectContainerToClear) { } /** - * @param ResourceInterface $resource - * * @throws InputException if no type or id has been set on the resource * @throws DuplicateException if the combination of type and id has been set before */ - public function claimUsedResourceIdentifier(ResourceInterface $resource) { + public function claimUsedResourceIdentifier(ResourceInterface $resource): void { if ($resource->getResource()->hasIdentification() === false) { throw new InputException('can not validate resource without identifier, set type and id/lid first'); } @@ -95,11 +93,9 @@ public function claimUsedResourceIdentifier(ResourceInterface $resource) { * * @todo allow non-url safe chars * - * @param string $memberName - * * @throws InputException */ - public static function checkMemberName($memberName) { + public static function checkMemberName(string $memberName): void { $globallyAllowedCharacters = 'a-zA-Z0-9'; $generallyAllowedCharacters = $globallyAllowedCharacters.'_-'; @@ -122,11 +118,7 @@ public static function checkMemberName($memberName) { throw new InputException('invalid member name "'.$memberName.'"'); } - /** - * @param string|int $httpStatusCode - * @return boolean - */ - public static function checkHttpStatusCode($httpStatusCode) { + public static function checkHttpStatusCode(string|int $httpStatusCode): bool { $httpStatusCode = (int) $httpStatusCode; if ($httpStatusCode < 100) { diff --git a/src/interfaces/DocumentInterface.php b/src/interfaces/DocumentInterface.php index dada3058..db3f987f 100644 --- a/src/interfaces/DocumentInterface.php +++ b/src/interfaces/DocumentInterface.php @@ -9,6 +9,8 @@ interface DocumentInterface { /** * generate array with the contents of the document, used by {@see ->toJson()} + * + * @return array */ public function toArray(): array; diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index a13c2124..ab7a5ce7 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -274,7 +274,7 @@ public function isEmpty(): bool { if ($this->detail !== null) { return false; } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { return false; } if ($this->source !== []) { @@ -317,7 +317,7 @@ public function toArray(): array { if ($this->detail !== null) { $array['detail'] = $this->detail; } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { $array['links'] = $this->links->toArray(); } if ($this->source !== []) { diff --git a/src/objects/RelationshipObject.php b/src/objects/RelationshipObject.php index ddd05fa6..bb83ff6c 100644 --- a/src/objects/RelationshipObject.php +++ b/src/objects/RelationshipObject.php @@ -249,7 +249,7 @@ public function isEmpty(): bool { if ($this->type === RelationshipTypeEnum::ToMany && $this->resources !== []) { return false; } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { return false; } if ($this->meta !== null && $this->meta->isEmpty() === false) { @@ -275,7 +275,7 @@ public function toArray(): array { $array = array_merge($array, $this->getExtensionMembers()); } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { $array['links'] = $this->links->toArray(); } if ($this->type === RelationshipTypeEnum::ToOne) { diff --git a/src/objects/ResourceObject.php b/src/objects/ResourceObject.php index 72c1aaa2..f387414c 100644 --- a/src/objects/ResourceObject.php +++ b/src/objects/ResourceObject.php @@ -189,7 +189,7 @@ public function hasIdentifierPropertiesOnly() { if ($this->relationships !== null && $this->relationships->isEmpty() === false) { return false; } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { return false; } @@ -230,7 +230,7 @@ public function isEmpty(): bool { if ($this->relationships !== null && $this->relationships->isEmpty() === false) { return false; } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { return false; } @@ -246,7 +246,7 @@ public function toArray(): array { if ($this->relationships !== null && $this->relationships->isEmpty() === false) { $array['relationships'] = $this->relationships->toArray(); } - if ($this->links !== null && $this->links->isEmpty() === false) { + if ($this->hasLinks()) { $array['links'] = $this->links->toArray(); } diff --git a/tests/helpers/HttpStatusCodeManagerTest.php b/tests/helpers/HttpStatusCodeManagerTest.php index 60ffc3dd..ea125c9e 100644 --- a/tests/helpers/HttpStatusCodeManagerTest.php +++ b/tests/helpers/HttpStatusCodeManagerTest.php @@ -13,7 +13,6 @@ public function testSetHttpStatusCode_HappyPath() { $helper = new HttpStatusCodeManager(); $this->assertFalse($helper->hasHttpStatusCode()); - $this->assertNull($helper->getHttpStatusCode()); $helper->setHttpStatusCode(204); diff --git a/tests/helpers/TestableNonTraitLinksManager.php b/tests/helpers/TestableNonTraitLinksManager.php index 3453fb92..e9e41457 100644 --- a/tests/helpers/TestableNonTraitLinksManager.php +++ b/tests/helpers/TestableNonTraitLinksManager.php @@ -13,7 +13,7 @@ class TestableNonTraitLinksManager { use LinksManager; public function toArray() { - if ($this->links === null) { + if ($this->hasLinks() === false) { return []; } From 5765b64eabd4947801309fdf35a7e856bc9a970b Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Mon, 22 Dec 2025 22:44:56 +0100 Subject: [PATCH 06/12] add strict type to profile --- src/profiles/CursorPaginationProfile.php | 170 +++++++---------------- 1 file changed, 50 insertions(+), 120 deletions(-) diff --git a/src/profiles/CursorPaginationProfile.php b/src/profiles/CursorPaginationProfile.php index 63aea9d1..aabf16dd 100644 --- a/src/profiles/CursorPaginationProfile.php +++ b/src/profiles/CursorPaginationProfile.php @@ -56,12 +56,14 @@ class CursorPaginationProfile implements ProfileInterface { /** * set links to paginate the data using cursors of the paginated data * - * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject - * @param string $baseOrCurrentUrl - * @param string $firstCursor - * @param string $lastCursor + * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject */ - public function setLinks(PaginableInterface $paginable, $baseOrCurrentUrl, $firstCursor, $lastCursor) { + public function setLinks( + PaginableInterface $paginable, + string $baseOrCurrentUrl, + string $firstCursor, + string $lastCursor, + ): void { $previousLinkObject = new LinkObject($this->generatePreviousLink($baseOrCurrentUrl, $firstCursor)); $nextLinkObject = new LinkObject($this->generateNextLink($baseOrCurrentUrl, $lastCursor)); @@ -69,41 +71,32 @@ public function setLinks(PaginableInterface $paginable, $baseOrCurrentUrl, $firs } /** - * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject - * @param string $baseOrCurrentUrl - * @param string $lastCursor + * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject */ - public function setLinksFirstPage(PaginableInterface $paginable, $baseOrCurrentUrl, $lastCursor) { + public function setLinksFirstPage(PaginableInterface $paginable, string $baseOrCurrentUrl, string $lastCursor): void { $this->setPaginationLinkObjectsWithoutPrevious($paginable, $baseOrCurrentUrl, $lastCursor); } /** - * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject - * @param string $baseOrCurrentUrl - * @param string $firstCursor + * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject */ - public function setLinksLastPage(PaginableInterface $paginable, $baseOrCurrentUrl, $firstCursor) { + public function setLinksLastPage(PaginableInterface $paginable, string $baseOrCurrentUrl, string $firstCursor): void { $this->setPaginationLinkObjectsWithoutNext($paginable, $baseOrCurrentUrl, $firstCursor); } /** * set the cursor of a specific resource to allow pagination after or before this resource - * - * @param ResourceInterface $resource - * @param string $cursor */ - public function setCursor(ResourceInterface $resource, $cursor) { + public function setCursor(ResourceInterface $resource, string $cursor): void { $this->setItemMeta($resource, $cursor); } /** * set count(s) to tell about the (estimated) total size * - * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject - * @param int $exactTotal optional - * @param int $bestGuessTotal optional + * @param PaginableInterface $paginable a CollectionDocument or RelationshipObject */ - public function setCount(PaginableInterface $paginable, $exactTotal=null, $bestGuessTotal=null) { + public function setCount(PaginableInterface $paginable, ?int $exactTotal=null, ?int $bestGuessTotal=null) { $this->setPaginationMeta($paginable, $exactTotal, $bestGuessTotal); } @@ -113,21 +106,13 @@ public function setCount(PaginableInterface $paginable, $exactTotal=null, $bestG /** * helper to get generate a correct page[before] link, use to apply manually - * - * @param string $baseOrCurrentUrl - * @param string $beforeCursor - * @return string */ - public function generatePreviousLink($baseOrCurrentUrl, $beforeCursor) { + public function generatePreviousLink(string $baseOrCurrentUrl, string $beforeCursor): string { return $this->setQueryParameter($baseOrCurrentUrl, 'page[before]', $beforeCursor); } /** * helper to get generate a correct page[after] link, use to apply manually - * - * @param string $baseOrCurrentUrl - * @param string $afterCursor - * @return string */ public function generateNextLink($baseOrCurrentUrl, $afterCursor) { return $this->setQueryParameter($baseOrCurrentUrl, 'page[after]', $afterCursor); @@ -142,42 +127,25 @@ public function generateNextLink($baseOrCurrentUrl, $afterCursor) { * - /data/0/relationships/foo/links/prev & /data/0/relationships/foo/links/next * * @see https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#terms-pagination-links - * - * @param PaginableInterface&HasLinksInterface $paginable - * @param LinkObject $previousLinkObject - * @param LinkObject $nextLinkObject */ - public function setPaginationLinkObjects(PaginableInterface $paginable, LinkObject $previousLinkObject, LinkObject $nextLinkObject) { - if ($paginable instanceof HasLinksInterface === false) { - throw new InputException('unsupported paginable to set pagination links on'); - } - + public function setPaginationLinkObjects( + PaginableInterface & HasLinksInterface $paginable, + LinkObject $previousLinkObject, + LinkObject $nextLinkObject, + ): void { $paginable->addLinkObject('prev', $previousLinkObject); $paginable->addLinkObject('next', $nextLinkObject); } - /** - * @param PaginableInterface $paginable - * @param string $baseOrCurrentUrl - * @param string $firstCursor - */ - public function setPaginationLinkObjectsWithoutNext(PaginableInterface $paginable, $baseOrCurrentUrl, $firstCursor) { + public function setPaginationLinkObjectsWithoutNext(PaginableInterface $paginable, string $baseOrCurrentUrl, string $firstCursor): void { $this->setPaginationLinkObjects($paginable, new LinkObject($this->generatePreviousLink($baseOrCurrentUrl, $firstCursor)), new LinkObject()); } - /** - * @param PaginableInterface $paginable - * @param string $baseOrCurrentUrl - * @param string $lastCursor - */ - public function setPaginationLinkObjectsWithoutPrevious(PaginableInterface $paginable, $baseOrCurrentUrl, $lastCursor) { + public function setPaginationLinkObjectsWithoutPrevious(PaginableInterface $paginable, string $baseOrCurrentUrl, string $lastCursor): void { $this->setPaginationLinkObjects($paginable, new LinkObject(), new LinkObject($this->generateNextLink($baseOrCurrentUrl, $lastCursor))); } - /** - * @param PaginableInterface $paginable - */ - public function setPaginationLinkObjectsExplicitlyEmpty(PaginableInterface $paginable) { + public function setPaginationLinkObjectsExplicitlyEmpty(PaginableInterface $paginable): void { $this->setPaginationLinkObjects($paginable, new LinkObject(), new LinkObject()); } @@ -190,15 +158,8 @@ public function setPaginationLinkObjectsExplicitlyEmpty(PaginableInterface $pagi * - /data/0/relationships/foo/meta/page * * @see https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#terms-pagination-item-metadata - * - * @param ResourceInterface&HasMetaInterface $resource - * @param string $cursor */ - public function setItemMeta(ResourceInterface $resource, $cursor) { - if ($resource instanceof HasMetaInterface === false) { - throw new InputException('resource doesn\'t support meta'); - } - + public function setItemMeta(ResourceInterface & HasMetaInterface $resource, string $cursor): void { $metadata = [ 'cursor' => $cursor, ]; @@ -221,16 +182,14 @@ public function setItemMeta(ResourceInterface $resource, $cursor) { * * @see https://jsonapi.org/profiles/ethanresnick/cursor-pagination/#terms-pagination-metadata * - * @param PaginableInterface&HasMetaInterface $paginable - * @param int $exactTotal optional - * @param int $bestGuessTotal optional - * @param boolean $rangeIsTruncated optional, if both after and before are supplied but the items exceed requested or max size + * @param boolean $rangeIsTruncated, if both after and before are supplied but the items exceed requested or max size */ - public function setPaginationMeta(PaginableInterface $paginable, $exactTotal=null, $bestGuessTotal=null, $rangeIsTruncated=null) { - if ($paginable instanceof HasMetaInterface === false) { - throw new InputException('paginable doesn\'t support meta'); - } - + public function setPaginationMeta( + PaginableInterface & HasMetaInterface $paginable, + ?int $exactTotal=null, + ?int $bestGuessTotal=null, + ?bool $rangeIsTruncated=null, + ): void { $metadata = []; if ($exactTotal !== null) { @@ -252,18 +211,9 @@ public function setPaginationMeta(PaginableInterface $paginable, $exactTotal=nul * get an ErrorObject for when the requested sorting cannot efficiently be paginated * * ends up at: - * - /errors/0/code - * - /errors/0/status - * - /errors/0/source/parameter - * - /errors/0/links/type/0 - * - /errors/0/title optional - * - /errors/0/detail optional - * - * @param string $genericTitle optional - * @param string $specificDetails optional - * @return ErrorObject + * - /errors/0/* */ - public function getUnsupportedSortErrorObject($genericTitle=null, $specificDetails=null) { + public function getUnsupportedSortErrorObject(?string $genericTitle=null, ?string $specificDetails=null): ErrorObject { $errorObject = new ErrorObject('Unsupported sort'); $errorObject->setTypeLink('https://jsonapi.org/profiles/ethanresnick/cursor-pagination/unsupported-sort'); $errorObject->blameQueryParameter('sort'); @@ -280,20 +230,12 @@ public function getUnsupportedSortErrorObject($genericTitle=null, $specificDetai * get an ErrorObject for when the requested page size exceeds the server-defined max page size * * ends up at: - * - /errors/0/code - * - /errors/0/status - * - /errors/0/source/parameter - * - /errors/0/links/type/0 - * - /errors/0/meta/page/maxSize - * - /errors/0/title optional - * - /errors/0/detail optional + * - /errors/0/* * - * @param int $maxSize - * @param string $genericTitle optional, e.g. 'Page size requested is too large.' - * @param string $specificDetails optional, e.g. 'You requested a size of 200, but 100 is the maximum.' - * @return ErrorObject + * @param string $genericTitle e.g. 'Page size requested is too large.' + * @param string $specificDetails e.g. 'You requested a size of 200, but 100 is the maximum.' */ - public function getMaxPageSizeExceededErrorObject($maxSize, $genericTitle=null, $specificDetails=null) { + public function getMaxPageSizeExceededErrorObject(int $maxSize, ?string $genericTitle=null, ?string $specificDetails=null): ErrorObject { $errorObject = new ErrorObject('Max page size exceeded'); $errorObject->setTypeLink('https://jsonapi.org/profiles/ethanresnick/cursor-pagination/max-size-exceeded'); $errorObject->blameQueryParameter('page[size]'); @@ -311,20 +253,18 @@ public function getMaxPageSizeExceededErrorObject($maxSize, $genericTitle=null, * get an ErrorObject for when the requested page size is not a positive integer, or when the requested page after/before is not a valid cursor * * ends up at: - * - /errors/0/code - * - /errors/0/status - * - /errors/0/source/parameter - * - /errors/0/links/type/0 optional - * - /errors/0/title optional - * - /errors/0/detail optional + * - /errors/0/* * - * @param int $queryParameter e.g. 'sort' or 'page[size]' - * @param string $typeLink optional - * @param string $genericTitle optional, e.g. 'Invalid Parameter.' - * @param string $specificDetails optional, e.g. 'page[size] must be a positive integer; got 0' - * @return ErrorObject + * @param string $queryParameter e.g. 'sort' or 'page[size]' + * @param string $genericTitle e.g. 'Invalid Parameter.' + * @param string $specificDetails e.g. 'page[size] must be a positive integer; got 0' */ - public function getInvalidParameterValueErrorObject($queryParameter, $typeLink=null, $genericTitle=null, $specificDetails=null) { + public function getInvalidParameterValueErrorObject( + string $queryParameter, + ?string $typeLink=null, + ?string $genericTitle=null, + ?string $specificDetails=null, + ): ErrorObject { $errorObject = new ErrorObject('Invalid parameter value'); $errorObject->blameQueryParameter($queryParameter); $errorObject->setHttpStatusCode(400); @@ -344,15 +284,9 @@ public function getInvalidParameterValueErrorObject($queryParameter, $typeLink=n * get an ErrorObject for when range pagination requests (when both 'page[after]' and 'page[before]' are requested) are not supported * * ends up at: - * - /errors/0/code - * - /errors/0/status - * - /errors/0/links/type/0 - * - * @param string $genericTitle optional - * @param string $specificDetails optional - * @return ErrorObject + * - /errors/0/* */ - public function getRangePaginationNotSupportedErrorObject($genericTitle=null, $specificDetails=null) { + public function getRangePaginationNotSupportedErrorObject(?string $genericTitle=null, ?string $specificDetails=null): ErrorObject { $errorObject = new ErrorObject('Range pagination not supported'); $errorObject->setTypeLink('https://jsonapi.org/profiles/ethanresnick/cursor-pagination/range-pagination-not-supported'); $errorObject->setHttpStatusCode(400); @@ -370,12 +304,8 @@ public function getRangePaginationNotSupportedErrorObject($genericTitle=null, $s /** * add or adjust a key in the query string of a url - * - * @param string $url - * @param string $key - * @param string $value */ - private function setQueryParameter($url, $key, $value) { + private function setQueryParameter(string $url, string $key, string $value): string { $originalQuery = parse_url($url, PHP_URL_QUERY); $decodedQuery = urldecode($originalQuery); $originalIsEncoded = ($decodedQuery !== $originalQuery); From a7b7396722ada09c3b567121c65429473492e80b Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 26 Dec 2025 08:35:04 +0100 Subject: [PATCH 07/12] add strict type to all objects --- src/objects/AttributesObject.php | 23 +-- src/objects/ErrorObject.php | 135 ++++++++---------- src/objects/JsonapiObject.php | 37 ++--- src/objects/LinkObject.php | 97 +++++-------- src/objects/LinksObject.php | 31 ++-- src/objects/MetaObject.php | 21 +-- src/objects/RelationshipObject.php | 82 +++++------ src/objects/RelationshipsObject.php | 26 ++-- src/objects/ResourceIdentifierObject.php | 105 +++++++------- src/objects/ResourceObject.php | 94 +++++------- tests/objects/RelationshipObjectTest.php | 10 -- .../objects/ResourceIdentifierObjectTest.php | 4 +- 12 files changed, 271 insertions(+), 394 deletions(-) diff --git a/src/objects/AttributesObject.php b/src/objects/AttributesObject.php index 2a0c3b39..fe10420c 100644 --- a/src/objects/AttributesObject.php +++ b/src/objects/AttributesObject.php @@ -9,8 +9,8 @@ use alsvanzelf\jsonapi\objects\AbstractObject; class AttributesObject extends AbstractObject { - /** @var array */ - protected $attributes = []; + /** @var array */ + protected array $attributes = []; /** * human api @@ -19,11 +19,8 @@ class AttributesObject extends AbstractObject { /** * @note if an `id` is set inside $attributes, it is removed from there * it is common to find it inside, and not doing so will cause an exception - * - * @param array $attributes - * @return AttributesObject */ - public static function fromArray(array $attributes) { + public static function fromArray(array $attributes): self { unset($attributes['id']); $attributesObject = new self(); @@ -35,11 +32,7 @@ public static function fromArray(array $attributes) { return $attributesObject; } - /** - * @param object $attributes - * @return AttributesObject - */ - public static function fromObject($attributes) { + public static function fromObject(object $attributes): self { $array = Converter::objectToArray($attributes); return self::fromArray($array); @@ -49,11 +42,7 @@ public static function fromObject($attributes) { * spec api */ - /** - * @param string $key - * @param mixed $value - */ - public function add($key, $value) { + public function add(string $key, mixed $value): void { Validator::checkMemberName($key); if (is_object($value)) { @@ -72,7 +61,7 @@ public function add($key, $value) { * * @return string[] */ - public function getKeys() { + public function getKeys(): array { return array_keys($this->attributes); } diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index ab7a5ce7..da7abc8b 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -16,20 +16,15 @@ class ErrorObject extends AbstractObject implements HasLinksInterface, HasMetaInterface { use HttpStatusCodeManager, LinksManager; - /** @var string */ - protected $id; - /** @var string */ - protected $code; - /** @var string */ - protected $title; - /** @var string */ - protected $detail; - /** @var array */ - protected $source = []; - /** @var MetaObject */ - protected $meta; - /** @var array */ - protected static $defaults = [ + protected string|int $id; + protected string $code; + protected string $title; + protected string $detail; + /** @var array{pointer?: string, parameter?: string, header?: string} */ + protected array $source = []; + protected MetaObject $meta; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * add the trace of exceptions when adding exceptions * in some cases it might be handy to disable if traces are too big @@ -44,13 +39,19 @@ class ErrorObject extends AbstractObject implements HasLinksInterface, HasMetaIn ]; /** - * @param string|int $genericCode developer-friendly code of the generic type of error - * @param string $genericTitle human-friendly title of the generic type of error - * @param string $specificDetails optional, human-friendly explanation of the specific error - * @param string $specificAboutLink optional, human-friendly explanation of the specific error - * @param string $genericTypeLink optional, human-friendly explanation of the generic type of error + * @param string|int|null $genericCode developer-friendly code of the generic type of error + * @param ?string $genericTitle human-friendly title of the generic type of error + * @param ?string $specificDetails human-friendly explanation of the specific error + * @param ?string $specificAboutLink human-friendly explanation of the specific error + * @param ?string $genericTypeLink human-friendly explanation of the generic type of error */ - public function __construct($genericCode=null, $genericTitle=null, $specificDetails=null, $specificAboutLink=null, $genericTypeLink=null) { + public function __construct( + string|int|null $genericCode=null, + ?string $genericTitle=null, + ?string $specificDetails=null, + ?string $specificAboutLink=null, + ?string $genericTypeLink=null, + ) { if ($genericCode !== null) { $this->setApplicationCode($genericCode); } @@ -64,11 +65,9 @@ public function __construct($genericCode=null, $genericTitle=null, $specificDeta */ /** - * @param \Throwable $exception - * @param array $options optional {@see ErrorObject::$defaults} - * @return ErrorObject + * @param TypeAlias_InternalOptions $options {@see ErrorObject::$defaults} */ - public static function fromException(\Throwable $exception, array $options=[]) { + public static function fromException(\Throwable $exception, array $options=[]): self { $options = array_merge(self::$defaults, $options); $errorObject = new self(); @@ -118,12 +117,17 @@ public static function fromException(\Throwable $exception, array $options=[]) { /** * explain this particular occurence of the error in a human-friendly way * - * @param string $genericTitle title of the generic type of error - * @param string $specificDetails optional, explanation of the specific error - * @param string $specificAboutLink optional, explanation of the specific error - * @param string $genericTypeLink optional, explanation of the generic type of error + * @param string $genericTitle title of the generic type of error + * @param ?string $specificDetails explanation of the specific error + * @param ?string $specificAboutLink explanation of the specific error + * @param ?string $genericTypeLink explanation of the generic type of error */ - public function setHumanExplanation($genericTitle, $specificDetails=null, $specificAboutLink=null, $genericTypeLink=null) { + public function setHumanExplanation( + string $genericTitle, + ?string $specificDetails=null, + ?string $specificAboutLink=null, + ?string $genericTypeLink=null, + ): void { $this->setHumanTitle($genericTitle); if ($specificDetails !== null) { @@ -140,54 +144,47 @@ public function setHumanExplanation($genericTitle, $specificDetails=null, $speci /** * set the link about this specific occurence of the error, explained in a human-friendly way * - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setAboutLink($href, array $meta=[]) { + public function setAboutLink(string $href, array $meta=[]): void { $this->addLink('about', $href, $meta); } /** * set the link of the generic type of this error, explained in a human-friendly way * - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setTypeLink($href, array $meta=[]) { + public function setTypeLink(string $href, array $meta=[]): void { $this->addLink('type', $href, $meta); } /** * blame the json pointer from the request body causing this error + * e.g. "/data/attributes/title" or "/data" * * @see https://tools.ietf.org/html/rfc6901 - * - * @param string $pointer e.g. "/data/attributes/title" or "/data" */ - public function blameJsonPointer($pointer) { + public function blameJsonPointer(string $pointer): void { $this->addSource('pointer', $pointer); } /** * blame the query parameter from the request causing this error - * - * @param string $parameter */ - public function blameQueryParameter($parameter) { + public function blameQueryParameter(string $parameter): void { $this->addSource('parameter', $parameter); } /** * blame the header from the request causing this error - * - * @param string $headerName */ - public function blameHeader($headerName) { + public function blameHeader(string $headerName): void { $this->addSource('header', $headerName); } public function addMeta(string $key, mixed $value): void { - if ($this->meta === null) { + if (isset($this->meta) === false) { $this->setMetaObject(new MetaObject()); } @@ -200,30 +197,27 @@ public function addMeta(string $key, mixed $value): void { /** * a unique identifier for this specific occurrence of the error - * - * @param string|int $id */ - public function setUniqueIdentifier($id) { + public function setUniqueIdentifier(string|int $id): void { $this->id = $id; } /** * a code expressing the generic type of this error * it should be application-specific and aimed at developers - * - * @param string|int $genericCode will be casted to a string + * ints will be casted to a string */ - public function setApplicationCode($genericCode) { + public function setApplicationCode(string|int $genericCode): void { $this->code = (string) $genericCode; } /** * add the source of the error * - * @param string $key {@see ->blameJsonPointer(), ->blameQueryParameter()} - * @param string $value + * @see ->blameJsonPointer() + * @see ->blameQueryParameter() */ - public function addSource($key, $value) { + public function addSource(string $key, string $value): void { Validator::checkMemberName($key); $this->source[$key] = $value; @@ -231,26 +225,19 @@ public function addSource($key, $value) { /** * a short human-friendly explanation of the generic type of this error - * - * @param string $genericTitle */ - public function setHumanTitle($genericTitle) { + public function setHumanTitle(string $genericTitle): void { $this->title = $genericTitle; } /** * a human-friendly explanation of this specific occurrence of the error - * - * @param string $specificDetails */ - public function setHumanDetails($specificDetails) { + public function setHumanDetails(string $specificDetails): void { $this->detail = $specificDetails; } - /** - * @param MetaObject $metaObject - */ - public function setMetaObject(MetaObject $metaObject) { + public function setMetaObject(MetaObject $metaObject): void { $this->meta = $metaObject; } @@ -259,19 +246,19 @@ public function setMetaObject(MetaObject $metaObject) { */ public function isEmpty(): bool { - if ($this->id !== null) { + if (isset($this->id)) { return false; } if ($this->hasHttpStatusCode()) { return false; } - if ($this->code !== null) { + if (isset($this->code)) { return false; } - if ($this->title !== null) { + if (isset($this->title)) { return false; } - if ($this->detail !== null) { + if (isset($this->detail)) { return false; } if ($this->hasLinks()) { @@ -280,7 +267,7 @@ public function isEmpty(): bool { if ($this->source !== []) { return false; } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { return false; } if ($this->hasAtMembers()) { @@ -302,19 +289,19 @@ public function toArray(): array { if ($this->hasExtensionMembers()) { $array = array_merge($array, $this->getExtensionMembers()); } - if ($this->id !== null) { + if (isset($this->id)) { $array['id'] = $this->id; } if ($this->hasHttpStatusCode()) { $array['status'] = (string) $this->getHttpStatusCode(); } - if ($this->code !== null) { + if (isset($this->code)) { $array['code'] = $this->code; } - if ($this->title !== null) { + if (isset($this->title)) { $array['title'] = $this->title; } - if ($this->detail !== null) { + if (isset($this->detail)) { $array['detail'] = $this->detail; } if ($this->hasLinks()) { @@ -323,7 +310,7 @@ public function toArray(): array { if ($this->source !== []) { $array['source'] = $this->source; } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); } diff --git a/src/objects/JsonapiObject.php b/src/objects/JsonapiObject.php index e8ebe383..f54d1aba 100644 --- a/src/objects/JsonapiObject.php +++ b/src/objects/JsonapiObject.php @@ -13,14 +13,12 @@ use alsvanzelf\jsonapi\objects\MetaObject; class JsonapiObject extends AbstractObject implements HasMetaInterface { - /** @var JsonapiVersionEnum */ - protected $version; + protected JsonapiVersionEnum $version; /** @var ExtensionInterface[] */ - protected $extensions = []; + protected array $extensions = []; /** @var ProfileInterface[] */ - protected $profiles = []; - /** @var MetaObject */ - protected $meta; + protected array $profiles = []; + protected MetaObject $meta; public function __construct(?JsonapiVersionEnum $version=JsonapiVersionEnum::Latest) { if ($version !== null) { @@ -33,7 +31,7 @@ public function __construct(?JsonapiVersionEnum $version=JsonapiVersionEnum::Lat */ public function addMeta(string $key, mixed $value): void { - if ($this->meta === null) { + if (isset($this->meta) === false) { $this->setMetaObject(new MetaObject()); } @@ -44,28 +42,19 @@ public function addMeta(string $key, mixed $value): void { * spec api */ - public function setVersion(JsonapiVersionEnum $version) { + public function setVersion(JsonapiVersionEnum $version): void { $this->version = $version; } - /** - * @param ExtensionInterface $extension - */ - public function addExtension(ExtensionInterface $extension) { + public function addExtension(ExtensionInterface $extension): void { $this->extensions[] = $extension; } - /** - * @param ProfileInterface $profile - */ - public function addProfile(ProfileInterface $profile) { + public function addProfile(ProfileInterface $profile): void { $this->profiles[] = $profile; } - /** - * @param MetaObject $metaObject - */ - public function setMetaObject(MetaObject $metaObject) { + public function setMetaObject(MetaObject $metaObject): void { $this->meta = $metaObject; } @@ -74,7 +63,7 @@ public function setMetaObject(MetaObject $metaObject) { */ public function isEmpty(): bool { - if ($this->version !== null) { + if (isset($this->version)) { return false; } if ($this->extensions !== []) { @@ -83,7 +72,7 @@ public function isEmpty(): bool { if ($this->profiles !== []) { return false; } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { return false; } if ($this->hasAtMembers()) { @@ -105,7 +94,7 @@ public function toArray(): array { if ($this->hasExtensionMembers()) { $array = array_merge($array, $this->getExtensionMembers()); } - if ($this->version !== null) { + if (isset($this->version)) { $array['version'] = $this->version->value; } if ($this->extensions !== []) { @@ -120,7 +109,7 @@ public function toArray(): array { $array['profile'][] = $profile->getOfficialLink(); } } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); } diff --git a/src/objects/LinkObject.php b/src/objects/LinkObject.php index d0df1593..45b5a243 100644 --- a/src/objects/LinkObject.php +++ b/src/objects/LinkObject.php @@ -9,26 +9,19 @@ use alsvanzelf\jsonapi\objects\MetaObject; class LinkObject extends AbstractObject implements HasMetaInterface { - /** @var string */ - protected $href; - /** @var string */ - protected $rel; - /** @var LinkObject */ - protected $describedby; - /** @var string */ - protected $title; - /** @var string */ - protected $type; + protected string $href; + protected string $rel; + protected LinkObject $describedby; + protected string $title; + protected string $type; /** @var string[] */ - protected $hreflang = []; - /** @var MetaObject */ - protected $meta; + protected array $hreflang = []; + protected MetaObject $meta; /** - * @param string $href - * @param array $meta optional + * @param array $meta */ - public function __construct($href=null, array $meta=[]) { + public function __construct(?string $href=null, array $meta=[]) { if ($href !== null) { $this->setHref($href); } @@ -41,17 +34,11 @@ public function __construct($href=null, array $meta=[]) { * human api */ - /** - * @param string $href - */ - public function setDescribedBy($href) { + public function setDescribedBy(string $href): void { $this->setDescribedByLinkObject(new LinkObject($href)); } - /** - * @param string $language - */ - public function addLanguage($language) { + public function addLanguage(string $language): void { if ($this->hreflang === []) { $this->setHreflang($language); } @@ -61,7 +48,7 @@ public function addLanguage($language) { } public function addMeta(string $key, mixed $value): void { - if ($this->meta === null) { + if (isset($this->meta) === false) { $this->setMetaObject(new MetaObject()); } @@ -72,56 +59,37 @@ public function addMeta(string $key, mixed $value): void { * spec api */ - /** - * @param string $href - */ - public function setHref($href) { + public function setHref(string $href): void { $this->href = $href; } /** * @todo validate according to https://tools.ietf.org/html/rfc8288#section-2.1 - * - * @param string $relationType */ - public function setRelationType($relationType) { + public function setRelationType(string $relationType): void { $this->rel = $relationType; } - /** - * @param LinkObject $describedBy - */ - public function setDescribedByLinkObject(LinkObject $describedBy) { + public function setDescribedByLinkObject(LinkObject $describedBy): void { $this->describedby = $describedBy; } - /** - * @param string $humanTitle - */ - public function setHumanTitle($humanTitle) { + public function setHumanTitle(string $humanTitle): void { $this->title = $humanTitle; } - /** - * @param string $mediaType - */ - public function setMediaType($mediaType) { + public function setMediaType(string $mediaType): void { $this->type = $mediaType; } /** * @todo validate according to https://tools.ietf.org/html/rfc5646 - * - * @param string ...$hreflang */ - public function setHreflang(...$hreflang) { + public function setHreflang(string ...$hreflang): void { $this->hreflang = $hreflang; } - /** - * @param MetaObject $metaObject - */ - public function setMetaObject(MetaObject $metaObject) { + public function setMetaObject(MetaObject $metaObject): void { $this->meta = $metaObject; } @@ -130,25 +98,25 @@ public function setMetaObject(MetaObject $metaObject) { */ public function isEmpty(): bool { - if ($this->href !== null) { + if (isset($this->href)) { return false; } - if ($this->rel !== null) { + if (isset($this->rel)) { return false; } - if ($this->title !== null) { + if (isset($this->title)) { return false; } - if ($this->type !== null) { + if (isset($this->type)) { return false; } if ($this->hreflang !== []) { return false; } - if ($this->describedby !== null && $this->describedby->isEmpty() === false) { + if (isset($this->describedby) && $this->describedby->isEmpty() === false) { return false; } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { return false; } if ($this->hasAtMembers()) { @@ -171,15 +139,16 @@ public function toArray(): array { $array = array_merge($array, $this->getExtensionMembers()); } - $array['href'] = $this->href; - - if ($this->rel !== null) { + if (isset($this->href)) { + $array['href'] = $this->href; + } + if (isset($this->rel)) { $array['rel'] = $this->rel; } - if ($this->title !== null) { + if (isset($this->title)) { $array['title'] = $this->title; } - if ($this->type !== null) { + if (isset($this->type)) { $array['type'] = $this->type; } if ($this->hreflang !== []) { @@ -190,10 +159,10 @@ public function toArray(): array { $array['hreflang'] = $this->hreflang; } } - if ($this->describedby !== null && $this->describedby->isEmpty() === false) { + if (isset($this->describedby) && $this->describedby->isEmpty() === false) { $array['describedby'] = $this->describedby->toArray(); } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); } diff --git a/src/objects/LinksObject.php b/src/objects/LinksObject.php index eb222401..428e0659 100644 --- a/src/objects/LinksObject.php +++ b/src/objects/LinksObject.php @@ -11,18 +11,17 @@ use alsvanzelf\jsonapi\objects\LinkObject; class LinksObject extends AbstractObject { - /** @var array with string|LinkObject */ - protected $links = []; + /** @var array */ + protected array $links = []; /** * human api */ /** - * @param array $links key-value with values being href strings - * @return LinksObject + * @param array $links key-value with values being href strings */ - public static function fromArray(array $links) { + public static function fromArray(array $links): LinksObject { $linksObject = new self(); foreach ($links as $key => $href) { @@ -32,22 +31,16 @@ public static function fromArray(array $links) { return $linksObject; } - /** - * @param object $links - * @return LinksObject - */ - public static function fromObject($links) { + public static function fromObject(object $links): LinksObject { $array = Converter::objectToArray($links); return self::fromArray($array); } /** - * @param string $key - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function add($key, $href, array $meta=[]) { + public function add(string $key, ?string $href, array $meta=[]): void { if ($meta === []) { $this->addLinkString($key, $href); } @@ -61,12 +54,9 @@ public function add($key, $href, array $meta=[]) { */ /** - * @param string $key - * @param string $href - * * @throws DuplicateException if another link is already using that $key */ - public function addLinkString($key, $href) { + public function addLinkString(string $key, ?string $href): void { Validator::checkMemberName($key); if (isset($this->links[$key])) { @@ -77,12 +67,9 @@ public function addLinkString($key, $href) { } /** - * @param string $key - * @param LinkObject $linkObject - * * @throws DuplicateException if another link is already using that $key */ - public function addLinkObject($key, LinkObject $linkObject) { + public function addLinkObject(string $key, LinkObject $linkObject): void { Validator::checkMemberName($key); if (isset($this->links[$key])) { diff --git a/src/objects/MetaObject.php b/src/objects/MetaObject.php index 2ed72d19..8aecd913 100644 --- a/src/objects/MetaObject.php +++ b/src/objects/MetaObject.php @@ -9,18 +9,17 @@ use alsvanzelf\jsonapi\objects\AbstractObject; class MetaObject extends AbstractObject { - /** @var array */ - protected $meta = []; + /** @var array */ + protected array $meta = []; /** * human api */ /** - * @param array $meta - * @return MetaObject + * @param array $meta */ - public static function fromArray(array $meta) { + public static function fromArray(array $meta): self { $metaObject = new self(); foreach ($meta as $key => $value) { @@ -30,11 +29,7 @@ public static function fromArray(array $meta) { return $metaObject; } - /** - * @param object $meta - * @return MetaObject - */ - public static function fromObject($meta) { + public static function fromObject(object $meta): self { $array = Converter::objectToArray($meta); return self::fromArray($array); @@ -44,11 +39,7 @@ public static function fromObject($meta) { * spec api */ - /** - * @param string $key - * @param mixed $value - */ - public function add($key, $value) { + public function add(string $key, mixed $value): void { Validator::checkMemberName($key); if (is_object($value)) { diff --git a/src/objects/RelationshipObject.php b/src/objects/RelationshipObject.php index bb83ff6c..c3b8eca4 100644 --- a/src/objects/RelationshipObject.php +++ b/src/objects/RelationshipObject.php @@ -21,15 +21,13 @@ class RelationshipObject extends AbstractObject implements PaginableInterface, RecursiveResourceContainerInterface, HasLinksInterface, HasMetaInterface { use LinksManager; - /** @var MetaObject */ - protected $meta; - /** @var ResourceInterface */ - protected $resource; + protected MetaObject $meta; + protected ResourceInterface $resource; /** @var ResourceInterface[] */ - protected $resources = []; + protected array $resources = []; public function __construct( - protected RelationshipTypeEnum $type, + protected readonly RelationshipTypeEnum $type, ) {} /** @@ -39,14 +37,17 @@ public function __construct( /** * create a RelationshipObject from mixed input * - * @param mixed $relation ResourceInterface | ResourceInterface[] | CollectionDocument - * @param array $links optional - * @param array $meta optional - * @return RelationshipObject + * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation + * @param array $links + * @param array $meta * * @throws InputException if $relation is not one of the supported formats */ - public static function fromAnything($relation, array $links=[], array $meta=[]) { + public static function fromAnything( + array|CollectionDocument|ResourceInterface|null $relation, + array $links=[], + array $meta=[], + ): self { if (is_array($relation)) { $relation = CollectionDocument::fromResources(...$relation); } @@ -68,12 +69,15 @@ public static function fromAnything($relation, array $links=[], array $meta=[]) } /** - * @param ResourceInterface $resource - * @param array $links optional - * @param array $meta optional - * @return RelationshipObject + * @param array $links + * @param array $meta */ - public static function fromResource(ResourceInterface $resource, array $links=[], array $meta=[], RelationshipTypeEnum $type=RelationshipTypeEnum::ToOne) { + public static function fromResource( + ResourceInterface $resource, + array $links=[], + array $meta=[], + RelationshipTypeEnum $type=RelationshipTypeEnum::ToOne, + ): self { $relationshipObject = new self($type); match ($type) { @@ -92,12 +96,10 @@ public static function fromResource(ResourceInterface $resource, array $links=[] } /** - * @param CollectionDocument $collectionDocument - * @param array $links optional - * @param array $meta optional - * @return RelationshipObject + * @param array $links + * @param array $meta */ - public static function fromCollectionDocument(CollectionDocument $collectionDocument, array $links=[], array $meta=[]) { + public static function fromCollectionDocument(CollectionDocument $collectionDocument, array $links=[], array $meta=[]): self { $relationshipObject = new self(RelationshipTypeEnum::ToMany); foreach ($collectionDocument->getContainedResources() as $resource) { @@ -115,18 +117,16 @@ public static function fromCollectionDocument(CollectionDocument $collectionDocu } /** - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setSelfLink($href, array $meta=[]) { + public function setSelfLink(string $href, array $meta=[]): void { $this->addLink('self', $href, $meta); } /** - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setRelatedLink($href, array $meta=[]) { + public function setRelatedLink(string $href, array $meta=[]): void { $this->addLink('related', $href, $meta); } @@ -158,7 +158,7 @@ public function setPaginationLinks( } public function addMeta(string $key, mixed $value): void { - if ($this->meta === null) { + if (isset($this->meta) === false) { $this->setMetaObject(new MetaObject()); } @@ -172,11 +172,9 @@ public function addMeta(string $key, mixed $value): void { /** * set the resource on a to-one relationship * - * @param ResourceInterface $resource - * * @throws InputException if used on a to-many relationship, use {@see ->addResource()} instead */ - public function setResource(ResourceInterface $resource) { + public function setResource(ResourceInterface $resource): void { if ($this->type === RelationshipTypeEnum::ToMany) { throw new InputException('can not set a resource on a to-many relationship, use ->addResource()'); } @@ -187,11 +185,9 @@ public function setResource(ResourceInterface $resource) { /** * add a resource to a to-many relationship * - * @param ResourceInterface $resource - * * @throws InputException if used on a to-one relationship, use {@see ->setResource()} instead */ - public function addResource(ResourceInterface $resource) { + public function addResource(ResourceInterface $resource): void { if ($this->type === RelationshipTypeEnum::ToOne) { throw new InputException('can not add a resource to a to-one relationship, use ->setResource()'); } @@ -199,10 +195,7 @@ public function addResource(ResourceInterface $resource) { $this->resources[] = $resource; } - /** - * @param MetaObject $metaObject - */ - public function setMetaObject(MetaObject $metaObject) { + public function setMetaObject(MetaObject $metaObject): void { $this->meta = $metaObject; } @@ -214,11 +207,8 @@ public function setMetaObject(MetaObject $metaObject) { * whether or not the $otherResource is (one of) the resource(s) inside the relationship * * @internal - * - * @param ResourceInterface $otherResource - * @return boolean */ - public function hasResource(ResourceInterface $otherResource) { + public function hasResource(ResourceInterface $otherResource): bool { if ($this->isEmpty()) { return false; } @@ -243,7 +233,7 @@ public function hasResource(ResourceInterface $otherResource) { */ public function isEmpty(): bool { - if ($this->type === RelationshipTypeEnum::ToOne && $this->resource !== null) { + if ($this->type === RelationshipTypeEnum::ToOne && isset($this->resource)) { return false; } if ($this->type === RelationshipTypeEnum::ToMany && $this->resources !== []) { @@ -252,7 +242,7 @@ public function isEmpty(): bool { if ($this->hasLinks()) { return false; } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { return false; } if ($this->hasAtMembers()) { @@ -280,7 +270,7 @@ public function toArray(): array { } if ($this->type === RelationshipTypeEnum::ToOne) { $array['data'] = null; - if ($this->resource !== null) { + if (isset($this->resource)) { $array['data'] = $this->resource->getResource($identifierOnly=true)->toArray(); } } @@ -290,7 +280,7 @@ public function toArray(): array { $array['data'][] = $resource->getResource($identifierOnly=true)->toArray(); } } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); } diff --git a/src/objects/RelationshipsObject.php b/src/objects/RelationshipsObject.php index a1aae78d..c1d42746 100644 --- a/src/objects/RelationshipsObject.php +++ b/src/objects/RelationshipsObject.php @@ -4,9 +4,11 @@ namespace alsvanzelf\jsonapi\objects; +use alsvanzelf\jsonapi\CollectionDocument; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\helpers\Validator; use alsvanzelf\jsonapi\interfaces\RecursiveResourceContainerInterface; +use alsvanzelf\jsonapi\interfaces\ResourceInterface; use alsvanzelf\jsonapi\objects\AbstractObject; use alsvanzelf\jsonapi\objects\LinkObject; use alsvanzelf\jsonapi\objects\RelationshipObject; @@ -14,20 +16,23 @@ class RelationshipsObject extends AbstractObject implements RecursiveResourceContainerInterface { /** @var RelationshipObject[] */ - protected $relationships = []; + protected array $relationships = []; /** * human api */ /** - * @param string $key - * @param mixed $relation ResourceInterface | ResourceInterface[] | CollectionDocument - * @param array $links optional - * @param array $meta optional - * @return RelationshipObject + * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation + * @param array $links + * @param array $meta */ - public function add($key, $relation, array $links=[], array $meta=[]) { + public function add( + string $key, + $relation, + array $links=[], + array $meta=[], + ): RelationshipObject { $relationshipObject = RelationshipObject::fromAnything($relation, $links, $meta); $this->addRelationshipObject($key, $relationshipObject); @@ -40,12 +45,9 @@ public function add($key, $relation, array $links=[], array $meta=[]) { */ /** - * @param string $key - * @param RelationshipObject $relationshipObject - * * @throws DuplicateException if another relationship is already using that $key */ - public function addRelationshipObject($key, RelationshipObject $relationshipObject) { + public function addRelationshipObject(string $key, RelationshipObject $relationshipObject): void { Validator::checkMemberName($key); if (isset($this->relationships[$key])) { @@ -64,7 +66,7 @@ public function addRelationshipObject($key, RelationshipObject $relationshipObje * * @return string[] */ - public function getKeys() { + public function getKeys(): array { return array_keys($this->relationships); } diff --git a/src/objects/ResourceIdentifierObject.php b/src/objects/ResourceIdentifierObject.php index e9760daa..22d35e9b 100644 --- a/src/objects/ResourceIdentifierObject.php +++ b/src/objects/ResourceIdentifierObject.php @@ -7,6 +7,7 @@ use alsvanzelf\jsonapi\enums\ObjectContainerEnum; use alsvanzelf\jsonapi\exceptions\DuplicateException; use alsvanzelf\jsonapi\exceptions\Exception; +use alsvanzelf\jsonapi\exceptions\InputException; use alsvanzelf\jsonapi\helpers\Validator; use alsvanzelf\jsonapi\interfaces\HasMetaInterface; use alsvanzelf\jsonapi\interfaces\ResourceInterface; @@ -14,26 +15,18 @@ use alsvanzelf\jsonapi\objects\MetaObject; class ResourceIdentifierObject extends AbstractObject implements HasMetaInterface, ResourceInterface { - /** @var string */ - protected $type; - /** @var string */ - protected $id; - /** @var string */ - protected $lid; - /** @var MetaObject */ - protected $meta; - /** @var Validator */ - protected $validator; + protected string $type; + protected string $id; + protected string $lid; + protected MetaObject $meta; + protected readonly Validator $validator; /** * @note $type and $id are optional to pass during construction * however they are required for a valid ResourceIdentifierObject * so use ->setType() and ->setId() if not passing them during construction - * - * @param string $type optional - * @param string|int $id optional */ - public function __construct($type=null, $id=null) { + public function __construct(?string $type=null, string|int|null $id=null) { $this->validator = new Validator(); if ($type !== null) { @@ -54,7 +47,7 @@ public function __construct($type=null, $id=null) { */ public function addMeta(string $key, mixed $value): void { - if ($this->meta === null) { + if (isset($this->meta) === false) { $this->setMetaObject(new MetaObject()); } @@ -65,20 +58,17 @@ public function addMeta(string $key, mixed $value): void { * spec api */ - /** - * @param string $type - */ - public function setType($type) { + public function setType(string $type): void { $this->type = $type; } /** - * @param string|int $id will be casted to a string + * int $id will be casted to a string * * @throws DuplicateException if localId is already set */ - public function setId($id) { - if ($this->lid !== null) { + public function setId(string|int $id): void { + if (isset($this->lid)) { throw new DuplicateException('id is not allowed when localId is already set'); } @@ -90,22 +80,19 @@ public function setId($id) { * * @note this should not be used to send back from the server to the client * - * @param string|int $localId will be casted to a string + * int $localId will be casted to a string * * @throws DuplicateException if normal id is already set */ - public function setLocalId($localId) { - if ($this->id !== null) { + public function setLocalId(string|int $localId): void { + if (isset($this->id)) { throw new DuplicateException('localId is not allowed when id is already set'); } $this->lid = (string) $localId; } - /** - * @param MetaObject $metaObject - */ - public function setMetaObject(MetaObject $metaObject) { + public function setMetaObject(MetaObject $metaObject): void { $this->meta = $metaObject; } @@ -116,13 +103,16 @@ public function setMetaObject(MetaObject $metaObject) { /** * @internal * - * @param ResourceObject $resourceObject - * @return ResourceIdentifierObject + * @throws InputException if the $resourceoObject's type or id is not set yet */ - public static function fromResourceObject(ResourceObject $resourceObject) { + public static function fromResourceObject(ResourceObject $resourceObject): self { + if ($resourceObject->hasIdentification() === false) { + throw new InputException('resource has no identification yet<'); + } + $resourceIdentifierObject = new self($resourceObject->type, $resourceObject->primaryId()); - if ($resourceObject->meta !== null) { + if (isset($resourceObject->meta)) { $resourceIdentifierObject->setMetaObject($resourceObject->meta); } @@ -132,12 +122,9 @@ public static function fromResourceObject(ResourceObject $resourceObject) { /** * @internal * - * @param ResourceInterface $resource - * @return boolean - * * @throws Exception if one or both are missing identification */ - public function equals(ResourceInterface $resource) { + public function equals(ResourceInterface $resource): bool { if ($this->hasIdentification() === false || $resource->getResource()->hasIdentification() === false) { throw new Exception('can not compare resources if identification is missing'); } @@ -147,11 +134,17 @@ public function equals(ResourceInterface $resource) { /** * @internal - * - * @return boolean */ - public function hasIdentification() { - return ($this->type !== null && $this->primaryId() !== null); + public function hasIdentification(): bool { + if (isset($this->type) === false) { + return false; + } + + if (isset($this->id) === false && isset($this->lid) === false) { + return false; + } + + return true; } /** @@ -159,11 +152,9 @@ public function hasIdentification() { * * @internal * - * @return string - * * @throws Exception if type or id is not set yet */ - public function getIdentificationKey() { + public function getIdentificationKey(): string { if ($this->hasIdentification() === false) { throw new Exception('resource has no identification yet'); } @@ -176,10 +167,13 @@ public function getIdentificationKey() { */ public function isEmpty(): bool { - if ($this->type !== null || $this->primaryId() !== null) { + if (isset($this->type)) { + return false; + } + if (isset($this->id) || isset($this->lid)) { return false; } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { return false; } if ($this->hasAtMembers()) { @@ -195,12 +189,13 @@ public function isEmpty(): bool { public function toArray(): array { $array = []; - $array['type'] = $this->type; - - if ($this->id !== null) { + if (isset($this->type)) { + $array['type'] = $this->type; + } + if (isset($this->id)) { $array['id'] = $this->id; } - elseif ($this->lid !== null) { + elseif (isset($this->lid)) { $array['lid'] = $this->lid; } @@ -211,7 +206,7 @@ public function toArray(): array { $array = array_merge($array, $this->getExtensionMembers()); } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); } @@ -230,8 +225,12 @@ public function getResource(bool $identifierOnly=false): ResourceIdentifierObjec * @internal */ - private function primaryId() { - if ($this->lid !== null) { + private function primaryId(): string { + if (isset($this->id) === false && isset($this->lid) === false) { + throw new Exception('resource has no identification yet'); + } + + if (isset($this->lid)) { return $this->lid; } diff --git a/src/objects/ResourceObject.php b/src/objects/ResourceObject.php index f387414c..4be34ef8 100644 --- a/src/objects/ResourceObject.php +++ b/src/objects/ResourceObject.php @@ -21,12 +21,10 @@ class ResourceObject extends ResourceIdentifierObject implements HasAttributesInterface, HasLinksInterface, RecursiveResourceContainerInterface { use LinksManager; - /** @var AttributesObject */ - protected $attributes; - /** @var RelationshipsObject */ - protected $relationships; - /** @var array */ - protected static $defaults = [ + protected AttributesObject $attributes; + protected RelationshipsObject $relationships; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * blocks 'type' as a keyword inside attributes or relationships * the specification doesn't allow this as 'type' is already set at the root of a resource @@ -44,13 +42,10 @@ class ResourceObject extends ResourceIdentifierObject implements HasAttributesIn * and if $id is null, it is filled with that value * it is common to find it inside, and not doing so will cause an exception * - * @param array $attributes - * @param string $type optional - * @param string|int $id optional - * @param array $options optional {@see ResourceObject::$defaults} - * @return ResourceObject + * @param array $attributes + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ - public static function fromArray(array $attributes, $type=null, $id=null, array $options=[]) { + public static function fromArray(array $attributes, ?string $type=null, string|int|null $id=null, array $options=[]): self { if (isset($attributes['id'])) { if ($id === null) { $id = $attributes['id']; @@ -66,13 +61,9 @@ public static function fromArray(array $attributes, $type=null, $id=null, array } /** - * @param object $attributes - * @param string $type optional - * @param string|int $id optional - * @param array $options optional {@see ResourceObject::$defaults} - * @return ResourceObject + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ - public static function fromObject($attributes, $type=null, $id=null, array $options=[]) { + public static function fromObject(object $attributes, ?string $type=null, string|int|null $id=null, array $options=[]): self { $array = Converter::objectToArray($attributes); return self::fromArray($array, $type, $id, $options); @@ -81,14 +72,12 @@ public static function fromObject($attributes, $type=null, $id=null, array $opti /** * add key-value pairs to attributes * - * @param string $key - * @param mixed $value - * @param array $options optional {@see ResourceObject::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ - public function add($key, $value, array $options=[]) { + public function add(string $key, mixed $value, array $options=[]): void { $options = array_merge(self::$defaults, $options); - if ($this->attributes === null) { + if (isset($this->attributes) === false) { $this->attributes = new AttributesObject(); } @@ -98,14 +87,18 @@ public function add($key, $value, array $options=[]) { } /** - * @param string $key - * @param mixed $relation ResourceInterface | ResourceInterface[] | CollectionDocument - * @param array $links optional - * @param array $meta optional - * @param array $options optional {@see ResourceObject::$defaults} - * @return RelationshipObject + * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation + * @param array $links + * @param array $meta + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ - public function addRelationship($key, $relation, array $links=[], array $meta=[], array $options=[]) { + public function addRelationship( + string $key, + array|CollectionDocument|ResourceInterface|null $relation, + array $links=[], + array $meta=[], + array $options=[], + ): RelationshipObject { $relationshipObject = RelationshipObject::fromAnything($relation, $links, $meta); $this->addRelationshipObject($key, $relationshipObject, $options); @@ -114,10 +107,9 @@ public function addRelationship($key, $relation, array $links=[], array $meta=[] } /** - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setSelfLink($href, array $meta=[]) { + public function setSelfLink(string $href, array $meta=[]): void { $this->addLink('self', $href, $meta); } @@ -126,10 +118,9 @@ public function setSelfLink($href, array $meta=[]) { */ /** - * @param AttributesObject $attributesObject - * @param array $options optional {@see ResourceObject::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ - public function setAttributesObject(AttributesObject $attributesObject, array $options=[]) { + public function setAttributesObject(AttributesObject $attributesObject, array $options=[]): void { $newKeys = $attributesObject->getKeys(); $this->validator->clearUsedFields(ObjectContainerEnum::Attributes); $this->validator->claimUsedFields($newKeys, ObjectContainerEnum::Attributes, $options); @@ -138,18 +129,16 @@ public function setAttributesObject(AttributesObject $attributesObject, array $o } /** - * @param string $key - * @param RelationshipObject $relationshipObject - * @param array $options optional {@see ResourceObject::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} * * @throws DuplicateException if the resource is contained as a resource in the relationship */ - public function addRelationshipObject($key, RelationshipObject $relationshipObject, array $options=[]) { + public function addRelationshipObject(string $key, RelationshipObject $relationshipObject, array $options=[]): void { if ($relationshipObject->hasResource($this)) { throw new DuplicateException('can not add relation to self'); } - if ($this->relationships === null) { + if (isset($this->relationships) === false) { $this->setRelationshipsObject(new RelationshipsObject()); } @@ -158,10 +147,7 @@ public function addRelationshipObject($key, RelationshipObject $relationshipObje $this->relationships->addRelationshipObject($key, $relationshipObject); } - /** - * @param RelationshipsObject $relationshipsObject - */ - public function setRelationshipsObject(RelationshipsObject $relationshipsObject) { + public function setRelationshipsObject(RelationshipsObject $relationshipsObject): void { $newKeys = $relationshipsObject->getKeys(); $this->validator->clearUsedFields(ObjectContainerEnum::Relationships); $this->validator->claimUsedFields($newKeys, ObjectContainerEnum::Relationships); @@ -179,14 +165,12 @@ public function setRelationshipsObject(RelationshipsObject $relationshipsObject) * this can be used to determine if a Relationship's resource could be added as included resource * * @internal - * - * @return boolean */ - public function hasIdentifierPropertiesOnly() { - if ($this->attributes !== null && $this->attributes->isEmpty() === false) { + public function hasIdentifierPropertiesOnly(): bool { + if (isset($this->attributes) && $this->attributes->isEmpty() === false) { return false; } - if ($this->relationships !== null && $this->relationships->isEmpty() === false) { + if (isset($this->relationships) && $this->relationships->isEmpty() === false) { return false; } if ($this->hasLinks()) { @@ -224,10 +208,10 @@ public function isEmpty(): bool { if (parent::isEmpty() === false) { return false; } - if ($this->attributes !== null && $this->attributes->isEmpty() === false) { + if (isset($this->attributes) && $this->attributes->isEmpty() === false) { return false; } - if ($this->relationships !== null && $this->relationships->isEmpty() === false) { + if (isset($this->relationships) && $this->relationships->isEmpty() === false) { return false; } if ($this->hasLinks()) { @@ -240,10 +224,10 @@ public function isEmpty(): bool { public function toArray(): array { $array = parent::toArray(); - if ($this->attributes !== null && $this->attributes->isEmpty() === false) { + if (isset($this->attributes) && $this->attributes->isEmpty() === false) { $array['attributes'] = $this->attributes->toArray(); } - if ($this->relationships !== null && $this->relationships->isEmpty() === false) { + if (isset($this->relationships) && $this->relationships->isEmpty() === false) { $array['relationships'] = $this->relationships->toArray(); } if ($this->hasLinks()) { @@ -258,7 +242,7 @@ public function toArray(): array { */ public function getNestedContainedResourceObjects(): array { - if ($this->relationships === null) { + if (isset($this->relationships) === false) { return []; } diff --git a/tests/objects/RelationshipObjectTest.php b/tests/objects/RelationshipObjectTest.php index b941feaa..8ce618d6 100644 --- a/tests/objects/RelationshipObjectTest.php +++ b/tests/objects/RelationshipObjectTest.php @@ -65,16 +65,6 @@ public function testFromAnything_WithResourceObjects() { $this->validateToManyRelationshipArray($relationshipObject->toArray()); } - public function testFromAnything_WithUnknownType() { - $fakeResource = new \stdClass(); - $fakeResource->type = 'user'; - $fakeResource->id = 42; - - $this->expectException(InputException::class); - - RelationshipObject::fromAnything($fakeResource); - } - public function testFromResource_ToMany() { $resourceObject = new ResourceObject('user', 42); $type = RelationshipTypeEnum::ToMany; diff --git a/tests/objects/ResourceIdentifierObjectTest.php b/tests/objects/ResourceIdentifierObjectTest.php index 5724491f..6383aa20 100644 --- a/tests/objects/ResourceIdentifierObjectTest.php +++ b/tests/objects/ResourceIdentifierObjectTest.php @@ -143,9 +143,9 @@ public function testGetIdentificationKey_NoIdentification() { $array = $resourceIdentifierObject->toArray(); - $this->assertArrayHasKey('type', $array); + $this->assertArrayNotHasKey('type', $array); $this->assertArrayNotHasKey('id', $array); - $this->assertNull($array['type']); + $this->assertSame([], $array); $this->assertFalse($resourceIdentifierObject->hasIdentification()); $this->expectException(Exception::class); From 871760ba3e2628382c03eec8354ea4dec892a262 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 26 Dec 2025 19:39:05 +0100 Subject: [PATCH 08/12] add strict type to all documents --- src/CollectionDocument.php | 22 +++----- src/DataDocument.php | 10 ++-- src/Document.php | 58 ++++++++------------- src/ErrorsDocument.php | 41 +++++++-------- src/MetaDocument.php | 15 ++---- src/ResourceDocument.php | 103 +++++++++++++++++-------------------- 6 files changed, 105 insertions(+), 144 deletions(-) diff --git a/src/CollectionDocument.php b/src/CollectionDocument.php index 57364841..fd8935b0 100644 --- a/src/CollectionDocument.php +++ b/src/CollectionDocument.php @@ -20,9 +20,9 @@ */ class CollectionDocument extends DataDocument implements PaginableInterface, ResourceContainerInterface { /** @var ResourceInterface[] */ - protected $resources = []; - /** @var array */ - protected static $defaults = [ + protected array $resources = []; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * add resources inside relationships to /included when adding resources to the collection */ @@ -37,11 +37,8 @@ class CollectionDocument extends DataDocument implements PaginableInterface, Res * generate a CollectionDocument from one or multiple resources * * adds included resources if found inside the resource's relationships, use {@see ->addResource()} to change that behavior - * - * @param ResourceInterface ...$resources - * @return CollectionDocument */ - public static function fromResources(ResourceInterface ...$resources) { + public static function fromResources(ResourceInterface ...$resources): self { $collectionDocument = new self(); foreach ($resources as $resource) { @@ -52,11 +49,9 @@ public static function fromResources(ResourceInterface ...$resources) { } /** - * @param string $type - * @param string|int $id - * @param array $attributes optional, if given a ResourceObject is added, otherwise a ResourceIdentifierObject is added + * @param array $attributes if given a ResourceObject is added, otherwise a ResourceIdentifierObject is added */ - public function add($type, $id, array $attributes=[]) { + public function add(string $type, string|int $id, array $attributes=[]): void { if ($attributes === []) { $this->addResource(new ResourceIdentifierObject($type, $id)); } @@ -94,12 +89,11 @@ public function setPaginationLinks( * * adds included resources if found inside the resource's relationships, unless $options['includeContainedResources'] is set to false * - * @param ResourceInterface $resource - * @param array $options optional {@see CollectionDocument::$defaults} + * @param TypeAlias_InternalOptions $options {@see CollectionDocument::$defaults} * * @throws InputException if the resource is empty */ - public function addResource(ResourceInterface $resource, array $options=[]) { + public function addResource(ResourceInterface $resource, array $options=[]): void { if ($resource->getResource()->isEmpty()) { throw new InputException('does not make sense to add empty resources to a collection'); } diff --git a/src/DataDocument.php b/src/DataDocument.php index ed6f550b..8d44270e 100644 --- a/src/DataDocument.php +++ b/src/DataDocument.php @@ -10,13 +10,13 @@ use alsvanzelf\jsonapi\objects\ResourceObject; /** + * @internal * @see ResourceDocument or CollectionDocument */ abstract class DataDocument extends Document { /** @var ResourceObject[] */ - protected $includedResources = []; - /** @var Validator */ - protected $validator; + protected array $includedResources = []; + protected readonly Validator $validator; public function __construct() { parent::__construct(); @@ -35,10 +35,8 @@ public function __construct() { /** * mainly used when an `included` query parameter is passed * and resources are requested separate from what is standard for a response - * - * @param ResourceObject ...$resourceObjects */ - public function addIncludedResourceObject(ResourceObject ...$resourceObjects) { + public function addIncludedResourceObject(ResourceObject ...$resourceObjects): void { foreach ($resourceObjects as $resourceObject) { try { $this->validator->claimUsedResourceIdentifier($resourceObject); diff --git a/src/Document.php b/src/Document.php index b44a24f9..7633e5ff 100644 --- a/src/Document.php +++ b/src/Document.php @@ -34,16 +34,14 @@ abstract class Document implements DocumentInterface, \JsonSerializable, HasLink LinksManager::addLink as linkManagerAddLink; } - /** @var MetaObject */ - protected $meta; - /** @var ?JsonapiObject */ - protected $jsonapi; + protected MetaObject $meta; + protected ?JsonapiObject $jsonapi; /** @var ExtensionInterface[] */ - protected $extensions = []; + protected array $extensions = []; /** @var ProfileInterface[] */ - protected $profiles = []; - /** @var array */ - protected static $defaults = [ + protected array $profiles = []; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * encode to json with these default options */ @@ -105,10 +103,9 @@ public function addLink(string $key, ?string $href, array $meta=[], DocumentLeve * * @note a LinkObject is added when extensions or profiles are applied * - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setSelfLink($href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root) { + public function setSelfLink(string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($level === DocumentLevelEnum::Root && ($this->extensions !== [] || $this->profiles !== [])) { $contentType = Converter::prepareContentType(ContentTypeEnum::Official, $this->extensions, $this->profiles); @@ -129,10 +126,9 @@ public function setSelfLink($href, array $meta=[], DocumentLevelEnum $level=Docu * * @note according to the spec, this can only be set to DocumentLevelEnum::Root * - * @param string $href - * @param array $meta optional, if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ - public function setDescribedByLink($href, array $meta=[]) { + public function setDescribedByLink(string $href, array $meta=[]): void { $this->addLink('describedby', $href, $meta, $level=DocumentLevelEnum::Root); } @@ -142,14 +138,14 @@ public function setDescribedByLink($href, array $meta=[]) { */ public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($level === DocumentLevelEnum::Root) { - if ($this->meta === null) { + if (isset($this->meta) === false) { $this->setMetaObject(new MetaObject()); } $this->meta->add($key, $value); } elseif ($level === DocumentLevelEnum::Jsonapi) { - if ($this->jsonapi === null) { + if (isset($this->jsonapi) === false) { $this->setJsonapiObject(new JsonapiObject()); } @@ -167,24 +163,18 @@ public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=Docu * spec api */ - /** - * @param MetaObject $metaObject - */ - public function setMetaObject(MetaObject $metaObject) { + public function setMetaObject(MetaObject $metaObject): void { $this->meta = $metaObject; } - /** - * @param JsonapiObject $jsonapiObject - */ - public function setJsonapiObject(JsonapiObject $jsonapiObject) { + public function setJsonapiObject(JsonapiObject $jsonapiObject): void { $this->jsonapi = $jsonapiObject; } /** * hide that this api supports jsonapi, or which version it is using */ - public function unsetJsonapiObject() { + public function unsetJsonapiObject(): void { $this->jsonapi = null; } @@ -197,12 +187,10 @@ public function unsetJsonapiObject() { * * @see https://jsonapi.org/extensions/#extensions * - * @param ExtensionInterface $extension - * * @throws Exception if namespace uses illegal characters * @throws DuplicateException if namespace conflicts with another applied extension */ - public function applyExtension(ExtensionInterface $extension) { + public function applyExtension(ExtensionInterface $extension): void { $namespace = $extension->getNamespace(); if (strlen($namespace) < 1 || preg_match('{[^a-zA-Z0-9]}', $namespace) === 1) { throw new Exception('invalid namespace "'.$namespace.'"'); @@ -213,7 +201,7 @@ public function applyExtension(ExtensionInterface $extension) { $this->extensions[$namespace] = $extension; - if ($this->jsonapi !== null) { + if (isset($this->jsonapi)) { $this->jsonapi->addExtension($extension); } } @@ -226,13 +214,11 @@ public function applyExtension(ExtensionInterface $extension) { * however the $profile could have custom methods to help * * @see https://jsonapi.org/extensions/#profiles - * - * @param ProfileInterface $profile */ - public function applyProfile(ProfileInterface $profile) { + public function applyProfile(ProfileInterface $profile): void { $this->profiles[] = $profile; - if ($this->jsonapi !== null) { + if (isset($this->jsonapi)) { $this->jsonapi->addProfile($profile); } } @@ -251,13 +237,13 @@ public function toArray(): array { $array = array_merge($array, $this->getExtensionMembers()); } - if ($this->jsonapi !== null && $this->jsonapi->isEmpty() === false) { + if (isset($this->jsonapi) && $this->jsonapi->isEmpty() === false) { $array['jsonapi'] = $this->jsonapi->toArray(); } if ($this->hasLinks()) { $array['links'] = $this->links->toArray(); } - if ($this->meta !== null && $this->meta->isEmpty() === false) { + if (isset($this->meta) && $this->meta->isEmpty() === false) { $array['meta'] = $this->meta->toArray(); } @@ -308,7 +294,7 @@ public function sendResponse(array $options=[]): void { */ #[\ReturnTypeWillChange] - public function jsonSerialize() { + public function jsonSerialize(): array { return $this->toArray(); } } diff --git a/src/ErrorsDocument.php b/src/ErrorsDocument.php index 503df3cd..549f046a 100644 --- a/src/ErrorsDocument.php +++ b/src/ErrorsDocument.php @@ -12,11 +12,11 @@ */ class ErrorsDocument extends Document { /** @var ErrorObject[] */ - protected $errors = []; - /** @var array */ - protected $httpStatusCodes; - /** @var array */ - protected static $defaults = [ + protected array $errors = []; + /** @var array> */ + protected array $httpStatusCodes; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * add the trace of exceptions when adding exceptions * in some cases it might be handy to disable if traces are too big @@ -29,9 +29,6 @@ class ErrorsDocument extends Document { 'includeExceptionPrevious' => true, ]; - /** - * @param ?ErrorObject $errorObject optional - */ public function __construct(?ErrorObject $errorObject=null) { parent::__construct(); @@ -45,11 +42,9 @@ public function __construct(?ErrorObject $errorObject=null) { */ /** - * @param \Throwable $exception - * @param array $options optional {@see ErrorsDocument::$defaults} - * @return ErrorsDocument + * @param TypeAlias_InternalOptions $options {@see ErrorsDocument::$defaults} */ - public static function fromException(\Throwable $exception, array $options=[]) { + public static function fromException(\Throwable $exception, array $options=[]): self { $options = array_merge(self::$defaults, $options); $errorsDocument = new self(); @@ -63,10 +58,9 @@ public static function fromException(\Throwable $exception, array $options=[]) { * * recursively adds multiple ErrorObjects if $exception carries a ->getPrevious() * - * @param \Throwable $exception - * @param array $options optional {@see ErrorsDocument::$defaults} + * @param TypeAlias_InternalOptions $options {@see ErrorsDocument::$defaults} */ - public function addException(\Throwable $exception, array $options=[]) { + public function addException(\Throwable $exception, array $options=[]): void { $options = array_merge(self::$defaults, $options); $this->addErrorObject(ErrorObject::fromException($exception, $options)); @@ -87,7 +81,13 @@ public function addException(\Throwable $exception, array $options=[]) { * @param string $specificAboutLink optional, human-friendly explanation of the specific error * @param string $genericTypeLink optional, human-friendly explanation of the generic type of error */ - public function add($genericCode, $genericTitle, $specificDetails=null, $specificAboutLink=null, $genericTypeLink=null) { + public function add( + string|int $genericCode, + string $genericTitle, + ?string $specificDetails=null, + ?string $specificAboutLink=null, + ?string $genericTypeLink=null, + ): void { $errorObject = new ErrorObject($genericCode, $genericTitle, $specificDetails, $specificAboutLink, $genericTypeLink); $this->addErrorObject($errorObject); @@ -99,10 +99,8 @@ public function add($genericCode, $genericTitle, $specificDetails=null, $specifi /** * @note also defines the http status code of the document if the ErrorObject has it defined - * - * @param ErrorObject $errorObject */ - public function addErrorObject(ErrorObject $errorObject) { + public function addErrorObject(ErrorObject $errorObject): void { $this->errors[] = $errorObject; if ($errorObject->hasHttpStatusCode()) { @@ -135,11 +133,8 @@ public function toArray(): array { /** * @internal - * - * @param string|int $httpStatusCode - * @return int */ - protected function determineHttpStatusCode($httpStatusCode) { + protected function determineHttpStatusCode(string|int $httpStatusCode): int { // add the new code $category = substr((string) $httpStatusCode, 0, 1); $this->httpStatusCodes[$category][$httpStatusCode] = true; diff --git a/src/MetaDocument.php b/src/MetaDocument.php index 1eda9711..4302845f 100644 --- a/src/MetaDocument.php +++ b/src/MetaDocument.php @@ -19,10 +19,9 @@ class MetaDocument extends Document { */ /** - * @param array $meta - * @return MetaDocument + * @param array $meta */ - public static function fromArray(array $meta) { + public static function fromArray(array $meta): self { $metaDocument = new self(); $metaDocument->setMetaObject(MetaObject::fromArray($meta)); @@ -30,10 +29,9 @@ public static function fromArray(array $meta) { } /** - * @param object $meta - * @return MetaDocument + * @param object $meta */ - public static function fromObject($meta) { + public static function fromObject(object $meta): self { $array = Converter::objectToArray($meta); return self::fromArray($array); @@ -41,11 +39,8 @@ public static function fromObject($meta) { /** * wrapper for Document::addMeta() to the primary data of this document available via `add()` - * - * @param string $key - * @param mixed $value */ - public function add($key, $value, DocumentLevelEnum $level=DocumentLevelEnum::Root) { + public function add(string $key, mixed $value, DocumentLevelEnum $level=DocumentLevelEnum::Root): void { parent::addMeta($key, $value, $level); } diff --git a/src/ResourceDocument.php b/src/ResourceDocument.php index e3f569ed..0b4b525e 100644 --- a/src/ResourceDocument.php +++ b/src/ResourceDocument.php @@ -26,10 +26,9 @@ * a CollectionDocument should be used if the primary Resource is (or can be) a set */ class ResourceDocument extends DataDocument implements HasAttributesInterface, ResourceInterface { - /** @var ResourceIdentifierObject|ResourceObject */ - protected $resource; - /** @var array */ - protected static $defaults = [ + protected ResourceIdentifierObject|ResourceObject $resource; + /** @var TypeAlias_InternalOptions */ + protected static array $defaults = [ /** * add resources inside relationships to /included when adding resources to the collection */ @@ -40,11 +39,8 @@ class ResourceDocument extends DataDocument implements HasAttributesInterface, R * @note $type and $id are optional to pass during construction * however they are required for a valid ResourceDocument * so use ->setPrimaryResource() if not passing them during construction - * - * @param string $type optional - * @param string|int $id optional */ - public function __construct($type=null, $id=null) { + public function __construct(?string $type=null, string|int|null $id=null) { parent::__construct(); $this->setPrimaryResource(new ResourceObject($type, $id)); @@ -55,13 +51,14 @@ public function __construct($type=null, $id=null) { */ /** - * @param array $attributes - * @param string $type optional - * @param string|int $id optional - * @param array $options optional {@see ResourceDocument::$defaults} {@see ResourceObject::$defaults} - * @return ResourceDocument + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} {@see ResourceObject::$defaults} */ - public static function fromArray(array $attributes, $type=null, $id=null, array $options=[]) { + public static function fromArray( + array $attributes, + ?string $type=null, + string|int|null $id=null, + array $options=[], + ): self { $resourceDocument = new self(); $resourceDocument->setPrimaryResource(ResourceObject::fromArray($attributes, $type, $id, $options), $options); @@ -69,13 +66,14 @@ public static function fromArray(array $attributes, $type=null, $id=null, array } /** - * @param object $attributes - * @param string $type optional - * @param string|int $id optional - * @param array $options optional {@see ResourceDocument::$defaults} - * @return ResourceDocument + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ - public static function fromObject($attributes, $type=null, $id=null, array $options=[]) { + public static function fromObject( + object $attributes, + ?string $type=null, + string|int|null $id=null, + array $options=[], + ): self { $array = Converter::objectToArray($attributes); return self::fromArray($array, $type, $id, $options); @@ -84,11 +82,10 @@ public static function fromObject($attributes, $type=null, $id=null, array $opti /** * add key-value pairs to the resource's attributes * - * @param string $key - * @param mixed $value objects will be converted using `get_object_vars()` - * @param array $options optional {@see ResourceDocument::$defaults} + * @param mixed $value objects will be converted using `get_object_vars()` + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ - public function add($key, $value, array $options=[]) { + public function add(string $key, mixed $value, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -101,13 +98,18 @@ public function add($key, $value, array $options=[]) { * * adds included resources if found inside the relation, unless $options['includeContainedResources'] is set to false * - * @param string $key - * @param mixed $relation ResourceInterface | ResourceInterface[] | CollectionDocument - * @param array $links optional - * @param array $meta optional - * @param array $options optional {@see ResourceDocument::$defaults} + * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation + * @param array $links + * @param array $meta + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ - public function addRelationship($key, $relation, array $links=[], array $meta=[], array $options=[]) { + public function addRelationship( + string $key, + array|CollectionDocument|ResourceInterface|null $relation, + array $links=[], + array $meta=[], + array $options=[], + ): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -142,10 +144,9 @@ public function addLink(string $key, ?string $href, array $meta=[], DocumentLeve /** * set the self link on the resource * - * @param string $href - * @param array $meta optional + * @param array $meta */ - public function setSelfLink($href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Resource) { + public function setSelfLink(string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Resource): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -171,32 +172,28 @@ public function addMeta(string $key, mixed $value, DocumentLevelEnum $level=Docu * wrapping ResourceObject spec api */ - /** - * @param string $type - */ - public function setType($type) { + public function setType(string $type): void { $this->resource->setType($type); } /** - * @param string|int $id will be casted to a string + * int $id will be casted to a string */ - public function setId($id) { + public function setId(string|int $id): void { $this->resource->setId($id); } /** - * @param string|int $localId will be casted to a string + * int $localId will be casted to a string */ - public function setLocalId($localId) { + public function setLocalId(string|int $localId): void { $this->resource->setLocalId($localId); } /** - * @param AttributesObject $attributesObject - * @param array $options optional {@see ResourceObject::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ - public function setAttributesObject(AttributesObject $attributesObject, array $options=[]) { + public function setAttributesObject(AttributesObject $attributesObject, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -209,11 +206,9 @@ public function setAttributesObject(AttributesObject $attributesObject, array $o * * adds included resources if found inside the RelationshipObject, unless $options['includeContainedResources'] is set to false * - * @param string $key - * @param RelationshipObject $relationshipObject - * @param array $options optional {@see ResourceDocument::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ - public function addRelationshipObject($key, RelationshipObject $relationshipObject, array $options=[]) { + public function addRelationshipObject(string $key, RelationshipObject $relationshipObject, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -232,10 +227,9 @@ public function addRelationshipObject($key, RelationshipObject $relationshipObje * * adds included resources if found inside the RelationshipObjects inside the RelationshipsObject, unless $options['includeContainedResources'] is set to false * - * @param RelationshipsObject $relationshipsObject - * @param array $options optional {@see ResourceDocument::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ - public function setRelationshipsObject(RelationshipsObject $relationshipsObject, array $options=[]) { + public function setRelationshipsObject(RelationshipsObject $relationshipsObject, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { throw new Exception('the resource is an identifier-only object'); } @@ -258,12 +252,11 @@ public function setRelationshipsObject(RelationshipsObject $relationshipsObject, * * adds included resources if found inside the resource's relationships, unless $options['includeContainedResources'] is set to false * - * @param ResourceInterface $resource - * @param array $options optional {@see ResourceDocument::$defaults} + * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} * * @throws InputException if the $resource is a ResourceDocument itself */ - public function setPrimaryResource(ResourceInterface $resource, array $options=[]) { + public function setPrimaryResource(ResourceInterface $resource, array $options=[]): void { if ($resource instanceof ResourceDocument) { throw new InputException('does not make sense to set a document inside a document, use ResourceObject or ResourceIdentifierObject instead'); } @@ -291,7 +284,7 @@ public function toArray(): array { $array = parent::toArray(); $array['data'] = null; - if ($this->resource !== null && $this->resource->isEmpty() === false) { + if ($this->resource->isEmpty() === false) { $array['data'] = $this->resource->toArray(); } From 2f660162419e2d1b6c3e99de1abf86d4a0c48bde Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 26 Dec 2025 19:39:15 +0100 Subject: [PATCH 09/12] readability --- src/Document.php | 5 ++++- src/objects/AbstractObject.php | 3 ++- src/objects/ErrorObject.php | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Document.php b/src/Document.php index 7633e5ff..d699b06b 100644 --- a/src/Document.php +++ b/src/Document.php @@ -30,7 +30,10 @@ * @see ResourceDocument, CollectionDocument, ErrorsDocument or MetaDocument */ abstract class Document implements DocumentInterface, \JsonSerializable, HasLinksInterface, HasMetaInterface, HasExtensionMembersInterface { - use AtMemberManager, ExtensionMemberManager, HttpStatusCodeManager, LinksManager { + use AtMemberManager; + use ExtensionMemberManager; + use HttpStatusCodeManager; + use LinksManager { LinksManager::addLink as linkManagerAddLink; } diff --git a/src/objects/AbstractObject.php b/src/objects/AbstractObject.php index 41ddc512..6573448b 100644 --- a/src/objects/AbstractObject.php +++ b/src/objects/AbstractObject.php @@ -10,5 +10,6 @@ use alsvanzelf\jsonapi\interfaces\ObjectInterface; abstract class AbstractObject implements ObjectInterface, HasExtensionMembersInterface { - use AtMemberManager, ExtensionMemberManager; + use AtMemberManager; + use ExtensionMemberManager; } diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index da7abc8b..15acdef2 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -14,7 +14,8 @@ use alsvanzelf\jsonapi\objects\AbstractObject; class ErrorObject extends AbstractObject implements HasLinksInterface, HasMetaInterface { - use HttpStatusCodeManager, LinksManager; + use HttpStatusCodeManager; + use LinksManager; protected string|int $id; protected string $code; From 60a28db5287ad6a2785b9f5fc3e39288d9773aff Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 26 Dec 2025 19:44:56 +0100 Subject: [PATCH 10/12] meta objects also adhere to jsonapi member names --- UPGRADE_2_TO_3.md | 6 ++++++ rector.php | 13 +++++++++---- src/Document.php | 6 +++--- src/MetaDocument.php | 2 +- src/ResourceDocument.php | 6 +++--- src/helpers/LinksManager.php | 2 +- src/interfaces/DocumentInterface.php | 2 +- src/interfaces/HasLinksInterface.php | 2 +- src/interfaces/ObjectInterface.php | 2 +- src/objects/ErrorObject.php | 4 ++-- src/objects/LinkObject.php | 2 +- src/objects/LinksObject.php | 4 ++-- src/objects/MetaObject.php | 4 ++-- src/objects/RelationshipObject.php | 10 +++++----- src/objects/RelationshipsObject.php | 2 +- src/objects/ResourceObject.php | 4 ++-- 16 files changed, 41 insertions(+), 30 deletions(-) diff --git a/UPGRADE_2_TO_3.md b/UPGRADE_2_TO_3.md index d2e331ef..8940a243 100644 --- a/UPGRADE_2_TO_3.md +++ b/UPGRADE_2_TO_3.md @@ -1,5 +1,11 @@ # Upgrade from library v2 to v3 +## Checking links, ... + +Objects more often use uninitialized properties. `has*()` helper methods have been added to support the new flow. + +- `$object->links` to `if ($object->hasLinks()) $object->links` + ## Interfaces When extending interfaces, you'll have to add method argument types and return types. diff --git a/rector.php b/rector.php index 97960dd9..de660fc6 100644 --- a/rector.php +++ b/rector.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; +use Rector\Php70\Rector\StmtsAwareInterface\IfIssetToCoalescingRector; use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector; // @see https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md for more rules @@ -16,16 +17,20 @@ ->withRules([ DeclareStrictTypesRector::class, ]) + ->withSkip([ + // better explicit readability + IfIssetToCoalescingRector::class, + ]) // tab-based indenting ->withIndent(indentChar: "\t", indentSize: 1) - - // slowly increase php version + + // lowest supported php version ->withPhpSets(php82: true) - + // slowly increase levels ->withTypeCoverageLevel(1) ->withDeadCodeLevel(1) - + // @todo add `->withPreparedSets()` once on a higher level with other rules ; diff --git a/src/Document.php b/src/Document.php index d699b06b..ba403119 100644 --- a/src/Document.php +++ b/src/Document.php @@ -89,7 +89,7 @@ public function __construct() { /** * if $meta is given, a LinkObject is added, otherwise a link string is added * - * @param array $meta + * @param array $meta * * @throws InputException if the $level is not DocumentLevelEnum::Root */ @@ -106,7 +106,7 @@ public function addLink(string $key, ?string $href, array $meta=[], DocumentLeve * * @note a LinkObject is added when extensions or profiles are applied * - * @param array $meta if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ public function setSelfLink(string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($level === DocumentLevelEnum::Root && ($this->extensions !== [] || $this->profiles !== [])) { @@ -129,7 +129,7 @@ public function setSelfLink(string $href, array $meta=[], DocumentLevelEnum $lev * * @note according to the spec, this can only be set to DocumentLevelEnum::Root * - * @param array $meta if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ public function setDescribedByLink(string $href, array $meta=[]): void { $this->addLink('describedby', $href, $meta, $level=DocumentLevelEnum::Root); diff --git a/src/MetaDocument.php b/src/MetaDocument.php index 4302845f..22a18160 100644 --- a/src/MetaDocument.php +++ b/src/MetaDocument.php @@ -19,7 +19,7 @@ class MetaDocument extends Document { */ /** - * @param array $meta + * @param array $meta */ public static function fromArray(array $meta): self { $metaDocument = new self(); diff --git a/src/ResourceDocument.php b/src/ResourceDocument.php index 0b4b525e..6af66475 100644 --- a/src/ResourceDocument.php +++ b/src/ResourceDocument.php @@ -100,7 +100,7 @@ public function add(string $key, mixed $value, array $options=[]): void { * * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation * @param array $links - * @param array $meta + * @param array $meta * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ public function addRelationship( @@ -126,7 +126,7 @@ public function addRelationship( /** * if $meta is given, a LinkObject is added, otherwise a link string is added * - * @param array $meta + * @param array $meta */ public function addLink(string $key, ?string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Root): void { if ($this->resource instanceof ResourceObject === false) { @@ -144,7 +144,7 @@ public function addLink(string $key, ?string $href, array $meta=[], DocumentLeve /** * set the self link on the resource * - * @param array $meta + * @param array $meta */ public function setSelfLink(string $href, array $meta=[], DocumentLevelEnum $level=DocumentLevelEnum::Resource): void { if ($this->resource instanceof ResourceObject === false) { diff --git a/src/helpers/LinksManager.php b/src/helpers/LinksManager.php index feb0d5f6..514302e5 100644 --- a/src/helpers/LinksManager.php +++ b/src/helpers/LinksManager.php @@ -19,7 +19,7 @@ trait LinksManager { * * if $meta is given, a LinkObject is added, otherwise a link string is added * - * @param array $meta + * @param array $meta */ public function addLink(string $key, ?string $href, array $meta=[]): void { $this->ensureLinksObject(); diff --git a/src/interfaces/DocumentInterface.php b/src/interfaces/DocumentInterface.php index db3f987f..e9612b67 100644 --- a/src/interfaces/DocumentInterface.php +++ b/src/interfaces/DocumentInterface.php @@ -10,7 +10,7 @@ interface DocumentInterface { /** * generate array with the contents of the document, used by {@see ->toJson()} * - * @return array + * @return array */ public function toArray(): array; diff --git a/src/interfaces/HasLinksInterface.php b/src/interfaces/HasLinksInterface.php index ef0ecee7..69ecb31c 100644 --- a/src/interfaces/HasLinksInterface.php +++ b/src/interfaces/HasLinksInterface.php @@ -13,7 +13,7 @@ interface HasLinksInterface { * * if $meta is given, a LinkObject is added, otherwise a link string is added * - * @param array $meta + * @param array $meta */ public function addLink(string $key, ?string $href, array $meta=[]): void; diff --git a/src/interfaces/ObjectInterface.php b/src/interfaces/ObjectInterface.php index 0ff24856..0fc9c3a5 100644 --- a/src/interfaces/ObjectInterface.php +++ b/src/interfaces/ObjectInterface.php @@ -17,7 +17,7 @@ public function isEmpty(): bool; * * @internal * - * @return array + * @return array */ public function toArray(): array; } diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index 15acdef2..7ef4429a 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -145,7 +145,7 @@ public function setHumanExplanation( /** * set the link about this specific occurence of the error, explained in a human-friendly way * - * @param array $meta if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ public function setAboutLink(string $href, array $meta=[]): void { $this->addLink('about', $href, $meta); @@ -154,7 +154,7 @@ public function setAboutLink(string $href, array $meta=[]): void { /** * set the link of the generic type of this error, explained in a human-friendly way * - * @param array $meta if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ public function setTypeLink(string $href, array $meta=[]): void { $this->addLink('type', $href, $meta); diff --git a/src/objects/LinkObject.php b/src/objects/LinkObject.php index 45b5a243..0c483bef 100644 --- a/src/objects/LinkObject.php +++ b/src/objects/LinkObject.php @@ -19,7 +19,7 @@ class LinkObject extends AbstractObject implements HasMetaInterface { protected MetaObject $meta; /** - * @param array $meta + * @param array $meta */ public function __construct(?string $href=null, array $meta=[]) { if ($href !== null) { diff --git a/src/objects/LinksObject.php b/src/objects/LinksObject.php index 428e0659..65385ef7 100644 --- a/src/objects/LinksObject.php +++ b/src/objects/LinksObject.php @@ -11,7 +11,7 @@ use alsvanzelf\jsonapi\objects\LinkObject; class LinksObject extends AbstractObject { - /** @var array */ + /** @var array */ protected array $links = []; /** @@ -38,7 +38,7 @@ public static function fromObject(object $links): LinksObject { } /** - * @param array $meta if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ public function add(string $key, ?string $href, array $meta=[]): void { if ($meta === []) { diff --git a/src/objects/MetaObject.php b/src/objects/MetaObject.php index 8aecd913..e52c5847 100644 --- a/src/objects/MetaObject.php +++ b/src/objects/MetaObject.php @@ -9,7 +9,7 @@ use alsvanzelf\jsonapi\objects\AbstractObject; class MetaObject extends AbstractObject { - /** @var array */ + /** @var array */ protected array $meta = []; /** @@ -17,7 +17,7 @@ class MetaObject extends AbstractObject { */ /** - * @param array $meta + * @param array $meta */ public static function fromArray(array $meta): self { $metaObject = new self(); diff --git a/src/objects/RelationshipObject.php b/src/objects/RelationshipObject.php index c3b8eca4..2a4af1a0 100644 --- a/src/objects/RelationshipObject.php +++ b/src/objects/RelationshipObject.php @@ -39,7 +39,7 @@ public function __construct( * * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation * @param array $links - * @param array $meta + * @param array $meta * * @throws InputException if $relation is not one of the supported formats */ @@ -69,8 +69,8 @@ public static function fromAnything( } /** - * @param array $links - * @param array $meta + * @param array $links + * @param array $meta */ public static function fromResource( ResourceInterface $resource, @@ -96,8 +96,8 @@ public static function fromResource( } /** - * @param array $links - * @param array $meta + * @param array $links + * @param array $meta */ public static function fromCollectionDocument(CollectionDocument $collectionDocument, array $links=[], array $meta=[]): self { $relationshipObject = new self(RelationshipTypeEnum::ToMany); diff --git a/src/objects/RelationshipsObject.php b/src/objects/RelationshipsObject.php index c1d42746..54edd3e0 100644 --- a/src/objects/RelationshipsObject.php +++ b/src/objects/RelationshipsObject.php @@ -25,7 +25,7 @@ class RelationshipsObject extends AbstractObject implements RecursiveResourceCon /** * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation * @param array $links - * @param array $meta + * @param array $meta */ public function add( string $key, diff --git a/src/objects/ResourceObject.php b/src/objects/ResourceObject.php index 4be34ef8..87adb644 100644 --- a/src/objects/ResourceObject.php +++ b/src/objects/ResourceObject.php @@ -89,7 +89,7 @@ public function add(string $key, mixed $value, array $options=[]): void { /** * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation * @param array $links - * @param array $meta + * @param array $meta * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public function addRelationship( @@ -107,7 +107,7 @@ public function addRelationship( } /** - * @param array $meta if given a LinkObject is added, otherwise a link string is added + * @param array $meta if given a LinkObject is added, otherwise a link string is added */ public function setSelfLink(string $href, array $meta=[]): void { $this->addLink('self', $href, $meta); From 9261cbc422bb6307309434b478b925c7d5b9e681 Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 26 Dec 2025 19:50:58 +0100 Subject: [PATCH 11/12] make more clear where to find the type custom definitions --- phpstan.neon | 2 +- src/CollectionDocument.php | 4 ++-- src/Document.php | 2 +- src/ErrorsDocument.php | 12 ++++++------ src/ResourceDocument.php | 20 ++++++++++---------- src/helpers/RequestParser.php | 6 +++--- src/helpers/Validator.php | 6 +++--- src/interfaces/DocumentInterface.php | 4 ++-- src/interfaces/HasAttributesInterface.php | 2 +- src/objects/ErrorObject.php | 4 ++-- src/objects/ResourceObject.php | 16 ++++++++-------- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 992b5b7d..68fc146e 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,7 +7,7 @@ parameters: - examples/ typeAliases: - TypeAlias_InternalOptions: 'array' + PHPStanTypeAlias_InternalOptions: 'array' treatPhpDocTypesAsCertain: false strictRules: diff --git a/src/CollectionDocument.php b/src/CollectionDocument.php index fd8935b0..29f3d435 100644 --- a/src/CollectionDocument.php +++ b/src/CollectionDocument.php @@ -21,7 +21,7 @@ class CollectionDocument extends DataDocument implements PaginableInterface, ResourceContainerInterface { /** @var ResourceInterface[] */ protected array $resources = []; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * add resources inside relationships to /included when adding resources to the collection @@ -89,7 +89,7 @@ public function setPaginationLinks( * * adds included resources if found inside the resource's relationships, unless $options['includeContainedResources'] is set to false * - * @param TypeAlias_InternalOptions $options {@see CollectionDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see CollectionDocument::$defaults} * * @throws InputException if the resource is empty */ diff --git a/src/Document.php b/src/Document.php index ba403119..e95a4cc4 100644 --- a/src/Document.php +++ b/src/Document.php @@ -43,7 +43,7 @@ abstract class Document implements DocumentInterface, \JsonSerializable, HasLink protected array $extensions = []; /** @var ProfileInterface[] */ protected array $profiles = []; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * encode to json with these default options diff --git a/src/ErrorsDocument.php b/src/ErrorsDocument.php index 549f046a..3e905b44 100644 --- a/src/ErrorsDocument.php +++ b/src/ErrorsDocument.php @@ -15,7 +15,7 @@ class ErrorsDocument extends Document { protected array $errors = []; /** @var array> */ protected array $httpStatusCodes; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * add the trace of exceptions when adding exceptions @@ -42,7 +42,7 @@ public function __construct(?ErrorObject $errorObject=null) { */ /** - * @param TypeAlias_InternalOptions $options {@see ErrorsDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ErrorsDocument::$defaults} */ public static function fromException(\Throwable $exception, array $options=[]): self { $options = array_merge(self::$defaults, $options); @@ -58,7 +58,7 @@ public static function fromException(\Throwable $exception, array $options=[]): * * recursively adds multiple ErrorObjects if $exception carries a ->getPrevious() * - * @param TypeAlias_InternalOptions $options {@see ErrorsDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ErrorsDocument::$defaults} */ public function addException(\Throwable $exception, array $options=[]): void { $options = array_merge(self::$defaults, $options); @@ -77,9 +77,9 @@ public function addException(\Throwable $exception, array $options=[]): void { /** * @param string|int $genericCode developer-friendly code of the generic type of error * @param string $genericTitle human-friendly title of the generic type of error - * @param string $specificDetails optional, human-friendly explanation of the specific error - * @param string $specificAboutLink optional, human-friendly explanation of the specific error - * @param string $genericTypeLink optional, human-friendly explanation of the generic type of error + * @param string $specificDetails human-friendly explanation of the specific error + * @param string $specificAboutLink human-friendly explanation of the specific error + * @param string $genericTypeLink human-friendly explanation of the generic type of error */ public function add( string|int $genericCode, diff --git a/src/ResourceDocument.php b/src/ResourceDocument.php index 6af66475..e4da6f13 100644 --- a/src/ResourceDocument.php +++ b/src/ResourceDocument.php @@ -27,7 +27,7 @@ */ class ResourceDocument extends DataDocument implements HasAttributesInterface, ResourceInterface { protected ResourceIdentifierObject|ResourceObject $resource; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * add resources inside relationships to /included when adding resources to the collection @@ -51,7 +51,7 @@ public function __construct(?string $type=null, string|int|null $id=null) { */ /** - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} {@see ResourceObject::$defaults} */ public static function fromArray( array $attributes, @@ -66,7 +66,7 @@ public static function fromArray( } /** - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ public static function fromObject( object $attributes, @@ -82,8 +82,8 @@ public static function fromObject( /** * add key-value pairs to the resource's attributes * - * @param mixed $value objects will be converted using `get_object_vars()` - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} + * @param mixed $value objects will be converted using `get_object_vars()` + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ public function add(string $key, mixed $value, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { @@ -101,7 +101,7 @@ public function add(string $key, mixed $value, array $options=[]): void { * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation * @param array $links * @param array $meta - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ public function addRelationship( string $key, @@ -191,7 +191,7 @@ public function setLocalId(string|int $localId): void { } /** - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public function setAttributesObject(AttributesObject $attributesObject, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { @@ -206,7 +206,7 @@ public function setAttributesObject(AttributesObject $attributesObject, array $o * * adds included resources if found inside the RelationshipObject, unless $options['includeContainedResources'] is set to false * - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ public function addRelationshipObject(string $key, RelationshipObject $relationshipObject, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { @@ -227,7 +227,7 @@ public function addRelationshipObject(string $key, RelationshipObject $relations * * adds included resources if found inside the RelationshipObjects inside the RelationshipsObject, unless $options['includeContainedResources'] is set to false * - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} */ public function setRelationshipsObject(RelationshipsObject $relationshipsObject, array $options=[]): void { if ($this->resource instanceof ResourceObject === false) { @@ -252,7 +252,7 @@ public function setRelationshipsObject(RelationshipsObject $relationshipsObject, * * adds included resources if found inside the resource's relationships, unless $options['includeContainedResources'] is set to false * - * @param TypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceDocument::$defaults} * * @throws InputException if the $resource is a ResourceDocument itself */ diff --git a/src/helpers/RequestParser.php b/src/helpers/RequestParser.php index e250dafa..3348821c 100644 --- a/src/helpers/RequestParser.php +++ b/src/helpers/RequestParser.php @@ -10,7 +10,7 @@ use Psr\Http\Message\ServerRequestInterface; class RequestParser { - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * reformat the include query parameter paths to nested arrays @@ -105,7 +105,7 @@ public function hasIncludePaths(): bool { * the nested format allows easier processing on each step of the chain * the raw format allows for custom processing * - * @param TypeAlias_InternalOptions $options {@see RequestParser::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see RequestParser::$defaults} * @return string[]|array */ public function getIncludePaths(array $options=[]): array { @@ -163,7 +163,7 @@ public function hasSortFields(): bool { * * @todo return some kind of SortFieldObject * - * @param TypeAlias_InternalOptions $options {@see RequestParser::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see RequestParser::$defaults} * @return string[]|array */ protected array $usedResourceIdentifiers = []; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * blocks 'type' as a keyword inside attributes or relationships @@ -32,8 +32,8 @@ class Validator { * * @see https://jsonapi.org/format/1.1/#document-resource-object-fields * - * @param string[] $fieldNames - * @param TypeAlias_InternalOptions $options {@see Validator::$defaults} + * @param string[] $fieldNames + * @param PHPStanTypeAlias_InternalOptions $options {@see Validator::$defaults} * * @throws DuplicateException */ diff --git a/src/interfaces/DocumentInterface.php b/src/interfaces/DocumentInterface.php index e9612b67..fc80a4a0 100644 --- a/src/interfaces/DocumentInterface.php +++ b/src/interfaces/DocumentInterface.php @@ -17,7 +17,7 @@ public function toArray(): array; /** * generate json with the contents of the document, used by {@see ->sendResponse()} * - * @param TypeAlias_InternalOptions $options + * @param PHPStanTypeAlias_InternalOptions $options * * @throws Exception if generating json fails */ @@ -28,7 +28,7 @@ public function toJson(array $options=[]): string; * * @note will set http status code and content type, and echo json * - * @param TypeAlias_InternalOptions $options + * @param PHPStanTypeAlias_InternalOptions $options */ public function sendResponse(array $options=[]): void; } diff --git a/src/interfaces/HasAttributesInterface.php b/src/interfaces/HasAttributesInterface.php index 4b3fd32f..f42b426b 100644 --- a/src/interfaces/HasAttributesInterface.php +++ b/src/interfaces/HasAttributesInterface.php @@ -10,7 +10,7 @@ interface HasAttributesInterface { * * @see ResourceObject::$defaults * - * @param TypeAlias_InternalOptions $options + * @param PHPStanTypeAlias_InternalOptions $options */ public function addAttribute(string $key, mixed $value, array $options=[]): void; } diff --git a/src/objects/ErrorObject.php b/src/objects/ErrorObject.php index 7ef4429a..26e6ce73 100644 --- a/src/objects/ErrorObject.php +++ b/src/objects/ErrorObject.php @@ -24,7 +24,7 @@ class ErrorObject extends AbstractObject implements HasLinksInterface, HasMetaIn /** @var array{pointer?: string, parameter?: string, header?: string} */ protected array $source = []; protected MetaObject $meta; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * add the trace of exceptions when adding exceptions @@ -66,7 +66,7 @@ public function __construct( */ /** - * @param TypeAlias_InternalOptions $options {@see ErrorObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ErrorObject::$defaults} */ public static function fromException(\Throwable $exception, array $options=[]): self { $options = array_merge(self::$defaults, $options); diff --git a/src/objects/ResourceObject.php b/src/objects/ResourceObject.php index 87adb644..67a4478e 100644 --- a/src/objects/ResourceObject.php +++ b/src/objects/ResourceObject.php @@ -23,7 +23,7 @@ class ResourceObject extends ResourceIdentifierObject implements HasAttributesIn protected AttributesObject $attributes; protected RelationshipsObject $relationships; - /** @var TypeAlias_InternalOptions */ + /** @var PHPStanTypeAlias_InternalOptions */ protected static array $defaults = [ /** * blocks 'type' as a keyword inside attributes or relationships @@ -42,8 +42,8 @@ class ResourceObject extends ResourceIdentifierObject implements HasAttributesIn * and if $id is null, it is filled with that value * it is common to find it inside, and not doing so will cause an exception * - * @param array $attributes - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param array $attributes + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public static function fromArray(array $attributes, ?string $type=null, string|int|null $id=null, array $options=[]): self { if (isset($attributes['id'])) { @@ -61,7 +61,7 @@ public static function fromArray(array $attributes, ?string $type=null, string|i } /** - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public static function fromObject(object $attributes, ?string $type=null, string|int|null $id=null, array $options=[]): self { $array = Converter::objectToArray($attributes); @@ -72,7 +72,7 @@ public static function fromObject(object $attributes, ?string $type=null, string /** * add key-value pairs to attributes * - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public function add(string $key, mixed $value, array $options=[]): void { $options = array_merge(self::$defaults, $options); @@ -90,7 +90,7 @@ public function add(string $key, mixed $value, array $options=[]): void { * @param CollectionDocument|ResourceInterface|ResourceInterface[]|null $relation * @param array $links * @param array $meta - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public function addRelationship( string $key, @@ -118,7 +118,7 @@ public function setSelfLink(string $href, array $meta=[]): void { */ /** - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} */ public function setAttributesObject(AttributesObject $attributesObject, array $options=[]): void { $newKeys = $attributesObject->getKeys(); @@ -129,7 +129,7 @@ public function setAttributesObject(AttributesObject $attributesObject, array $o } /** - * @param TypeAlias_InternalOptions $options {@see ResourceObject::$defaults} + * @param PHPStanTypeAlias_InternalOptions $options {@see ResourceObject::$defaults} * * @throws DuplicateException if the resource is contained as a resource in the relationship */ From c458c398022931c18f21a97e499f65c1c88c440c Mon Sep 17 00:00:00 2001 From: Lode Claassen Date: Fri, 26 Dec 2025 20:13:46 +0100 Subject: [PATCH 12/12] CS --- phpstan.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.neon b/phpstan.neon index 68fc146e..a5a5f410 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -8,6 +8,7 @@ parameters: typeAliases: PHPStanTypeAlias_InternalOptions: 'array' + treatPhpDocTypesAsCertain: false strictRules: