Skip to content

Commit

Permalink
refactor: add ResourceUrlRegexList to maintain collection of Resource…
Browse files Browse the repository at this point in the history
…UrlRegex

updated API from ResourceUrlRegex with naming and Arrayable interface

refs #8
  • Loading branch information
ThorstenSuckow committed Oct 23, 2022
1 parent b3cc906 commit 5683550
Show file tree
Hide file tree
Showing 4 changed files with 349 additions and 25 deletions.
30 changes: 23 additions & 7 deletions src/Rest/Request/ResourceUrlRegex.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,24 @@

namespace Conjoon\Rest\Request;

use Conjoon\Core\Contract\Arrayable;

/**
* Provides functionality to map urls to url-templates, for retrieving information about
* associated resource targets and path parameters.
*
* @example
* $regex = new ResourceUrlRegex("/MailFolders/{mailFolderId}/MessageItems/{messageItemId}", MessageItem::class);
* $regex->getPathParameterNames();
* // ["mailFolderId", "messageItemId"];
*
* $regex->hasResourceId("http://localhost/MailFolders/INBOX/MessageItems/123"); // true
* $regex->getResourceName("http://localhost/MailFolders/INBOX/MessageItems/123"); // "MessageItem"
*
* $regex->getParameters("http://localhost/MailFolders/INBOX/MessageItems/123");
* // ["mailFolderId" => "INBOX", "messageItemId" => "123"]
*/
class ResourceUrlRegex
class ResourceUrlRegex implements Arrayable
{
/**
* @var ?string
Expand All @@ -52,12 +56,12 @@ class ResourceUrlRegex
/**
* @var string
*/
private string $urlTemplate;
private readonly string $urlTemplate;

/**
* @var string
*/
private string $resourceName;
private readonly string $resourceName;


/**
Expand Down Expand Up @@ -161,7 +165,7 @@ public function getRegexString(): string
*/
public function hasResourceId(string $url): ?bool
{
$matches = $this->getMatches($url);
$matches = $this->getMatch($url);
if (!$matches) {
return null;
}
Expand Down Expand Up @@ -195,7 +199,7 @@ public function hasResourceId(string $url): ?bool
*/
public function getPathParameters(string $url): ?array
{
$matches = $this->getMatches($url);
$matches = $this->getMatch($url);
if (!$matches) {
return null;
}
Expand All @@ -221,7 +225,7 @@ public function getPathParameters(string $url): ?array
*/
public function getResourceName(string $url): ?string
{
if (!$this->getMatches($url)) {
if (!$this->getMatch($url)) {
return null;
}

Expand Down Expand Up @@ -256,7 +260,7 @@ public function getUrlTemplate(): string
* @param string $url
* @return array<int, array<int, string>>|null
*/
protected function getMatches(string $url): ?array
public function getMatch(string $url): ?array
{
$regex = $this->getRegexString();

Expand All @@ -268,4 +272,16 @@ protected function getMatches(string $url): ?array

return $matches;
}


/**
* @inheritdoc
* @return array<int, string>
*/
public function toArray(): array
{
return [
$this->getUrlTemplate(), $this->resourceName
];
}
}
128 changes: 128 additions & 0 deletions src/Rest/Request/ResourceUrlRegexList.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

/**
* conjoon
* php-lib-conjoon
* Copyright (C) 2022 Thorsten Suckow-Homberg https://github.com/conjoon/php-lib-conjoon
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

declare(strict_types=1);

namespace Conjoon\Rest\Request;

use Conjoon\Core\AbstractList;
use Conjoon\Http\Url;
use Iterator;
use ArrayAccess;

/**
* A list for managing ResourceUrlRegex-instances.
* Provides functionality for matching Urls to ResourceUrlRegex for reducing overhead
* while trying to find matches. Changes to the list itself will reset the cache.
*
* @extends AbstractList<ResourceUrlRegex>
*/
class ResourceUrlRegexList extends AbstractList
{
/**
* @var array<string, ?ResourceUrlRegex>
*/
private array $matches = [];

/**
* @inheritdoc
*/
public function getEntityType(): string
{
return ResourceUrlRegex::class;
}


/**
* @inheritdoc
*/
public function offsetSet($offset, $value): void
{
$this->matches = [];
parent::offsetSet($offset, $value);
}


/**
* @inheritdoc
*/
public function offsetUnset($offset): void
{
$this->matches = [];
parent::offsetUnset($offset);
}


/**
* Probes all available entries in this list for a match with the specified
* url, and returns the ResourceUrlRegex that matched the Url.
* If no match was found, null will be returned. Results are cached so that for a
* specific url the list is inspected only once.
*
* @param Url $url
* @return ResourceUrlRegex|null
*
* @see offsetSet
* @see offsetUnset
*/
public function getMatch(Url $url): ?ResourceUrlRegex
{
$urlStr = $url->toString();

if (array_key_exists($urlStr, $this->matches)) {
return $this->matches[$urlStr];
}

$this->matches[$urlStr] = null;
foreach ($this->data as $resourceUrlRegex) {
$match = $resourceUrlRegex->getMatch($urlStr);
if ($match !== null) {
$this->matches[$urlStr] = $resourceUrlRegex;
break;
}
}

return $this->matches[$urlStr];
}


/**
* @inheritdoc
* @return array <int, array<int, string>>
*/
public function toArray(): array
{
$res = [];

foreach ($this->data as $entry) {
/** @var ResourceUrlRegex $entry */
$res[] = $entry->toArray();
}

return $res;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,14 @@

declare(strict_types=1);

namespace Tests\Conjoon\JsonApi\Request;
namespace Tests\Conjoon\Rest\Request;

use Conjoon\Core\AbstractList;
use Conjoon\JsonApi\Request\ResourceUrlRegex;
use Conjoon\JsonApi\Request\ResourceUrlRegexList;
use Conjoon\Rest\Request\ResourceUrlRegex;
use Conjoon\Rest\Request\ResourceUrlRegexList;
use Conjoon\Http\Url;
use PHPUnit\Framework\MockObject\MockObject;
use ReflectionException;
use Tests\TestCase;

/**
Expand All @@ -41,8 +44,9 @@ class ResourceUrlRegexListTest extends TestCase
{
/**
* Tests constructor
* @return void
*/
public function testClass()
public function testClass(): void
{
$list = $this->createList();
$this->assertInstanceOf(AbstractList::class, $list);
Expand All @@ -52,14 +56,70 @@ public function testClass()


/**
* Tests toArray
* Tests getMatch()
* @return void
* @throws ReflectionException
*/
public function testGetMatch(): void
{
$list = $this->createList();
$url = new Url("url");
$urlStr = $url->toString();

$matches = $this->makeAccessible($list, "matches", true);

$getRegexMock = function ($returnValue, $numCalls) use ($urlStr) {
$regex = $this->getMockBuilder(ResourceUrlRegex::class)
->disableOriginalConstructor()
->onlyMethods(["getMatch"])
->getMock();
$regex->expects($this->exactly($numCalls))
->method("getMatch")
->with($urlStr)->willReturn($returnValue);

return $regex;
};
$regex1 = $getRegexMock(null, 2);
$regex2 = $getRegexMock([], 2);
$regex3 = $getRegexMock(null, 0);
$regex4 = $getRegexMock(null, 0);

$list[] = $regex1;
$list[] = $regex2;
$list[] = $regex3;

$this->assertSame($regex2, $list->getMatch($url));
// force re-call to make sure cache is used
$this->assertSame($regex2, $list->getMatch($url));

/** @phpstan-ignore-next-line */
$this->assertNotEmpty($matches->getValue($list));

$list[] = $regex4;
/** @phpstan-ignore-next-line */
$this->assertEmpty($matches->getValue($list));

$this->assertSame(
$regex2,
$list->getMatch($url)
);

unset($list[4]);
/** @phpstan-ignore-next-line */
$this->assertEmpty($matches->getValue($list));
}


/**
* Tests toArray()
* @return void
*/
public function testToArray()
public function testToArray(): void
{
$list = new ResourceUrlRegexList();
$this->assertEquals([], $list->toArray());

/** @var ResourceUrlRegex&MockObject $resourceUrlRegex */
$resourceUrlRegex = $this->createMockForAbstract(
ResourceUrlRegex::class,
["toArray"],
Expand Down
Loading

0 comments on commit 5683550

Please sign in to comment.