From 88ea30fa6857f87831e9801430305cf7395d96f9 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 00:56:15 +0200 Subject: [PATCH 01/17] Require PHP 8.1, test on PHP 8.4 --- .github/workflows/ci.yml | 9 +++------ README.md | 2 +- composer.json | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 347cf26..41e3952 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: pull_request: env: - COVERAGE_PHP_VERSION: "8.3" + COVERAGE_PHP_VERSION: "8.4" jobs: phpunit: @@ -16,17 +16,14 @@ jobs: fail-fast: false matrix: php-version: - - "7.2" - - "7.3" - - "7.4" - - "8.0" - "8.1" - "8.2" - "8.3" + - "8.4" deps: - "highest" include: - - php-version: "7.2" + - php-version: "8.1" deps: "lowest" steps: diff --git a/README.md b/README.md index 6babfb0..bd500da 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ composer require brick/structured-data ### Requirements -This library requires PHP 7.2 or later. It makes use of the following extensions: +This library requires PHP 8.1 or later. It makes use of the following extensions: - [dom](https://www.php.net/manual/en/book.dom.php) - [json](https://www.php.net/manual/en/book.json.php) diff --git a/composer.json b/composer.json index 6866213..c226a6d 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ ], "license": "MIT", "require": { - "php": "^7.2 || ^8.0", + "php": "^8.1", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", From 80b916a231ecf6787d72e30872f6ab5844efabc3 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 00:51:17 +0200 Subject: [PATCH 02/17] Add Psalm --- .github/workflows/ci.yml | 20 ++++ composer.json | 3 +- psalm-baseline.xml | 215 +++++++++++++++++++++++++++++++++++++++ psalm.xml | 19 ++++ 4 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 psalm-baseline.xml create mode 100644 psalm.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41e3952..42098de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,29 @@ on: pull_request: env: + PSALM_PHP_VERSION: "8.4" COVERAGE_PHP_VERSION: "8.4" jobs: + psalm: + name: Psalm + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PSALM_PHP_VERSION }} + + - name: Install composer dependencies + uses: ramsey/composer-install@v3 + + - name: Run Psalm + run: vendor/bin/psalm --no-progress + phpunit: name: PHPUnit runs-on: ubuntu-latest diff --git a/composer.json b/composer.json index c226a6d..e502118 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ }, "require-dev": { "phpunit/phpunit": "^8.0 || ^9.0", - "php-coveralls/php-coveralls": "^2.0" + "php-coveralls/php-coveralls": "^2.0", + "vimeo/psalm": "6.12.0" }, "autoload": { "psr-4": { diff --git a/psalm-baseline.xml b/psalm-baseline.xml new file mode 100644 index 0000000..95b079e --- /dev/null +++ b/psalm-baseline.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + readJson($node->textContent, $url); + }]]> + + + + + + + + + + + + + + parentNode; + + if ($itemprop->isSameNode($node)) { + return true; + } + + if ($itemprop->attributes->getNamedItem('itemscope')) { + return false; + } + } + + // Unreachable, but makes static analysis happy + return false; + }]]> + + + + + + parentNode; + + if ($itemprop->isSameNode($node)) { + return true; + } + + if ($itemprop->attributes->getNamedItem('itemscope')) { + return false; + } + } + + // Unreachable, but makes static analysis happy + return false; + }]]> + nodeToItem($node, $xpath, $url); + }]]> + + + + textContent)]]> + + + attributes->getNamedItem('itemprop')->textContent]]> + + + + + + + + + + + + + + + parentNode; + + if ($itemprop->isSameNode($node)) { + return true; + } + + if ($itemprop->attributes->getNamedItem('typeof')) { + return false; + } + } + + // Unreachable, but makes static analysis happy + return false; + }]]> + + + + + + parentNode; + + if ($itemprop->isSameNode($node)) { + return true; + } + + if ($itemprop->attributes->getNamedItem('typeof')) { + return false; + } + } + + // Unreachable, but makes static analysis happy + return false; + }]]> + nodeToItem($node, $xpath, $url, self::PREDEFINED_PREFIXES, null); + }]]> + + + + textContent]]> + textContent)]]> + + + attributes->getNamedItem('property')->textContent]]> + textContent]]> + + + + + + + + + + + + + + + + + + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..cb64a11 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,19 @@ + + + + + + + + + From 0474533e5c04ec476163a2f5880cd8f47f2f8d34 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 00:57:54 +0200 Subject: [PATCH 03/17] Allow sabre/uri v3 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e502118..0827ad7 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", - "sabre/uri": "^2.1" + "sabre/uri": "^2.1 || ^3.0" }, "require-dev": { "phpunit/phpunit": "^8.0 || ^9.0", From a048ea66481024b49dd45650a4c8313a86d41f4d Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:03:27 +0200 Subject: [PATCH 04/17] Avoid CI jobs running twice --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 42098de..c350ee1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: CI on: push: + branches: [master] pull_request: env: From 205346a77c6949bef12ca01c3e2d09a996c3b18a Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:04:03 +0200 Subject: [PATCH 05/17] Final classes --- psalm-baseline.xml | 28 ---------------------------- src/DOMBuilder.php | 2 +- src/HTMLReader.php | 2 +- src/Item.php | 2 +- src/JsonLdWriter.php | 2 +- src/Reader/JsonLdReader.php | 2 +- src/Reader/MicrodataReader.php | 2 +- src/Reader/RdfaLiteReader.php | 2 +- src/Reader/ReaderChain.php | 2 +- 9 files changed, 8 insertions(+), 36 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 95b079e..3d2a76e 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,27 +1,11 @@ - - - - - - - - - - - - - - - - extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)]]> @@ -38,9 +22,6 @@ - - - @@ -80,9 +61,6 @@ - - - - - - - - - diff --git a/src/DOMBuilder.php b/src/DOMBuilder.php index f1fbddb..78bc882 100644 --- a/src/DOMBuilder.php +++ b/src/DOMBuilder.php @@ -6,7 +6,7 @@ use DOMDocument; -class DOMBuilder +final class DOMBuilder { /** * Builds a DOMDocument from an HTML string. diff --git a/src/HTMLReader.php b/src/HTMLReader.php index 529d4a2..451691d 100644 --- a/src/HTMLReader.php +++ b/src/HTMLReader.php @@ -4,7 +4,7 @@ namespace Brick\StructuredData; -class HTMLReader +final class HTMLReader { /** * @var Reader diff --git a/src/Item.php b/src/Item.php index 0255fcd..b37dbe3 100644 --- a/src/Item.php +++ b/src/Item.php @@ -9,7 +9,7 @@ /** * An item, such as a Thing in schema.org's vocabulary. */ -class Item +final class Item { /** * The global identifier of the item, if any. diff --git a/src/JsonLdWriter.php b/src/JsonLdWriter.php index 2cdfe87..9fc6e21 100644 --- a/src/JsonLdWriter.php +++ b/src/JsonLdWriter.php @@ -7,7 +7,7 @@ /** * Exports Items to JSON-LD. */ -class JsonLdWriter +final class JsonLdWriter { /** * Exports a list of Items as JSON-LD. diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index e0e42ef..4863e36 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -29,7 +29,7 @@ * * https://json-ld.org/spec/latest/json-ld/ */ -class JsonLdReader implements Reader +final class JsonLdReader implements Reader { /** * @var string[] diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index 1f1e692..4fea5ee 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -21,7 +21,7 @@ * * @todo support for the itemref attribute */ -class MicrodataReader implements Reader +final class MicrodataReader implements Reader { /** * @inheritDoc diff --git a/src/Reader/RdfaLiteReader.php b/src/Reader/RdfaLiteReader.php index ffde4ac..95dd111 100644 --- a/src/Reader/RdfaLiteReader.php +++ b/src/Reader/RdfaLiteReader.php @@ -23,7 +23,7 @@ * * @todo support for the prefix attribute; only predefined prefixes are supported right now */ -class RdfaLiteReader implements Reader +final class RdfaLiteReader implements Reader { /** * The predefined RDFa prefixes. diff --git a/src/Reader/ReaderChain.php b/src/Reader/ReaderChain.php index 08c7e1e..cbc3511 100644 --- a/src/Reader/ReaderChain.php +++ b/src/Reader/ReaderChain.php @@ -11,7 +11,7 @@ /** * Chains several schema readers and returns the aggregate results. */ -class ReaderChain implements Reader +final class ReaderChain implements Reader { /** * @var Reader[] From ffeddd63fcfdb8177f8a265e8145a59f37f12788 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:07:25 +0200 Subject: [PATCH 06/17] Fix typos --- src/Item.php | 2 +- src/Reader/JsonLdReader.php | 2 +- src/Reader/RdfaLiteReader.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Item.php b/src/Item.php index b37dbe3..0ff75b9 100644 --- a/src/Item.php +++ b/src/Item.php @@ -57,7 +57,7 @@ public function getId() : ?string /** * Returns the list of types this Item implements. * - * Each type is a represented as a URL, e.g. http://schema.org/Product . + * Each type is represented as a URL, e.g. http://schema.org/Product . * * @return array */ diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index 4863e36..c4a4b8e 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -230,7 +230,7 @@ private function resolveTerm(string $term, ?string $vocabulary) * @param string $name The property name. * @param mixed $value The property value. Any JSON type. * @param string $url The URL the document was retrieved from, for relative URL resolution. - * @param string|null $vocabulary The currently vocabulary URL, if any. + * @param string|null $vocabulary The current vocabulary URL, if any. * * @return Item|string|null The value, or NULL if the input value is NULL or an array. */ diff --git a/src/Reader/RdfaLiteReader.php b/src/Reader/RdfaLiteReader.php index 95dd111..82ebc71 100644 --- a/src/Reader/RdfaLiteReader.php +++ b/src/Reader/RdfaLiteReader.php @@ -92,7 +92,7 @@ public function read(DOMDocument $document, string $url) : array $xpath = new DOMXPath($document); /** - * Top-level item have a typeof attribute and no property attribute. + * Top-level item has a typeof attribute and no property attribute. */ $nodes = $xpath->query('//*[@typeof and not(@property)]'); $nodes = iterator_to_array($nodes); @@ -119,7 +119,7 @@ private function nodeToItem(DOMNode $node, DOMXPath $xpath, string $url, array $ $vocabulary = $this->updateVocabulary($node, $vocabulary); /** - * The resource attribute holds the item identifier, than must be resolved relative to the current URL. + * The resource attribute holds the item identifier, that must be resolved relative to the current URL. * * https://www.w3.org/TR/rdfa-lite/#resource */ From 62a5cf15e80d8fa97517f351ce4d001cc3228567 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:15:38 +0200 Subject: [PATCH 07/17] Add types --- psalm-baseline.xml | 5 ----- src/HTMLReader.php | 5 +---- src/Item.php | 18 ++++++------------ src/JsonLdWriter.php | 2 +- src/Reader/JsonLdReader.php | 4 ++-- src/Reader/MicrodataReader.php | 4 +--- src/Reader/RdfaLiteReader.php | 4 +--- src/Reader/ReaderChain.php | 2 +- 8 files changed, 13 insertions(+), 31 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3d2a76e..d8bbae0 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,10 +1,5 @@ - - - - - extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)]]> diff --git a/src/HTMLReader.php b/src/HTMLReader.php index 451691d..e5bf179 100644 --- a/src/HTMLReader.php +++ b/src/HTMLReader.php @@ -6,10 +6,7 @@ final class HTMLReader { - /** - * @var Reader - */ - private $reader; + private Reader $reader; /** * HTMLReader constructor. diff --git a/src/Item.php b/src/Item.php index 0ff75b9..60ba643 100644 --- a/src/Item.php +++ b/src/Item.php @@ -13,24 +13,22 @@ final class Item { /** * The global identifier of the item, if any. - * - * @var string|null */ - private $id; + private ?string $id; /** * The types this Item implements, as URLs. * - * @var array + * @var string[] */ - private $types; + private array $types; /** * The properties, as a map of property name to list of values. * * @var array> */ - private $properties = []; + private array $properties = []; /** * Item constructor. @@ -59,7 +57,7 @@ public function getId() : ?string * * Each type is represented as a URL, e.g. http://schema.org/Product . * - * @return array + * @return string[] */ public function getTypes() : array { @@ -100,12 +98,8 @@ public function getProperty(string $name) : array * * @return void */ - public function addProperty(string $name, $value) : void + public function addProperty(string $name, Item|string $value) : void { - if (! $value instanceof Item && ! is_string($value)) { - throw new TypeError(sprintf('Property value must be an instance of %s or a string.', Item::class)); - } - $this->properties[$name][] = $value; } } diff --git a/src/JsonLdWriter.php b/src/JsonLdWriter.php index 9fc6e21..84cd0c6 100644 --- a/src/JsonLdWriter.php +++ b/src/JsonLdWriter.php @@ -62,7 +62,7 @@ private function convertItem(Item $item) : array * * @return mixed */ - private function extractIfSingle(array $values) + private function extractIfSingle(array $values) : mixed { if (count($values) === 1) { return $values[0]; diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index c4a4b8e..8723702 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -34,7 +34,7 @@ final class JsonLdReader implements Reader /** * @var string[] */ - private $iriProperties; + private array $iriProperties; /** * JsonLdReader constructor. @@ -234,7 +234,7 @@ private function resolveTerm(string $term, ?string $vocabulary) * * @return Item|string|null The value, or NULL if the input value is NULL or an array. */ - private function getPropertyValue(string $name, $value, string $url, ?string $vocabulary) + private function getPropertyValue(string $name, mixed $value, string $url, ?string $vocabulary) : Item|string|null { if (is_string($value)) { if (in_array($name, $this->iriProperties, true)) { diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index 4fea5ee..3b9709e 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -158,10 +158,8 @@ private function nodeToItem(DOMNode $node, DOMXPath $xpath, string $url) : Item * @param DOMNode $node A DOMNode representing an element with the itemprop attribute. * @param DOMXPath $xpath A DOMXPath object created from the node's document element. * @param string $url The URL the document was retrieved from, for relative URL resolution. - * - * @return Item|string */ - private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url) + private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url) : Item|string { /** * If the element also has an itemscope attribute: the value is the item created by the element. diff --git a/src/Reader/RdfaLiteReader.php b/src/Reader/RdfaLiteReader.php index 82ebc71..a9faa03 100644 --- a/src/Reader/RdfaLiteReader.php +++ b/src/Reader/RdfaLiteReader.php @@ -319,10 +319,8 @@ private function checkVocabularyUrl(string $url) : ?string * @param string $url The URL the document was retrieved from, for relative URL resolution. * @param string[] $prefixes The prefixes in use, as a map of prefix to vocabulary URL. * @param string|null $vocabulary The URL of the vocabulary in use, if any. - * - * @return Item|string */ - private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url, array $prefixes, ?string $vocabulary) + private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url, array $prefixes, ?string $vocabulary) : Item|string { // If the element also has an typeof attribute, create an item from the element $attr = $node->attributes->getNamedItem('typeof'); diff --git a/src/Reader/ReaderChain.php b/src/Reader/ReaderChain.php index cbc3511..7ef712c 100644 --- a/src/Reader/ReaderChain.php +++ b/src/Reader/ReaderChain.php @@ -16,7 +16,7 @@ final class ReaderChain implements Reader /** * @var Reader[] */ - private $readers; + private array $readers; /** * ReaderChain constructor. From 0b79cd298e20b4326198b21753afc451076d9c42 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:17:35 +0200 Subject: [PATCH 08/17] Clean up unused import --- src/Item.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Item.php b/src/Item.php index 60ba643..0bc374a 100644 --- a/src/Item.php +++ b/src/Item.php @@ -4,8 +4,6 @@ namespace Brick\StructuredData; -use TypeError; - /** * An item, such as a Thing in schema.org's vocabulary. */ From ce28c2c9f429e052a956bf7f1b7060dfb2e54b3c Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:18:35 +0200 Subject: [PATCH 09/17] Readonly properties --- src/HTMLReader.php | 2 +- src/Item.php | 4 ++-- src/Reader/JsonLdReader.php | 2 +- src/Reader/ReaderChain.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/HTMLReader.php b/src/HTMLReader.php index e5bf179..d28c95b 100644 --- a/src/HTMLReader.php +++ b/src/HTMLReader.php @@ -6,7 +6,7 @@ final class HTMLReader { - private Reader $reader; + private readonly Reader $reader; /** * HTMLReader constructor. diff --git a/src/Item.php b/src/Item.php index 0bc374a..dea1a36 100644 --- a/src/Item.php +++ b/src/Item.php @@ -12,14 +12,14 @@ final class Item /** * The global identifier of the item, if any. */ - private ?string $id; + private readonly ?string $id; /** * The types this Item implements, as URLs. * * @var string[] */ - private array $types; + private readonly array $types; /** * The properties, as a map of property name to list of values. diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index 8723702..cce1011 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -34,7 +34,7 @@ final class JsonLdReader implements Reader /** * @var string[] */ - private array $iriProperties; + private readonly array $iriProperties; /** * JsonLdReader constructor. diff --git a/src/Reader/ReaderChain.php b/src/Reader/ReaderChain.php index 7ef712c..00fd511 100644 --- a/src/Reader/ReaderChain.php +++ b/src/Reader/ReaderChain.php @@ -16,7 +16,7 @@ final class ReaderChain implements Reader /** * @var Reader[] */ - private array $readers; + private readonly array $readers; /** * ReaderChain constructor. From 860a66edce2298c6156e73527ee6148b5be7ff84 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:21:12 +0200 Subject: [PATCH 10/17] Clean up unnecessary workaround --- psalm-baseline.xml | 6 ------ src/Reader/MicrodataReader.php | 3 --- 2 files changed, 9 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index d8bbae0..52137f8 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -69,9 +69,6 @@ return false; } } - - // Unreachable, but makes static analysis happy - return false; }]]> @@ -90,9 +87,6 @@ return false; } } - - // Unreachable, but makes static analysis happy - return false; }]]> nodeToItem($node, $xpath, $url); diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index 3b9709e..c026004 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -111,9 +111,6 @@ private function nodeToItem(DOMNode $node, DOMXPath $xpath, string $url) : Item return false; } } - - // Unreachable, but makes static analysis happy - return false; }); $vocabularyIdentifier = $this->getVocabularyIdentifier($types); From 8b88060f8d98e1e9eaf119593c264bdac3500bc7 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:23:45 +0200 Subject: [PATCH 11/17] Add #[Override] attribute --- psalm-baseline.xml | 14 -------------- src/Reader/JsonLdReader.php | 5 ++--- src/Reader/MicrodataReader.php | 5 ++--- src/Reader/RdfaLiteReader.php | 5 ++--- src/Reader/ReaderChain.php | 5 ++--- 5 files changed, 8 insertions(+), 26 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 52137f8..c8af861 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -23,9 +23,6 @@ - - - @@ -71,9 +68,6 @@ } }]]> - - - - - - - - - - - diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index cce1011..a2d4953 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -7,6 +7,7 @@ use Brick\StructuredData\Item; use Brick\StructuredData\Reader; +use Override; use stdClass; use DOMDocument; @@ -51,9 +52,7 @@ public function __construct(array $iriProperties = []) $this->iriProperties = $iriProperties; } - /** - * @inheritDoc - */ + #[Override] public function read(DOMDocument $document, string $url) : array { $xpath = new DOMXPath($document); diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index c026004..7d039d5 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -11,6 +11,7 @@ use DOMNode; use DOMXPath; +use Override; use Sabre\Uri\InvalidUriException; use function Sabre\Uri\resolve; @@ -23,9 +24,7 @@ */ final class MicrodataReader implements Reader { - /** - * @inheritDoc - */ + #[Override] public function read(DOMDocument $document, string $url) : array { $xpath = new DOMXPath($document); diff --git a/src/Reader/RdfaLiteReader.php b/src/Reader/RdfaLiteReader.php index a9faa03..87c047a 100644 --- a/src/Reader/RdfaLiteReader.php +++ b/src/Reader/RdfaLiteReader.php @@ -11,6 +11,7 @@ use DOMNode; use DOMXPath; +use Override; use Sabre\Uri\InvalidUriException; use function Sabre\Uri\resolve; use function Sabre\Uri\parse; @@ -84,9 +85,7 @@ final class RdfaLiteReader implements Reader 'xsd' => 'http://www.w3.org/2001/XMLSchema#', ]; - /** - * @inheritDoc - */ + #[Override] public function read(DOMDocument $document, string $url) : array { $xpath = new DOMXPath($document); diff --git a/src/Reader/ReaderChain.php b/src/Reader/ReaderChain.php index 00fd511..0d684bb 100644 --- a/src/Reader/ReaderChain.php +++ b/src/Reader/ReaderChain.php @@ -7,6 +7,7 @@ use Brick\StructuredData\Reader; use DOMDocument; +use Override; /** * Chains several schema readers and returns the aggregate results. @@ -28,9 +29,7 @@ public function __construct(Reader ...$readers) $this->readers = $readers; } - /** - * @inheritDoc - */ + #[Override] public function read(DOMDocument $document, string $url) : array { if (! $this->readers) { From 813b1ba5b180f095748aeadee68a28f2bc43133f Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:26:00 +0200 Subject: [PATCH 12/17] Use JSON_THROW_ON_ERROR --- psalm-baseline.xml | 6 ------ src/JsonLdWriter.php | 2 +- src/Reader/JsonLdReader.php | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index c8af861..8766b93 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,12 +1,6 @@ - - extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)]]> - - - - diff --git a/src/JsonLdWriter.php b/src/JsonLdWriter.php index 84cd0c6..e45c62c 100644 --- a/src/JsonLdWriter.php +++ b/src/JsonLdWriter.php @@ -22,7 +22,7 @@ public function write(Item ...$items) : string return $this->convertItem($item); }, $items); - return json_encode($this->extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + return json_encode($this->extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); } /** diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index a2d4953..1d08ae4 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -83,7 +83,7 @@ public function read(DOMDocument $document, string $url) : array */ private function readJson(string $json, string $url) : array { - $data = json_decode($json); + $data = json_decode($json, flags: JSON_THROW_ON_ERROR); if ($data === null) { return []; From 527cbe5d94a85de8326723cecf8395d46a4cb54a Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:31:14 +0200 Subject: [PATCH 13/17] Suppress Psalm's MixedAssignment --- psalm-baseline.xml | 13 ------------- psalm.xml | 4 ++++ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 8766b93..d00f594 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,10 +1,5 @@ - - - - - @@ -23,14 +18,6 @@ - - - - - - - - diff --git a/psalm.xml b/psalm.xml index cb64a11..b99bd54 100644 --- a/psalm.xml +++ b/psalm.xml @@ -16,4 +16,8 @@ + + + + From ac27dfb134ff57c772dac8d64c195fdcd138ba89 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:38:47 +0200 Subject: [PATCH 14/17] Clean up unnecessary variable in catch block --- src/Reader/JsonLdReader.php | 6 +++--- src/Reader/MicrodataReader.php | 6 +++--- src/Reader/RdfaLiteReader.php | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index 1d08ae4..2645044 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -133,7 +133,7 @@ private function readItem(stdClass $item, string $url, ?string $vocabulary) : It if (isset($item->{'@id'}) && is_string($item->{'@id'})) { try { $id = resolve($url, $item->{'@id'}); // always relative to the document URL, no support for @base - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { // ignore } } @@ -239,7 +239,7 @@ private function getPropertyValue(string $name, mixed $value, string $url, ?stri if (in_array($name, $this->iriProperties, true)) { try { $value = resolve($url, $value); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { // ignore } } @@ -273,7 +273,7 @@ private function checkVocabularyUrl(string $url) : ?string { try { $parts = parse($url); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return null; } diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index 7d039d5..0c2fc72 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -188,7 +188,7 @@ private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url) : if ($attr !== null) { try { return resolve($url, $attr->textContent); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return ''; } } @@ -207,7 +207,7 @@ private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url) : if ($attr !== null) { try { return resolve($url, $attr->textContent); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return ''; } } @@ -224,7 +224,7 @@ private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url) : if ($attr !== null) { try { return resolve($url, $attr->textContent); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return ''; } } diff --git a/src/Reader/RdfaLiteReader.php b/src/Reader/RdfaLiteReader.php index 87c047a..565acab 100644 --- a/src/Reader/RdfaLiteReader.php +++ b/src/Reader/RdfaLiteReader.php @@ -244,7 +244,7 @@ private function isValidAbsoluteURL(string $url) : bool { try { $parts = parse($url); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return false; } @@ -291,7 +291,7 @@ private function checkVocabularyUrl(string $url) : ?string { try { $parts = parse($url); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return null; } @@ -341,7 +341,7 @@ private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url, a if ($attr !== null) { try { return resolve($url, $attr->textContent); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return ''; } } @@ -352,7 +352,7 @@ private function getPropertyValue(DOMNode $node, DOMXPath $xpath, string $url, a if ($attr !== null) { try { return resolve($url, $attr->textContent); - } catch (InvalidUriException $e) { + } catch (InvalidUriException) { return ''; } } From 329c31a316b74970c471d741d35de243c54fb151 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:42:18 +0200 Subject: [PATCH 15/17] Short functions --- psalm-baseline.xml | 15 +++------------ src/JsonLdWriter.php | 7 ++++--- src/Reader/JsonLdReader.php | 21 ++++++++++++--------- src/Reader/MicrodataReader.php | 11 +++++------ src/Reader/RdfaLiteReader.php | 11 +++++------ 5 files changed, 29 insertions(+), 36 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index d00f594..16e2cec 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -18,13 +18,8 @@ - - - - readJson($node->textContent, $url); - }]]> + $this->readJson($node->textContent, $url)]]> @@ -50,6 +45,7 @@ }]]> + $this->nodeToItem($node, $xpath, $url)]]> parentNode; @@ -63,9 +59,6 @@ } } }]]> - nodeToItem($node, $xpath, $url); - }]]> @@ -102,6 +95,7 @@ }]]> + $this->nodeToItem($node, $xpath, $url, self::PREDEFINED_PREFIXES, null)]]> parentNode; @@ -118,9 +112,6 @@ // Unreachable, but makes static analysis happy return false; }]]> - nodeToItem($node, $xpath, $url, self::PREDEFINED_PREFIXES, null); - }]]> diff --git a/src/JsonLdWriter.php b/src/JsonLdWriter.php index e45c62c..50b6389 100644 --- a/src/JsonLdWriter.php +++ b/src/JsonLdWriter.php @@ -18,9 +18,10 @@ final class JsonLdWriter */ public function write(Item ...$items) : string { - $items = array_map(function(Item $item) { - return $this->convertItem($item); - }, $items); + $items = array_map( + fn(Item $item) => $this->convertItem($item), + $items, + ); return json_encode($this->extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR); } diff --git a/src/Reader/JsonLdReader.php b/src/Reader/JsonLdReader.php index 2645044..f949126 100644 --- a/src/Reader/JsonLdReader.php +++ b/src/Reader/JsonLdReader.php @@ -64,9 +64,10 @@ public function read(DOMDocument $document, string $url) : array return []; } - $items = array_map(function(DOMNode $node) use ($url) { - return $this->readJson($node->textContent, $url); - }, $nodes); + $items = array_map( + fn(DOMNode $node) => $this->readJson($node->textContent, $url), + $nodes, + ); return array_merge(...$items); } @@ -100,9 +101,10 @@ private function readJson(string $json, string $url) : array } if (is_array($data)) { - $items = array_map(function($item) use ($url) { - return is_object($item) ? $this->readItem($item, $url, null) : null; - }, $data); + $items = array_map( + fn($item) => is_object($item) ? $this->readItem($item, $url, null) : null, + $data, + ); $items = array_filter($items); $items = array_values($items); @@ -147,9 +149,10 @@ private function readItem(stdClass $item, string $url, ?string $vocabulary) : It $type = $this->resolveTerm($type, $vocabulary); $types = [$type]; } elseif (is_array($type)) { - $types = array_map(function($type) use ($vocabulary) { - return is_string($type) ? $this->resolveTerm($type, $vocabulary) : null; - }, $types); + $types = array_map( + fn($type) => is_string($type) ? $this->resolveTerm($type, $vocabulary) : null, + $types, + ); $types = array_filter($types); $types = array_values($types); diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index 0c2fc72..67556a2 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -37,9 +37,10 @@ public function read(DOMDocument $document, string $url) : array $nodes = $xpath->query('//*[@itemscope and not(@itemprop)]'); $nodes = iterator_to_array($nodes); - return array_map(function(DOMNode $node) use ($xpath, $url) { - return $this->nodeToItem($node, $xpath, $url); - }, $nodes); + return array_map( + fn(DOMNode $node) => $this->nodeToItem($node, $xpath, $url), + $nodes, + ); } /** @@ -83,9 +84,7 @@ private function nodeToItem(DOMNode $node, DOMXPath $xpath, string $url) : Item * If the itemtype attribute is missing or parsing it in this way finds no tokens, the item is said to have * no item types. */ - $types = array_values(array_filter($types, function(string $type) { - return $type !== ''; - })); + $types = array_values(array_filter($types, fn(string $type) => $type !== '')); } else { $types = []; } diff --git a/src/Reader/RdfaLiteReader.php b/src/Reader/RdfaLiteReader.php index 565acab..0e2ed23 100644 --- a/src/Reader/RdfaLiteReader.php +++ b/src/Reader/RdfaLiteReader.php @@ -96,9 +96,10 @@ public function read(DOMDocument $document, string $url) : array $nodes = $xpath->query('//*[@typeof and not(@property)]'); $nodes = iterator_to_array($nodes); - return array_map(function(DOMNode $node) use ($xpath, $url) { - return $this->nodeToItem($node, $xpath, $url, self::PREDEFINED_PREFIXES, null); - }, $nodes); + return array_map( + fn(DOMNode $node) => $this->nodeToItem($node, $xpath, $url, self::PREDEFINED_PREFIXES, null), + $nodes, + ); } /** @@ -149,9 +150,7 @@ private function nodeToItem(DOMNode $node, DOMXPath $xpath, string $url, array $ }, $types); // Remove empty values - $types = array_values(array_filter($types, function(string $type) { - return $type !== ''; - })); + $types = array_values(array_filter($types, fn(string $type) => $type !== '')); $item = new Item($id, ...$types); From 4261c8b18aa0c7b5a246b9f04b9860f674c42e86 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:42:50 +0200 Subject: [PATCH 16/17] Use str_contains() --- src/Reader/MicrodataReader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Reader/MicrodataReader.php b/src/Reader/MicrodataReader.php index 67556a2..6c7c68e 100644 --- a/src/Reader/MicrodataReader.php +++ b/src/Reader/MicrodataReader.php @@ -134,7 +134,7 @@ private function nodeToItem(DOMNode $node, DOMXPath $xpath, string $url) : Item * We therefore consider anything containing these characters as an absolute URL, and only prepend the * vocabulary identifier if none of these characters are found. */ - if (strpos($name, '.') === false && strpos($name, ':') === false) { + if (!str_contains($name, '.') && !str_contains($name, ':')) { $name = $vocabularyIdentifier . $name; } From 5186139e21b392e1790a05bbfb2000f78fdb3baf Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Wed, 11 Jun 2025 01:48:04 +0200 Subject: [PATCH 17/17] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd500da..8dc026e 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ optimizing existing code, etc.), `y` is incremented. **When a breaking change is introduced, a new `0.x` version cycle is always started.** -It is therefore safe to lock your project to a given release cycle, such as `0.1.*`. +It is therefore safe to lock your project to a given release cycle, such as `0.2.*`. If you need to upgrade to a newer release cycle, check the [release history](https://github.com/brick/structured-data/releases) for a list of changes introduced by each further `0.x.0` version.