From a09b96509ca77e553d480e377a9c705b4f04138b Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 12 Sep 2024 00:01:44 +0200 Subject: [PATCH 01/10] Add element registry --- composer.json | 8 ++- src/Registry/AbstractElementRegistry.php | 89 ++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 src/Registry/AbstractElementRegistry.php diff --git a/composer.json b/composer.json index b60c64c..7168968 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "simplesamlphp/xml-common", "description": "A library with classes and utilities for handling XML structures.", - "type": "project", + "type": "simplesamlphp-xmlprovider", "keywords": ["saml", "xml"], "homepage": "http://simplesamlphp.org", "license": "LGPL-2.1-or-later", @@ -34,7 +34,8 @@ "ext-spl": "*", "ext-xmlreader": "*", - "simplesamlphp/assert": "^1.2" + "simplesamlphp/assert": "^1.2", + "symfony/finder": "^6.4" }, "require-dev": { "simplesamlphp/simplesamlphp-test-framework": "^1.7" @@ -47,7 +48,8 @@ "allow-plugins": { "composer/package-versions-deprecated": true, "dealerdirect/phpcodesniffer-composer-installer": true, - "phpstan/extension-installer": true + "phpstan/extension-installer": true, + "simplesamlphp/composer-xmlprovider-installer": true } } } diff --git a/src/Registry/AbstractElementRegistry.php b/src/Registry/AbstractElementRegistry.php new file mode 100644 index 0000000..60586b5 --- /dev/null +++ b/src/Registry/AbstractElementRegistry.php @@ -0,0 +1,89 @@ + */ + protected array $registry = []; + + final private function __construct() + { + // Initialize the registry with all the elements we know + $classesDir = dirname(__FILE__, 3) . '/vendor/simplesamlphp/composer-xmlprovider-installer/classes'; + + $finder = Finder::create()->files()->name('element.registry.*.php')->in($classesDir); + if ($finder->hasResults()) { + foreach ($finder as $file) { + $elements = include($file); + $this->registry = array_merge($this->registry, $elements); + } + } + } + + + public static function getInstance(): AbstractElementRegistry + { + if (self::$instance === null) { + self::$instance = new static(); + } + + return self::$instance; + } + + + /** + * Register a class that can process a certain XML-element. + * + * @param string $class The class name of a class extending AbstractElement. + */ + public function registerElementHandler(string $class): void + { + Assert::subclassOf($class, AbstractElement::class); + $className = AbstractElement::getClassName($class); + $key = ($class::NS === null) ? $className : implode(':', [$class::NS, $className]); + $this->registry[$key] = $class; + } + + + /** + * Search for a class that implements an $element in the given $namespace. + * + * Such classes must have been registered previously by calling registerElementHandler(), and they must + * extend \SimpleSAML\XML\AbstractElement. + * + * @param string|null $namespace The namespace URI for the given element. + * @param string $element The local name of the element. + * + * @return string|null The fully-qualified name of a class extending \SimpleSAML\XML\AbstractElement and + * implementing support for the given element, or null if no such class has been registered before. + */ + public function getElementHandler(?string $namespace, string $element): ?string + { + Assert::nullOrValidURI($namespace, InvalidDOMElementException::class); + Assert::validNCName($element, InvalidDOMElementException::class); + + $key = ($namespace === null) ? $element : implode(':', [$namespace, $element]); + if (array_key_exists($key, $this->registry) === true) { + return $this->registry[$key]; + } + + return null; + } +} From 9c38466aee8b39e476a499782ccab6e7c1d9a740 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 12 Sep 2024 11:31:06 +0200 Subject: [PATCH 02/10] Remove abstract keyword --- .../{AbstractElementRegistry.php => ElementRegistry.php} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/Registry/{AbstractElementRegistry.php => ElementRegistry.php} (98%) diff --git a/src/Registry/AbstractElementRegistry.php b/src/Registry/ElementRegistry.php similarity index 98% rename from src/Registry/AbstractElementRegistry.php rename to src/Registry/ElementRegistry.php index 60586b5..6d3085d 100644 --- a/src/Registry/AbstractElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -14,15 +14,15 @@ use function dirname; use function implode; -abstract class AbstractElementRegistry +class ElementRegistry { /** @var \SimpleSAML\XML\Registry\AbstractElementRegistry|null $instance */ protected static ?AbstractElementRegistry $instance = null; - /** @var array */ protected array $registry = []; + final private function __construct() { // Initialize the registry with all the elements we know From f05c7debdfdb31cc1b4c82f3f34e627c816c0a49 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 12 Sep 2024 12:14:12 +0200 Subject: [PATCH 03/10] Create multi-dimensional array instead of fiddling with composed keys --- src/Registry/ElementRegistry.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index 6d3085d..6c4b0c7 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -56,9 +56,10 @@ public static function getInstance(): AbstractElementRegistry public function registerElementHandler(string $class): void { Assert::subclassOf($class, AbstractElement::class); + $namespace = class::NS; $className = AbstractElement::getClassName($class); - $key = ($class::NS === null) ? $className : implode(':', [$class::NS, $className]); - $this->registry[$key] = $class; + + $this->registry[$namespace][$key] = $class; } @@ -79,11 +80,6 @@ public function getElementHandler(?string $namespace, string $element): ?string Assert::nullOrValidURI($namespace, InvalidDOMElementException::class); Assert::validNCName($element, InvalidDOMElementException::class); - $key = ($namespace === null) ? $element : implode(':', [$namespace, $element]); - if (array_key_exists($key, $this->registry) === true) { - return $this->registry[$key]; - } - - return null; + return $this->registry[$namespace][$element] ?? null; } } From b64e57fdd82350c4294df3dd770f410ae5e7d463 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 12 Sep 2024 12:17:10 +0200 Subject: [PATCH 04/10] Use array_merge_recursive instead --- src/Registry/ElementRegistry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index 6c4b0c7..0886dd1 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -10,7 +10,7 @@ use Symfony\Component\Finder\Finder; use function array_key_exists; -use function array_merge; +use function array_merge_recursive; use function dirname; use function implode; @@ -32,7 +32,7 @@ final private function __construct() if ($finder->hasResults()) { foreach ($finder as $file) { $elements = include($file); - $this->registry = array_merge($this->registry, $elements); + $this->registry = array_merge_recursive($this->registry, $elements); } } } From 9c31de0a1807719425801c4927fbde7fbedacb94 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 12 Sep 2024 13:22:41 +0200 Subject: [PATCH 05/10] Fix little mistakes --- src/Registry/ElementRegistry.php | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index 0886dd1..ec1ad5f 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -9,18 +9,16 @@ use SimpleSAML\XML\Exception\InvalidDOMElementException; use Symfony\Component\Finder\Finder; -use function array_key_exists; use function array_merge_recursive; use function dirname; -use function implode; -class ElementRegistry +final class ElementRegistry { - /** @var \SimpleSAML\XML\Registry\AbstractElementRegistry|null $instance */ - protected static ?AbstractElementRegistry $instance = null; + /** @var \SimpleSAML\XML\Registry\ElementRegistry|null $instance */ + private static ?ElementRegistry $instance = null; - /** @var array */ - protected array $registry = []; + /** @var array> */ + private array $registry = []; final private function __construct() @@ -38,7 +36,7 @@ final private function __construct() } - public static function getInstance(): AbstractElementRegistry + public static function getInstance(): ElementRegistry { if (self::$instance === null) { self::$instance = new static(); @@ -56,10 +54,10 @@ public static function getInstance(): AbstractElementRegistry public function registerElementHandler(string $class): void { Assert::subclassOf($class, AbstractElement::class); - $namespace = class::NS; $className = AbstractElement::getClassName($class); + $namespace = $class::NS; - $this->registry[$namespace][$key] = $class; + $this->registry[$namespace][$className] = $class; } From 8004746d830d3d6d7a11ec36c9987bd480feffce Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Thu, 12 Sep 2024 18:25:55 +0200 Subject: [PATCH 06/10] Add unit tests --- src/Registry/ElementRegistry.php | 13 +++--- tests/Registry/ElementRegistryTest.php | 58 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 tests/Registry/ElementRegistryTest.php diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index ec1ad5f..1d1b6ed 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -11,6 +11,7 @@ use function array_merge_recursive; use function dirname; +use function file_exists; final class ElementRegistry { @@ -26,11 +27,13 @@ final private function __construct() // Initialize the registry with all the elements we know $classesDir = dirname(__FILE__, 3) . '/vendor/simplesamlphp/composer-xmlprovider-installer/classes'; - $finder = Finder::create()->files()->name('element.registry.*.php')->in($classesDir); - if ($finder->hasResults()) { - foreach ($finder as $file) { - $elements = include($file); - $this->registry = array_merge_recursive($this->registry, $elements); + if (file_exists($classesDir) === true) { + $finder = Finder::create()->files()->name('element.registry.*.php')->in($classesDir); + if ($finder->hasResults()) { + foreach ($finder as $file) { + $elements = include($file); + $this->registry = array_merge_recursive($this->registry, $elements); + } } } } diff --git a/tests/Registry/ElementRegistryTest.php b/tests/Registry/ElementRegistryTest.php new file mode 100644 index 0000000..f38acf3 --- /dev/null +++ b/tests/Registry/ElementRegistryTest.php @@ -0,0 +1,58 @@ +registerElementHandler('\SimpleSAML\Test\XML\Element'); + } + + + /** + */ + public function testFetchingHandlerWorks(): void + { + $handler = self::$registry->getElementHandler('urn:x-simplesamlphp:namespace', 'Element'); + $this->assertEquals($handler, '\SimpleSAML\Test\XML\Element'); + } + + + /** + */ + public function testAddingHandlerWorks(): void + { + self::$registry->registerElementHandler('\SimpleSAML\Test\XML\ExtendableElement'); + $handler = self::$registry->getElementHandler('urn:x-simplesamlphp:namespace', 'ExtendableElement'); + $this->assertEquals($handler, '\SimpleSAML\Test\XML\ExtendableElement'); + } + + + /** + */ + public function testUnknownHandlerReturnsNull(): void + { + $handler = self::$registry->getElementHandler('urn:x-simplesamlphp:namespace', 'UnknownElement'); + $this->assertNull($handler); + } +} From e65ca17d27419c23270a9cda53976b9e7e78890c Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Fri, 13 Sep 2024 11:29:13 +0200 Subject: [PATCH 07/10] Fix path - asume being installed in the vendor-directory --- src/Registry/ElementRegistry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index 1d1b6ed..26e43e4 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -25,7 +25,7 @@ final class ElementRegistry final private function __construct() { // Initialize the registry with all the elements we know - $classesDir = dirname(__FILE__, 3) . '/vendor/simplesamlphp/composer-xmlprovider-installer/classes'; + $classesDir = dirname(__FILE__, 6) . '/vendor/simplesamlphp/composer-xmlprovider-installer/classes'; if (file_exists($classesDir) === true) { $finder = Finder::create()->files()->name('element.registry.*.php')->in($classesDir); From 4fc6b3f0578439b8115ed390250175bb06289bed Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Fri, 13 Sep 2024 12:25:18 +0200 Subject: [PATCH 08/10] Require the xmlprovider-installer as it is a base-requirement for any package using this library --- .github/workflows/php.yml | 2 +- composer.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 8b092b3..ade9b06 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -82,7 +82,7 @@ jobs: run: composer-require-checker check --config-file=tools/composer-require-checker.json composer.json - name: Check code for unused dependencies in composer.json - run: composer-unused + run: composer-unused --excludePackage=simplesamlphp/composer-xmlprovider-installer - name: PHP Code Sniffer run: phpcs diff --git a/composer.json b/composer.json index 7168968..abf7e51 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "ext-xmlreader": "*", "simplesamlphp/assert": "^1.2", + "simplesamlphp/composer-xmlprovider-installer": "dev-master", "symfony/finder": "^6.4" }, "require-dev": { From a1ba0eb47373656a6f19587526fbdfbe06c46b2a Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Fri, 13 Sep 2024 12:34:08 +0200 Subject: [PATCH 09/10] Add importFromFile-method --- src/Registry/ElementRegistry.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index 26e43e4..20ac195 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -7,6 +7,7 @@ use SimpleSAML\Assert\Assert; use SimpleSAML\XML\AbstractElement; use SimpleSAML\XML\Exception\InvalidDOMElementException; +use SimpleSAML\XML\Exception\IOException; use Symfony\Component\Finder\Finder; use function array_merge_recursive; @@ -22,7 +23,7 @@ final class ElementRegistry private array $registry = []; - final private function __construct() + private function __construct() { // Initialize the registry with all the elements we know $classesDir = dirname(__FILE__, 6) . '/vendor/simplesamlphp/composer-xmlprovider-installer/classes'; @@ -31,14 +32,24 @@ final private function __construct() $finder = Finder::create()->files()->name('element.registry.*.php')->in($classesDir); if ($finder->hasResults()) { foreach ($finder as $file) { - $elements = include($file); - $this->registry = array_merge_recursive($this->registry, $elements); + $this->importFromFile($file); } } } } + public function importFromFile(string $file): void + { + if (file_exists($file) === true) { + $elements = include($file); + $this->registry = array_merge_recursive($this->registry, $elements); + } else { + throw new IOException('File not found.'); + } + } + + public static function getInstance(): ElementRegistry { if (self::$instance === null) { From eabfe6f59ef5677bca0bb049c5620afadb28d1b9 Mon Sep 17 00:00:00 2001 From: Tim van Dijen Date: Fri, 13 Sep 2024 15:27:40 +0200 Subject: [PATCH 10/10] Pass file path instead of SplFileInfo-object --- src/Registry/ElementRegistry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Registry/ElementRegistry.php b/src/Registry/ElementRegistry.php index 20ac195..9bb707a 100644 --- a/src/Registry/ElementRegistry.php +++ b/src/Registry/ElementRegistry.php @@ -32,7 +32,7 @@ private function __construct() $finder = Finder::create()->files()->name('element.registry.*.php')->in($classesDir); if ($finder->hasResults()) { foreach ($finder as $file) { - $this->importFromFile($file); + $this->importFromFile($file->getPathName()); } } }