Skip to content

Commit 887ec94

Browse files
committed
Create assertion and type-class for xs:QName
1 parent 7c62a43 commit 887ec94

File tree

4 files changed

+188
-0
lines changed

4 files changed

+188
-0
lines changed

src/Assert/QNameTrait.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\XML\Assert;
6+
7+
use InvalidArgumentException;
8+
9+
/**
10+
* @package simplesamlphp/xml-common
11+
*/
12+
trait QNameTrait
13+
{
14+
/** @var string */
15+
private static string $qname_regex = '/^([a-z_][\w.-]*)(:[a-z_][\w.-]*)?$/Dui';
16+
17+
/***********************************************************************************
18+
* NOTE: Custom assertions may be added below this line. *
19+
* They SHOULD be marked as `protected` to ensure the call is forced *
20+
* through __callStatic(). *
21+
* Assertions marked `public` are called directly and will *
22+
* not handle any custom exception passed to it. *
23+
***********************************************************************************/
24+
25+
26+
/**
27+
* @param string $value
28+
* @param string $message
29+
*/
30+
protected static function validQName(string $value, string $message = ''): void
31+
{
32+
parent::regex(
33+
$value,
34+
self::$qname_regex,
35+
$message ?: '%s is not a valid xs:QName',
36+
InvalidArgumentException::class,
37+
);
38+
}
39+
}

src/Type/QNameValue.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\XML\Type;
6+
7+
use SimpleSAML\XML\Assert\Assert;
8+
use SimpleSAML\XML\Exception\SchemaViolationException;
9+
10+
/**
11+
* @package simplesaml/xml-common
12+
*/
13+
class QNameValue extends AbstractValueType
14+
{
15+
/**
16+
* Validate the value.
17+
*
18+
* @param string $value
19+
* @throws \SimpleSAML\XML\Exception\SchemaViolationException on failure
20+
* @return void
21+
*/
22+
protected function validateValue(string $value): void
23+
{
24+
// Note: value must already be sanitized before validating
25+
Assert::validQName($this->sanitizeValue($value), SchemaViolationException::class);
26+
}
27+
}

tests/Assert/QNameTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Test\XML\Assert;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
use SimpleSAML\Assert\AssertionFailedException;
11+
use SimpleSAML\XML\Assert\Assert;
12+
13+
/**
14+
* Class \SimpleSAML\Test\XML\Assert\QNameTest
15+
*
16+
* @package simplesamlphp/xml-common
17+
*/
18+
#[CoversClass(Assert::class)]
19+
final class QNameTest extends TestCase
20+
{
21+
/**
22+
* @param boolean $shouldPass
23+
* @param string $name
24+
*/
25+
#[DataProvider('provideQName')]
26+
public function testValidQName(bool $shouldPass, string $name): void
27+
{
28+
try {
29+
Assert::validQName($name);
30+
$this->assertTrue($shouldPass);
31+
} catch (AssertionFailedException $e) {
32+
$this->assertFalse($shouldPass);
33+
}
34+
}
35+
36+
37+
/**
38+
* @return array<int, array{0: bool, 1: string}>
39+
*/
40+
public static function provideQName(): array
41+
{
42+
return [
43+
[true, 'some:Test'],
44+
// both parts can contain a dash
45+
[true, 'som-e:Test'],
46+
[true, 'so-me:T-est'],
47+
// Neither part can start with a dash
48+
[false, 'some:-Test'],
49+
[false, '-some:-Test'],
50+
[true, 'Test'],
51+
// Cannot start with a colon
52+
[false, ':test'],
53+
// Cannot contain multiple colons
54+
[false, 'test:test:test'],
55+
// Cannot start with a number
56+
[false, '1Test'],
57+
// Cannot contain a wildcard character
58+
[false, 'Te*st'],
59+
// Prefixed newlines are forbidden
60+
[false, "\nsome:Test"],
61+
// Trailing newlines are forbidden
62+
[false, "some:Test\n"],
63+
];
64+
}
65+
}

tests/Type/QNameValueTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Test\XML\Type;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
use SimpleSAML\XML\Exception\SchemaViolationException;
11+
use SimpleSAML\XML\Type\QNameValue;
12+
13+
/**
14+
* Class \SimpleSAML\Test\Type\QNameValueTest
15+
*
16+
* @package simplesamlphp/xml-common
17+
*/
18+
#[CoversClass(QNameValue::class)]
19+
final class QNameValueTest extends TestCase
20+
{
21+
/**
22+
* @param boolean $shouldPass
23+
* @param string $qname
24+
*/
25+
#[DataProvider('provideQName')]
26+
public function testQName(bool $shouldPass, string $qname): void
27+
{
28+
try {
29+
QNameValue::fromString($qname);
30+
$this->assertTrue($shouldPass);
31+
} catch (SchemaViolationException $e) {
32+
$this->assertFalse($shouldPass);
33+
}
34+
}
35+
36+
37+
/**
38+
* @return array<string, array{0: bool, 1: string}>
39+
*/
40+
public static function provideQName(): array
41+
{
42+
return [
43+
'valid' => [true, 'some:Test'],
44+
'first part containing dash' => [true, 'som-e:Test'],
45+
'both parts containing dash' => [true, 'so-me:T-est'],
46+
'start 2nd part with dash' => [false, 'some:-Test'],
47+
'start both parts with dash' => [false, '-some:-Test'],
48+
'no colon' => [true, 'Test'],
49+
'start with colon' => [false, ':test'],
50+
'multiple colons' => [false, 'test:test:test'],
51+
'start with digit' => [false, '1Test'],
52+
'wildcard' => [false, 'Te*st'],
53+
'prefixed newline' => [false, "\nsome:Test"],
54+
'trailing newline' => [false, "some:Test\n"],
55+
];
56+
}
57+
}

0 commit comments

Comments
 (0)