Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add element registry #26

Merged
merged 10 commits into from
Sep 13, 2024
2 changes: 1 addition & 1 deletion .github/workflows/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 6 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -34,7 +34,9 @@
"ext-spl": "*",
"ext-xmlreader": "*",

"simplesamlphp/assert": "^1.2"
"simplesamlphp/assert": "^1.2",
"simplesamlphp/composer-xmlprovider-installer": "dev-master",
"symfony/finder": "^6.4"
},
"require-dev": {
"simplesamlphp/simplesamlphp-test-framework": "^1.7"
Expand All @@ -47,7 +49,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
}
}
}
97 changes: 97 additions & 0 deletions src/Registry/ElementRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\XML\Registry;

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;
use function dirname;
use function file_exists;

final class ElementRegistry
{
/** @var \SimpleSAML\XML\Registry\ElementRegistry|null $instance */
private static ?ElementRegistry $instance = null;

/** @var array<string, array<string, string>> */
private array $registry = [];


private function __construct()
{
// Initialize the registry with all the elements we know
$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);
if ($finder->hasResults()) {
foreach ($finder as $file) {
$this->importFromFile($file->getPathName());
}
}
}
}


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) {
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);
$namespace = $class::NS;

$this->registry[$namespace][$className] = $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);

return $this->registry[$namespace][$element] ?? null;
}
}
58 changes: 58 additions & 0 deletions tests/Registry/ElementRegistryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\Test\XML;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\TestCase;
use SimpleSAML\XML\Registry\ElementRegistry;

/**
* @package simplesamlphp\xml-common
*/
#[CoversClass(ElementRegistry::class)]
#[Group('registry')]
final class ElementRegistryTest extends TestCase
{
/** @var \SimpleSAML\XML\Registry\ElementRegistry */
protected static ElementRegistry $registry;


/**
*/
public static function setUpBeforeClass(): void
{
self::$registry = ElementRegistry::getInstance();
self::$registry->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);
}
}