Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Schema/Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ class Resource implements \JsonSerializable
private const RESOURCE_NAME_PATTERN = '/^[a-zA-Z0-9_-]+$/';

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

/**
* @param string $uri the URI of this resource
Expand Down
6 changes: 3 additions & 3 deletions src/Schema/ResourceTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ class ResourceTemplate implements \JsonSerializable
private const RESOURCE_NAME_PATTERN = '/^[a-zA-Z0-9_-]+$/';

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

/**
* @param string $uriTemplate a URI template (according to RFC 6570) that can be used to construct resource URIs
Expand Down
19 changes: 19 additions & 0 deletions tests/Unit/Schema/ResourceTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ public function testConstructorInvalid(): void
);
}

#[DataProvider('provideValidTemplatesWithoutDoubleSlash')]
public function testConstructorAcceptsTemplatesWithoutDoubleSlash(string $uriTemplate): void
{
$resource = new ResourceTemplate(
uriTemplate: $uriTemplate,
name: 'test-template',
);

$this->assertInstanceOf(ResourceTemplate::class, $resource);
$this->assertSame($uriTemplate, $resource->uriTemplate);
}

public static function provideValidTemplatesWithoutDoubleSlash(): iterable
{
yield 'custom scheme without slashes' => ['config:{key}'];
yield 'custom scheme with slashes' => ['config://{key}'];
yield 'urn-style template' => ['urn:resource:{id}'];
}

public function testFromArrayValid(): void
{
$resource = ResourceTemplate::fromArray([
Expand Down
21 changes: 21 additions & 0 deletions tests/Unit/Schema/ResourceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ public function testConstructorInvalid(): void
);
}

#[DataProvider('provideValidUrisWithoutDoubleSlash')]
public function testConstructorAcceptsUrisWithoutDoubleSlash(string $uri): void
{
$resource = new Resource(
uri: $uri,
name: 'test-resource',
);

$this->assertInstanceOf(Resource::class, $resource);
$this->assertSame($uri, $resource->uri);
}

public static function provideValidUrisWithoutDoubleSlash(): iterable
{
yield 'urn' => ['urn:isbn:0451450523'];
yield 'mailto' => ['mailto:user@example.com'];
yield 'data' => ['data:text/plain;base64,SGVsbG8='];
yield 'custom scheme without slashes' => ['config:myapp/settings'];
yield 'custom scheme with slashes' => ['config://myapp/settings'];
}

public function testFromArrayValid(): void
{
$resource = Resource::fromArray([
Expand Down