register('soap', '/soap//soap-', ['soap' => ''], ''); $this->assertEquals('/soap/beans/soap-beans?soap=beans#beans', $arrReg->format('soap', ['soap' => 'beans'])); $this->assertEquals('', $arrReg->format('does-not-exist', ['test' => 'test'])); $nameScopeOnly = $arrReg->scopeTo(namePrefix: 'pfx-'); $nameScopeOnly->register('cat', '/meow', ['id' => 'c']); $this->assertEquals('/meow?id=c50', $nameScopeOnly->format('cat', ['id' => '50'])); $this->assertEquals('/meow?id=c', $arrReg->format('pfx-cat')); // this is a little undesirable but just keep your query args clean :) $this->assertEquals('', $arrReg->format('cat', ['id' => '84'])); $pathScopeOnly = $arrReg->scopeTo(pathPrefix: '/prefix'); $pathScopeOnly->register('dog', '/woof', ['type' => '']); $this->assertEquals('/prefix/woof', $pathScopeOnly->format('dog')); $this->assertEquals('/prefix/woof?type=borf', $arrReg->format('dog', ['woof' => 'borf'])); // we cannot access dog cus pfx- gets prepended $this->assertEquals('', $nameScopeOnly->format('dog')); // we CAN access cat because path prefixing only applies to registration // not sure when you'd want this behaviour but its entirely transparent to implement soooooooo why not lmao $this->assertEquals('/meow?id=cactuallydog', $pathScopeOnly->format('pfx-cat', ['id' => 'actuallydog'])); // this is debatably the only scope impl that makes sense, but again zero overhead $scopedReg = $arrReg->scopeTo('pfx2-', '/also'); $scopedReg->register('fragmented', '/this/one/has/a/fragment', fragment: 'p'); $this->assertEquals('/also/this/one/has/a/fragment#p9001', $scopedReg->format('fragmented', ['post' => 9001])); $this->assertEquals('/also/this/one/has/a/fragment#p20001', $arrReg->format('pfx2-fragmented', ['post' => 20001])); $sourceClass = new class implements UrlSource { use UrlSourceTrait; #[UrlFormat('method-a', '/method-a')] public function a(): void {} #[UrlFormat('method-b', '/method-b', ['beans' => '', 'test' => ''])] public function b(): void {} #[UrlFormat('method-c', '/method-c', fragment: '')] public function c(): void {} #[UrlFormat('method-d', '/method-d', fragment: '')] public function d(): void {} #[UrlFormat('method-e', '/method-e/', ['count' => ''])] public function e(): void {} }; // can't use path scope only because of the names $arrReg->register($sourceClass); $nameScopeOnly->register($sourceClass); $scopedReg->register($sourceClass); $this->assertEquals('/method-a', $arrReg->format('method-a')); $this->assertEquals('/method-b', $arrReg->format('method-b')); $this->assertEquals('/method-b?beans=720', $arrReg->format('method-b', ['count' => 720])); $this->assertEquals('/method-b?beans=720&test=wowzers', $arrReg->format('method-b', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/method-c', $arrReg->format('method-c')); $this->assertEquals('/method-c#720', $arrReg->format('method-c', ['count' => 720])); $this->assertEquals('/method-d', $arrReg->format('method-d')); $this->assertEquals('/method-d#98342389', $arrReg->format('method-d', ['count' => 98342389])); $this->assertEquals('/method-e/', $arrReg->format('method-e')); $this->assertEquals('/method-e/wowzers', $arrReg->format('method-e', ['mode' => 'wowzers'])); $this->assertEquals('/method-e/wowzers?count=720', $arrReg->format('method-e', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/method-a', $arrReg->format('pfx-method-a')); $this->assertEquals('/method-b', $arrReg->format('pfx-method-b')); $this->assertEquals('/method-b?beans=720', $arrReg->format('pfx-method-b', ['count' => 720])); $this->assertEquals('/method-b?beans=720&test=wowzers', $arrReg->format('pfx-method-b', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/method-c', $arrReg->format('pfx-method-c')); $this->assertEquals('/method-c#720', $arrReg->format('pfx-method-c', ['count' => 720])); $this->assertEquals('/method-d', $arrReg->format('pfx-method-d')); $this->assertEquals('/method-d#98342389', $arrReg->format('pfx-method-d', ['count' => 98342389])); $this->assertEquals('/method-e/', $arrReg->format('pfx-method-e')); $this->assertEquals('/method-e/wowzers', $arrReg->format('pfx-method-e', ['mode' => 'wowzers'])); $this->assertEquals('/method-e/wowzers?count=720', $arrReg->format('pfx-method-e', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/also/method-a', $arrReg->format('pfx2-method-a')); $this->assertEquals('/also/method-b', $arrReg->format('pfx2-method-b')); $this->assertEquals('/also/method-b?beans=720', $arrReg->format('pfx2-method-b', ['count' => 720])); $this->assertEquals('/also/method-b?beans=720&test=wowzers', $arrReg->format('pfx2-method-b', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/also/method-c', $arrReg->format('pfx2-method-c')); $this->assertEquals('/also/method-c#720', $arrReg->format('pfx2-method-c', ['count' => 720])); $this->assertEquals('/also/method-d', $arrReg->format('pfx2-method-d')); $this->assertEquals('/also/method-d#98342389', $arrReg->format('pfx2-method-d', ['count' => 98342389])); $this->assertEquals('/also/method-e/', $arrReg->format('pfx2-method-e')); $this->assertEquals('/also/method-e/wowzers', $arrReg->format('pfx2-method-e', ['mode' => 'wowzers'])); $this->assertEquals('/also/method-e/wowzers?count=720', $arrReg->format('pfx2-method-e', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/method-a', $nameScopeOnly->format('method-a')); $this->assertEquals('/method-b', $nameScopeOnly->format('method-b')); $this->assertEquals('/method-b?beans=720', $nameScopeOnly->format('method-b', ['count' => 720])); $this->assertEquals('/method-b?beans=720&test=wowzers', $nameScopeOnly->format('method-b', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/method-c', $nameScopeOnly->format('method-c')); $this->assertEquals('/method-c#720', $nameScopeOnly->format('method-c', ['count' => 720])); $this->assertEquals('/method-d', $nameScopeOnly->format('method-d')); $this->assertEquals('/method-d#98342389', $nameScopeOnly->format('method-d', ['count' => 98342389])); $this->assertEquals('/method-e/', $nameScopeOnly->format('method-e')); $this->assertEquals('/method-e/wowzers', $nameScopeOnly->format('method-e', ['mode' => 'wowzers'])); $this->assertEquals('/method-e/wowzers?count=720', $nameScopeOnly->format('method-e', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/also/method-a', $scopedReg->format('method-a')); $this->assertEquals('/also/method-b', $scopedReg->format('method-b')); $this->assertEquals('/also/method-b?beans=720', $scopedReg->format('method-b', ['count' => 720])); $this->assertEquals('/also/method-b?beans=720&test=wowzers', $scopedReg->format('method-b', ['count' => 720, 'mode' => 'wowzers'])); $this->assertEquals('/also/method-c', $scopedReg->format('method-c')); $this->assertEquals('/also/method-c#720', $scopedReg->format('method-c', ['count' => 720])); $this->assertEquals('/also/method-d', $scopedReg->format('method-d')); $this->assertEquals('/also/method-d#98342389', $scopedReg->format('method-d', ['count' => 98342389])); $this->assertEquals('/also/method-e/', $scopedReg->format('method-e')); $this->assertEquals('/also/method-e/wowzers', $scopedReg->format('method-e', ['mode' => 'wowzers'])); $this->assertEquals('/also/method-e/wowzers?count=720', $scopedReg->format('method-e', ['count' => 720, 'mode' => 'wowzers'])); // now that there's more going on within the registry lets try this again! $this->assertEquals('', $arrReg->format('does-not-exist', ['test' => 'test'])); } public function testUrlVariableSerialisation(): void { $registry = new ArrayUrlRegistry; $registry->register('test', '/', ['p' => '']); $this->assertEquals('/', $registry->format('test')); $this->assertEquals('/string', $registry->format('test', ['value' => 'string'])); $this->assertEquals('/1234567890', $registry->format('test', ['value' => 1234567890])); $this->assertEquals('/1234.5678', $registry->format('test', ['value' => 1234.5678])); $this->assertEquals('/a,b,c', $registry->format('test', ['value' => ['a', 'b', 'c']])); $this->assertEquals('/12,b,4.5', $registry->format('test', ['value' => [12, 'b', 4.5]])); $this->assertEquals('/1', $registry->format('test', ['value' => true])); $this->assertEquals('/', $registry->format('test', ['value' => false])); $this->assertEquals('/', $registry->format('test', ['value' => null])); $this->assertEquals('/', $registry->format('test', ['value' => []])); // "page" is a special case where 1 is considered default // idk if it makes sense for this to be hardcoded, probably not but it makes sense 99.9% of the times so eat pant. $this->assertEquals('/', $registry->format('test', ['value' => 0, 'page' => 0])); $this->assertEquals('/1', $registry->format('test', ['value' => 1, 'page' => 1])); $this->assertEquals('/2?p=2', $registry->format('test', ['value' => 2, 'page' => 2])); $this->assertEquals('/?p=2', $registry->format('test', ['value' => 0, 'page' => 2])); $stringable = new class implements Stringable { public function __toString(): string { return 'meow'; } }; $this->assertEquals('/meow', $registry->format('test', ['value' => $stringable])); } public function testUrlRegistrationBlankNameException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$nameOrSource may not be empty'); (new ArrayUrlRegistry)->register(''); } public function testUrlRegistrationDupeNameException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('A URL format with $nameOrSource has already been registered'); $registry = new ArrayUrlRegistry; $registry->register('test', '/path1'); $registry->register('test', '/path2'); } public function testUrlRegistrationBlankPathException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$path may not be empty'); (new ArrayUrlRegistry)->register('test', ''); } public function testUrlRegistrationNonRootPathException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$path must begin with /'); (new ArrayUrlRegistry)->register('test', 'soap'); } public function testUrlRegistrationScopedNonRootPrefixException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$pathPrefix must start with a / if it is non-empty'); (new ArrayUrlRegistry)->scopeTo('beans', 'beans'); } public function testUrlRegistrationScopedBlankNameException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$nameOrSource may not be empty'); (new ArrayUrlRegistry)->scopeTo('beans', '/beans')->register(''); } public function testUrlRegistrationScopedBlankPathException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$path may not be empty'); (new ArrayUrlRegistry)->scopeTo('beans', '/beans')->register('test', ''); } public function testUrlRegistrationScopedNonRootPathException(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('$path must begin with /'); (new ArrayUrlRegistry)->scopeTo('beans', '/beans')->register('test', 'soap'); } }