Skip to content

Commit 7974727

Browse files
[Schema] fix: allow URIs without double slash in Resource and ResourceTemplate (RFC 3986) (#297)
* fix: allow URIs without double slash in Resource and ResourceTemplate (RFC 3986) The URI regex required "://" after the scheme, rejecting valid URIs like urn:isbn:123, mailto:user@example.com, config:key, and data: URIs. RFC 3986 only requires ":" after the scheme; the "//" is part of the authority component which is optional. Changed both Resource.URI_PATTERN and ResourceTemplate.URI_TEMPLATE_PATTERN to require ":" instead of "://". Adds data-provider tests for URIs without double slash in both classes. Fixes #293 * Reword unit tests --------- Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
1 parent a929548 commit 7974727

4 files changed

Lines changed: 46 additions & 6 deletions

File tree

src/Schema/Resource.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ class Resource implements \JsonSerializable
4040
private const RESOURCE_NAME_PATTERN = '/^[a-zA-Z0-9_-]+$/';
4141

4242
/**
43-
* URI pattern regex - requires a valid scheme, followed by colon and optional path.
44-
* Example patterns: config://, file://path, db://table, etc.
43+
* URI pattern regex - requires a valid scheme followed by colon and optional path (RFC 3986).
44+
* Example patterns: file://path, db://table, urn:isbn:123, config:key, etc.
4545
*/
46-
private const URI_PATTERN = '/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/[^\s]*$/';
46+
private const URI_PATTERN = '/^[a-zA-Z][a-zA-Z0-9+.-]*:[^\s]*$/';
4747

4848
/**
4949
* @param string $uri the URI of this resource

src/Schema/ResourceTemplate.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ class ResourceTemplate implements \JsonSerializable
3737
private const RESOURCE_NAME_PATTERN = '/^[a-zA-Z0-9_-]+$/';
3838

3939
/**
40-
* URI Template pattern regex - requires a valid scheme, followed by colon and path with at least one placeholder.
41-
* Example patterns: config://{key}, file://{path}/contents.txt, db://{table}/{id}, etc.
40+
* URI Template pattern regex - requires a valid scheme followed by colon and path with at least one placeholder (RFC 3986).
41+
* Example patterns: file://{path}/contents.txt, db://{table}/{id}, config:{key}, etc.
4242
*/
43-
private const URI_TEMPLATE_PATTERN = '/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\/.*{[^{}]+}.*/';
43+
private const URI_TEMPLATE_PATTERN = '/^[a-zA-Z][a-zA-Z0-9+.-]*:.*{[^{}]+}.*/';
4444

4545
/**
4646
* @param string $uriTemplate a URI template (according to RFC 6570) that can be used to construct resource URIs

tests/Unit/Schema/ResourceTemplateTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,25 @@ public function testConstructorInvalid(): void
4646
);
4747
}
4848

49+
#[DataProvider('provideValidTemplates')]
50+
public function testConstructorAcceptsTemplates(string $uriTemplate): void
51+
{
52+
$resource = new ResourceTemplate(
53+
uriTemplate: $uriTemplate,
54+
name: 'test-template',
55+
);
56+
57+
$this->assertInstanceOf(ResourceTemplate::class, $resource);
58+
$this->assertSame($uriTemplate, $resource->uriTemplate);
59+
}
60+
61+
public static function provideValidTemplates(): iterable
62+
{
63+
yield 'custom scheme without slashes' => ['config:{key}'];
64+
yield 'custom scheme with slashes' => ['config://{key}'];
65+
yield 'urn-style template' => ['urn:resource:{id}'];
66+
}
67+
4968
public function testFromArrayValid(): void
5069
{
5170
$resource = ResourceTemplate::fromArray([

tests/Unit/Schema/ResourceTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,27 @@ public function testConstructorInvalid(): void
4646
);
4747
}
4848

49+
#[DataProvider('provideValidUris')]
50+
public function testConstructorAcceptsUris(string $uri): void
51+
{
52+
$resource = new Resource(
53+
uri: $uri,
54+
name: 'test-resource',
55+
);
56+
57+
$this->assertInstanceOf(Resource::class, $resource);
58+
$this->assertSame($uri, $resource->uri);
59+
}
60+
61+
public static function provideValidUris(): iterable
62+
{
63+
yield 'urn' => ['urn:isbn:0451450523'];
64+
yield 'mailto' => ['mailto:user@example.com'];
65+
yield 'data' => ['data:text/plain;base64,SGVsbG8='];
66+
yield 'custom scheme without slashes' => ['config:myapp/settings'];
67+
yield 'custom scheme with slashes' => ['config://myapp/settings'];
68+
}
69+
4970
public function testFromArrayValid(): void
5071
{
5172
$resource = Resource::fromArray([

0 commit comments

Comments
 (0)