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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@ name: CI

on:
push:
branches: [master]
pull_request:

env:
COVERAGE_PHP_VERSION: "8.3"
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
Expand All @@ -16,17 +37,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:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
Expand Down
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
],
"license": "MIT",
"require": {
"php": "^7.2 || ^8.0",
"php": "^8.1",
"ext-dom": "*",
"ext-json": "*",
"ext-libxml": "*",
"sabre/uri": "^2.1"
"sabre/uri": "^2.1 || ^3.0"
},
"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": {
Expand Down
134 changes: 134 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="6.12.0@cf420941d061a57050b6c468ef2c778faf40aee2">
<file src="src/Reader/JsonLdReader.php">
<ArgumentTypeCoercion>
<code><![CDATA[$data]]></code>
<code><![CDATA[$item]]></code>
<code><![CDATA[$value]]></code>
</ArgumentTypeCoercion>
<DocblockTypeContradiction>
<code><![CDATA[is_string($type)]]></code>
</DocblockTypeContradiction>
<MissingClosureParamType>
<code><![CDATA[$a]]></code>
</MissingClosureParamType>
<MixedArgument>
<code><![CDATA[$name]]></code>
</MixedArgument>
<MixedArrayAccess>
<code><![CDATA[$name[0]]]></code>
</MixedArrayAccess>
<PossiblyInvalidArgument>
<code><![CDATA[fn(DOMNode $node) => $this->readJson($node->textContent, $url)]]></code>
</PossiblyInvalidArgument>
<RawObjectIteration>
<code><![CDATA[$item]]></code>
</RawObjectIteration>
<RedundantConditionGivenDocblockType>
<code><![CDATA[null]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="src/Reader/MicrodataReader.php">
<InvalidArgument>
<code><![CDATA[function(DOMNode $itemprop) use ($node, $xpath) {
for (;;) {
$itemprop = $itemprop->parentNode;

if ($itemprop->isSameNode($node)) {
return true;
}

if ($itemprop->attributes->getNamedItem('itemscope')) {
return false;
}
}
}]]></code>
</InvalidArgument>
<PossiblyInvalidArgument>
<code><![CDATA[fn(DOMNode $node) => $this->nodeToItem($node, $xpath, $url)]]></code>
<code><![CDATA[function(DOMNode $itemprop) use ($node, $xpath) {
for (;;) {
$itemprop = $itemprop->parentNode;

if ($itemprop->isSameNode($node)) {
return true;
}

if ($itemprop->attributes->getNamedItem('itemscope')) {
return false;
}
}
}]]></code>
</PossiblyInvalidArgument>
<PossiblyNullArgument>
<code><![CDATA[$names]]></code>
<code><![CDATA[preg_replace('/\s+/', ' ', $node->textContent)]]></code>
</PossiblyNullArgument>
<PossiblyNullPropertyFetch>
<code><![CDATA[$itemprop->attributes->getNamedItem('itemprop')->textContent]]></code>
</PossiblyNullPropertyFetch>
<PossiblyNullReference>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[isSameNode]]></code>
</PossiblyNullReference>
</file>
<file src="src/Reader/RdfaLiteReader.php">
<InvalidArgument>
<code><![CDATA[function(DOMNode $itemprop) use ($node, $xpath) {
for (;;) {
$itemprop = $itemprop->parentNode;

if ($itemprop->isSameNode($node)) {
return true;
}

if ($itemprop->attributes->getNamedItem('typeof')) {
return false;
}
}

// Unreachable, but makes static analysis happy
return false;
}]]></code>
</InvalidArgument>
<PossiblyInvalidArgument>
<code><![CDATA[fn(DOMNode $node) => $this->nodeToItem($node, $xpath, $url, self::PREDEFINED_PREFIXES, null)]]></code>
<code><![CDATA[function(DOMNode $itemprop) use ($node, $xpath) {
for (;;) {
$itemprop = $itemprop->parentNode;

if ($itemprop->isSameNode($node)) {
return true;
}

if ($itemprop->attributes->getNamedItem('typeof')) {
return false;
}
}

// Unreachable, but makes static analysis happy
return false;
}]]></code>
</PossiblyInvalidArgument>
<PossiblyNullArgument>
<code><![CDATA[$names]]></code>
<code><![CDATA[$typeof->textContent]]></code>
<code><![CDATA[preg_replace('/\s+/', ' ', $node->textContent)]]></code>
</PossiblyNullArgument>
<PossiblyNullPropertyFetch>
<code><![CDATA[$property->attributes->getNamedItem('property')->textContent]]></code>
<code><![CDATA[$typeof->textContent]]></code>
</PossiblyNullPropertyFetch>
<PossiblyNullReference>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[getNamedItem]]></code>
<code><![CDATA[isSameNode]]></code>
</PossiblyNullReference>
</file>
</files>
23 changes: 23 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
findUnusedBaselineEntry="true"
findUnusedPsalmSuppress="true"
findUnusedCode="false"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>

<issueHandlers>
<MixedAssignment errorLevel="suppress" />
</issueHandlers>
</psalm>
2 changes: 1 addition & 1 deletion src/DOMBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use DOMDocument;

class DOMBuilder
final class DOMBuilder
{
/**
* Builds a DOMDocument from an HTML string.
Expand Down
7 changes: 2 additions & 5 deletions src/HTMLReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@

namespace Brick\StructuredData;

class HTMLReader
final class HTMLReader
{
/**
* @var Reader
*/
private $reader;
private readonly Reader $reader;

/**
* HTMLReader constructor.
Expand Down
24 changes: 8 additions & 16 deletions src/Item.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,29 @@

namespace Brick\StructuredData;

use TypeError;

/**
* An item, such as a Thing in schema.org's vocabulary.
*/
class Item
final class Item
{
/**
* The global identifier of the item, if any.
*
* @var string|null
*/
private $id;
private readonly ?string $id;

/**
* The types this Item implements, as URLs.
*
* @var array<string>
* @var string[]
*/
private $types;
private readonly array $types;

/**
* The properties, as a map of property name to list of values.
*
* @var array<string, array<Item|string>>
*/
private $properties = [];
private array $properties = [];

/**
* Item constructor.
Expand All @@ -57,9 +53,9 @@ 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<string>
* @return string[]
*/
public function getTypes() : array
{
Expand Down Expand Up @@ -100,12 +96,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;
}
}
13 changes: 7 additions & 6 deletions src/JsonLdWriter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* Exports Items to JSON-LD.
*/
class JsonLdWriter
final class JsonLdWriter
{
/**
* Exports a list of Items as JSON-LD.
Expand All @@ -18,11 +18,12 @@ 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);
return json_encode($this->extractIfSingle($items), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
}

/**
Expand Down Expand Up @@ -62,7 +63,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];
Expand Down
Loading