Skip to content

Commit

Permalink
Add container classes to accomodate extension points
Browse files Browse the repository at this point in the history
  • Loading branch information
tvdijen committed Feb 26, 2024
1 parent 98d37ed commit 8ca6a75
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"require-dev": {
"beste/clock": "^3.0",
"psr/log": "^2.0 | ^3.0",
"simplesamlphp/simplesamlphp-test-framework": "^1.5"
},
"autoload": {
Expand Down
108 changes: 108 additions & 0 deletions src/SAML11/Compat/AbstractContainer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\Compat;

use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;
use SimpleSAML\Assert\Assert;
use SimpleSAML\SAML11\XML\ExtensionPointInterface;
use SimpleSAML\XML\AbstractElement;
use SimpleSAML\XML\Exception\SchemaViolationException;

use function array_key_exists;
use function implode;
use function is_subclass_of;

abstract class AbstractContainer
{
/** @var string */
private const XSI_TYPE_PREFIX = '<xsi:type>';

/** @var array */
protected array $registry = [];


/**
* Register a class that can handle given extension points of the standard.
*
* @param string $class The class name of a class extending AbstractElement or implementing ExtensionPointInterface.
* @psalm-param class-string $class
*/
public function registerExtensionHandler(string $class): void
{
Assert::subclassOf($class, AbstractElement::class);
if (is_subclass_of($class, ExtensionPointInterface::class, true)) {
$key = implode(':', [self::XSI_TYPE_PREFIX, $class::getXsiTypeNamespaceURI(), $class::getXsiTypeName()]);
} else {
$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 registerExtensionHandler(), 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.
* @psalm-return class-string|null
*/
public function getElementHandler(?string $namespace, string $element): ?string
{
Assert::nullOrValidURI($namespace, SchemaViolationException::class);
Assert::validNCName($element, SchemaViolationException::class);

$key = ($namespace === null) ? $element : implode(':', [$namespace, $element]);
if (array_key_exists($key, $this->registry) === true) {
return $this->registry[$key];
}

return null;
}


/**
* Search for a class that implements a custom element type.
*
* Such classes must have been registered previously by calling registerExtensionHandler(), and they must
* implement \SimpleSAML\SAML11\XML\saml\ExtensionPointInterface.
*
* @param string $type The type of the identifier (xsi:type of a BaseID element).
*
* @return string|null The fully-qualified name of a class implementing
* \SimpleSAML\SAML11\XML\saml\ExtensionPointInterface or null if no such class has been registered before.
* @psalm-return class-string|null
*/
public function getExtensionHandler(string $type): ?string
{
Assert::notEmpty($type, 'Cannot search for identifier handlers with an empty type.');
$type = implode(':', [self::XSI_TYPE_PREFIX, $type]);
if (!array_key_exists($type, $this->registry)) {
return null;
}
Assert::implementsInterface($this->registry[$type], ExtensionPointInterface::class);
return $this->registry[$type];
}


/**
* Get a PSR-3 compatible logger.
* @return \Psr\Log\LoggerInterface
*/
abstract public function getLogger(): LoggerInterface;


/**
* Get the system clock, using UTC for a timezone
*/
abstract public function getClock(): ClockInterface;
}
34 changes: 34 additions & 0 deletions src/SAML11/Compat/ContainerSingleton.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\Compat;

use SimpleSAML\Assert\Assert;

class ContainerSingleton
{
/** @var \SimpleSAML\SAML11\Compat\AbstractContainer */
protected static AbstractContainer $container;


/**
* @return \SimpleSAML\SAML11\Compat\AbstractContainer
*/
public static function getInstance(): AbstractContainer
{
Assert::notNull(self::$container, 'No container set.');
return self::$container;
}


/**
* Set a container to use.
*
* @param \SimpleSAML\SAML11\Compat\AbstractContainer $container
*/
public static function setContainer(AbstractContainer $container): void
{
self::$container = $container;
}
}
51 changes: 51 additions & 0 deletions src/SAML11/Compat/MockContainer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11\Compat;

use Psr\Clock\ClockInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

/**
* Class \SimpleSAML\SAML11\Compat\MockContainer
*/
class MockContainer extends AbstractContainer
{
/** @var \Psr\Clock\ClockInterface */
private ClockInterface $clock;


/**
* Get a PSR-3 compatible logger.
* @return \Psr\Log\LoggerInterface
*/
public function getLogger(): LoggerInterface
{
return new NullLogger();
}


/**
* Set the system clock
*
* @param \Psr\Clock\ClockInterface $clock
* @return void
*/
public function setClock(ClockInterface $clock): void
{
$this->clock = $clock;
}


/**
* Get the system clock
*
* @return \Psr\Clock\ClockInterface
*/
public function getClock(): ClockInterface
{
return $this->clock;
}
}
24 changes: 24 additions & 0 deletions src/SAML11/Utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\SAML11;

use SimpleSAML\SAML11\Compat\AbstractContainer;
use SimpleSAML\SAML11\Compat\ContainerSingleton;

/**
* Helper functions for the SAML 1.1 library.
*
* @package simplesamlphp/saml11
*/
class Utils
{
/**
* @return \SimpleSAML\SAML11\Compat\AbstractContainer
*/
public static function getContainer(): AbstractContainer
{
return ContainerSingleton::getInstance();
}
}

0 comments on commit 8ca6a75

Please sign in to comment.