Skip to content

Commit 7747840

Browse files
committed
Create assertion and type-class for xs:NMTOKENS
1 parent b23aed1 commit 7747840

File tree

4 files changed

+168
-0
lines changed

4 files changed

+168
-0
lines changed

src/Assert/NMTokensTrait.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 NMTokensTrait
13+
{
14+
/** @var string */
15+
private static string $nmtokens_regex = '/^([\w.:-]+)([\s][\w.:-]+)*$/Du';
16+
17+
18+
/**
19+
* @param string $value
20+
* @param string $message
21+
*/
22+
protected static function validNMTokens(string $value, string $message = ''): void
23+
{
24+
Assert::regex(
25+
$value,
26+
self::$nmtokens_regex,
27+
$message ?: '%s is not a valid xs:NMTOKENS',
28+
InvalidArgumentException::class,
29+
);
30+
}
31+
}

src/Type/NMTokensValue.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 NMTokensValue extends TokenValue
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::validNMTokens($this->sanitizeValue($value), SchemaViolationException::class);
26+
}
27+
}

tests/Assert/NMTokensTest.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\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\NMTokensTest
15+
*
16+
* @package simplesamlphp/xml-common
17+
*/
18+
#[CoversClass(Assert::class)]
19+
final class NMTokensTest extends TestCase
20+
{
21+
/**
22+
* @param boolean $shouldPass
23+
* @param string $nmtokens
24+
*/
25+
#[DataProvider('provideNMTokens')]
26+
public function testValidTokens(bool $shouldPass, string $nmtokens): void
27+
{
28+
try {
29+
Assert::validNMTokens($nmtokens);
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 provideNMTokens(): array
41+
{
42+
return [
43+
[true, 'Snoopy'],
44+
[true, 'CMS'],
45+
[true, 'fööbár'],
46+
[true, '1950-10-04'],
47+
[true, '0836217462 0836217463'],
48+
[true, 'foo bar'],
49+
// Quotes are forbidden
50+
[false, 'foo "bar" baz'],
51+
// Commas are forbidden
52+
[false, 'foo,bar'],
53+
// Trailing whitespace is forbidden
54+
[false, "foobar\n"],
55+
];
56+
}
57+
}

tests/Type/NMTokensValueTest.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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\NMTokensValue;
12+
13+
/**
14+
* Class \SimpleSAML\Test\Type\NMTokensValueTest
15+
*
16+
* @package simplesamlphp/xml-common
17+
*/
18+
#[CoversClass(NMTokensValue::class)]
19+
final class NMTokensValueTest extends TestCase
20+
{
21+
/**
22+
* @param boolean $shouldPass
23+
* @param string $nmtokens
24+
*/
25+
#[DataProvider('provideNMTokens')]
26+
public function testName(bool $shouldPass, string $nmtokens): void
27+
{
28+
try {
29+
NMTokensValue::fromString($nmtokens);
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 provideNMTokens(): array
41+
{
42+
return [
43+
'valid' => [true, 'Snoopy foobar'],
44+
'diacritical' => [true, 'Snoopy fööbár'],
45+
'start with colon' => [true, ':CMS :ABC'],
46+
'start with dash' => [true, '-1950-10-04 -1984-11-07'],
47+
'numeric first char' => [true, '0836217462'],
48+
'comma' => [false, 'foo,bar'],
49+
'whitespace collapse' => [true, "foobar\n"],
50+
'normalization' => [true, ' foobar nmtoken '],
51+
];
52+
}
53+
}

0 commit comments

Comments
 (0)