Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

+48
View File
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use DateInterval;
use DateTimeInterface;
use Pdp\ResourceUri;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\SimpleCache\CacheInterface;
use Stringable;
final class PsrStorageFactory implements
ResourceUri,
PublicSuffixListStorageFactory,
TopLevelDomainListStorageFactory
{
public function __construct(
private readonly CacheInterface $cache,
private readonly ClientInterface $client,
private readonly RequestFactoryInterface $requestFactory
) {
}
/**
* @param DateInterval|DateTimeInterface|Stringable|int|string|null $cacheTtl
*/
public function createPublicSuffixListStorage(string $cachePrefix = '', $cacheTtl = null): PublicSuffixListStorage
{
return new RulesStorage(
new PublicSuffixListPsr16Cache($this->cache, $cachePrefix, $cacheTtl),
new PublicSuffixListPsr18Client($this->client, $this->requestFactory)
);
}
/**
* @param DateInterval|DateTimeInterface|Stringable|int|string|null $cacheTtl
*/
public function createTopLevelDomainListStorage(string $cachePrefix = '', $cacheTtl = null): TopLevelDomainListStorage
{
return new TopLevelDomainsStorage(
new TopLevelDomainListPsr16Cache($this->cache, $cachePrefix, $cacheTtl),
new TopLevelDomainListPsr18Client($this->client, $this->requestFactory)
);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\PublicSuffixList;
interface PublicSuffixListCache
{
/**
* Retrieves the Public Suffix List from Cache.
*/
public function fetch(string $uri): ?PublicSuffixList;
/**
* Caches the Public Suffix List.
*
* If a local cache already exists, it will be overwritten.
*
* Returns true if the action was successful, false otherwise
*/
public function remember(string $uri, PublicSuffixList $publicSuffixList): bool;
/**
* Deletes the Public Suffix List entry.
*
* Returns true if the action was successful, false otherwise
*/
public function forget(string $uri): bool;
}
+16
View File
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\PublicSuffixList;
use Pdp\UnableToLoadPublicSuffixList;
interface PublicSuffixListClient
{
/**
* @throws UnableToLoadPublicSuffixList
*/
public function get(string $uri): PublicSuffixList;
}
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use DateInterval;
use DateTimeInterface;
use Pdp\PublicSuffixList;
use Psr\SimpleCache\CacheException;
use Psr\SimpleCache\CacheInterface;
use Stringable;
use Throwable;
use function md5;
use function strtolower;
final class PublicSuffixListPsr16Cache implements PublicSuffixListCache
{
private readonly ?DateInterval $cacheTtl;
public function __construct(
private readonly CacheInterface $cache,
private readonly string $cachePrefix = '',
DateInterval|DateTimeInterface|Stringable|int|string|null $cacheTtl = null
) {
$this->cacheTtl = TimeToLive::convert($cacheTtl);
}
public function fetch(string $uri): ?PublicSuffixList
{
$cacheKey = $this->cacheKey($uri);
$publicSuffixList = $this->cache->get($cacheKey);
if (null === $publicSuffixList) {
return null;
}
if (!$publicSuffixList instanceof PublicSuffixList) {
$this->cache->delete($cacheKey);
return null;
}
return $publicSuffixList;
}
/**
* Returns the cache key according to the source URL.
*/
private function cacheKey(string $str): string
{
return $this->cachePrefix.md5(strtolower($str));
}
public function remember(string $uri, PublicSuffixList $publicSuffixList): bool
{
try {
return $this->cache->set($this->cacheKey($uri), $publicSuffixList, $this->cacheTtl);
} catch (Throwable $exception) {
if ($exception instanceof CacheException) {
return false;
}
throw $exception;
}
}
public function forget(string $uri): bool
{
return $this->cache->delete($this->cacheKey($uri));
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\PublicSuffixList;
use Pdp\Rules;
use Pdp\UnableToLoadResource;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
final class PublicSuffixListPsr18Client implements PublicSuffixListClient
{
public function __construct(
private readonly ClientInterface $client,
private readonly RequestFactoryInterface $requestFactory
) {
}
public function get(string $uri): PublicSuffixList
{
$request = $this->requestFactory->createRequest('GET', $uri);
try {
$response = $this->client->sendRequest($request);
} catch (ClientExceptionInterface $exception) {
throw UnableToLoadResource::dueToUnavailableService($uri, $exception);
}
if (400 <= $response->getStatusCode()) {
throw UnableToLoadResource::dueToUnexpectedStatusCode($uri, $response->getStatusCode());
}
return Rules::fromString($response->getBody());
}
}
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
interface PublicSuffixListStorage extends PublicSuffixListClient
{
public function delete(string $uri): bool;
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use DateInterval;
use DateTimeInterface;
use Stringable;
interface PublicSuffixListStorageFactory
{
/**
* @param DateInterval|DateTimeInterface|Stringable|int|string|null $cacheTtl storage TTL object should implement the __toString method
*/
public function createPublicSuffixListStorage(string $cachePrefix = '', $cacheTtl = null): PublicSuffixListStorage;
}
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\PublicSuffixList;
final class RulesStorage implements PublicSuffixListStorage
{
public function __construct(
private readonly PublicSuffixListCache $cache,
private readonly PublicSuffixListClient $client
) {
}
public function get(string $uri): PublicSuffixList
{
$publicSuffixList = $this->cache->fetch($uri);
if (null !== $publicSuffixList) {
return $publicSuffixList;
}
$publicSuffixList = $this->client->get($uri);
$this->cache->remember($uri, $publicSuffixList);
return $publicSuffixList;
}
public function delete(string $uri): bool
{
return $this->cache->forget($uri);
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use DateInterval;
use DateTimeImmutable;
use DateTimeInterface;
use InvalidArgumentException;
use Stringable;
use Throwable;
use function filter_var;
use const FILTER_VALIDATE_INT;
/**
* @internal
*/
final class TimeToLive
{
public static function fromDurationString(string $duration): DateInterval
{
try {
set_error_handler(fn () => true);
$interval = DateInterval::createFromDateString($duration);
restore_error_handler();
if (!$interval instanceof DateInterval) {
throw new InvalidArgumentException(
'The ttl value "'.$duration.'" can not be parsable by `DateInterval::createFromDateString`.'
);
}
} catch (Throwable $exception) {
if (!$exception instanceof InvalidArgumentException) {
throw new InvalidArgumentException(
'The ttl value "'.$duration.'" can not be parsable by `DateInterval::createFromDateString`.',
0,
$exception
);
}
throw $exception;
}
return $interval;
}
public static function until(DateTimeInterface $date): DateInterval
{
return (new DateTimeImmutable('NOW', $date->getTimezone()))->diff($date, false);
}
/**
* Returns a DateInterval relative to the current date and time.
*
* DEPRECATION WARNING! This method will be removed in the next major point release
*
* @deprecated 6.1.0 deprecated
* @codeCoverageIgnore
* @see TimeToLive::until
*/
public static function fromDateTimeInterface(DateTimeInterface $date): DateInterval
{
return self::until($date);
}
/**
* Returns a DateInterval from string parsing.
*
* DEPRECATION WARNING! This method will be removed in the next major point release
*
* @deprecated 6.1.0 deprecated
* @see TimeToLive::fromDurationString
* @codeCoverageIgnore
*
* @throws InvalidArgumentException if the value can not be parsable
*
*/
public static function fromScalar(Stringable|int|string $duration): DateInterval
{
return self::fromDurationString((string) $duration);
}
/**
* Convert the input data into a DateInterval object or null.
*
* @throws InvalidArgumentException if the value can not be computed
*/
public static function convert(DateInterval|DateTimeInterface|Stringable|int|string|null $ttl): ?DateInterval
{
if ($ttl instanceof Stringable) {
$ttl = (string) $ttl;
}
return match (true) {
$ttl instanceof DateInterval || null === $ttl => $ttl,
$ttl instanceof DateTimeInterface => self::until($ttl),
is_int($ttl) => self::fromDurationString($ttl.' seconds'),
false !== ($seconds = filter_var($ttl, FILTER_VALIDATE_INT)) => self::fromDurationString($seconds.' seconds'),
default => self::fromDurationString($ttl),
};
}
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\TopLevelDomainList;
interface TopLevelDomainListCache
{
/**
* Retrieves the Top Level Domain List from the Cache.
*/
public function fetch(string $uri): ?TopLevelDomainList;
/**
* Cache the Top Level Domain List.
*
* If a local cache already exists, it will be overwritten.
*
* Returns true if the action was successful, false otherwise
*/
public function remember(string $uri, TopLevelDomainList $topLevelDomainList): bool;
/**
* Deletes the Top Level Domain List entry.
*
* Returns true if the action was successful, false otherwise
*/
public function forget(string $uri): bool;
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\TopLevelDomainList;
use Pdp\UnableToLoadTopLevelDomainList;
interface TopLevelDomainListClient
{
/**
* @throws UnableToLoadTopLevelDomainList
*/
public function get(string $uri): TopLevelDomainList;
}
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use DateInterval;
use DateTimeInterface;
use Pdp\TopLevelDomainList;
use Psr\SimpleCache\CacheException;
use Psr\SimpleCache\CacheInterface;
use Stringable;
use Throwable;
use function md5;
use function strtolower;
final class TopLevelDomainListPsr16Cache implements TopLevelDomainListCache
{
private readonly ?DateInterval $cacheTtl;
public function __construct(
private readonly CacheInterface $cache,
private readonly string $cachePrefix = '',
DateInterval|DateTimeInterface|Stringable|int|string|null $cacheTtl = null
) {
$this->cacheTtl = TimeToLive::convert($cacheTtl);
}
public function fetch(string $uri): ?TopLevelDomainList
{
$cacheKey = $this->cacheKey($uri);
$topLevelDomainList = $this->cache->get($cacheKey);
if (null === $topLevelDomainList) {
return null;
}
if (!$topLevelDomainList instanceof TopLevelDomainList) {
$this->cache->delete($cacheKey);
return null;
}
return $topLevelDomainList;
}
/**
* Returns the cache key according to the source URL.
*/
private function cacheKey(string $str): string
{
return $this->cachePrefix.md5(strtolower($str));
}
public function remember(string $uri, TopLevelDomainList $topLevelDomainList): bool
{
try {
return $this->cache->set($this->cacheKey($uri), $topLevelDomainList, $this->cacheTtl);
} catch (Throwable $exception) {
if ($exception instanceof CacheException) {
return false;
}
throw $exception;
}
}
public function forget(string $uri): bool
{
return $this->cache->delete($this->cacheKey($uri));
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\TopLevelDomainList;
use Pdp\TopLevelDomains;
use Pdp\UnableToLoadResource;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
final class TopLevelDomainListPsr18Client implements TopLevelDomainListClient
{
public function __construct(
private readonly ClientInterface $client,
private readonly RequestFactoryInterface $requestFactory
) {
}
public function get(string $uri): TopLevelDomainList
{
$request = $this->requestFactory->createRequest('GET', $uri);
try {
$response = $this->client->sendRequest($request);
} catch (ClientExceptionInterface $exception) {
throw UnableToLoadResource::dueToUnavailableService($uri, $exception);
}
if (400 <= $response->getStatusCode()) {
throw UnableToLoadResource::dueToUnexpectedStatusCode($uri, $response->getStatusCode());
}
return TopLevelDomains::fromString($response->getBody());
}
}
@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
interface TopLevelDomainListStorage extends TopLevelDomainListClient
{
public function delete(string $uri): bool;
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use DateInterval;
use DateTimeInterface;
use Stringable;
interface TopLevelDomainListStorageFactory
{
/**
* @param DateInterval|DateTimeInterface|Stringable|int|string|null $cacheTtl storage TTL object should implement the __toString method
*/
public function createTopLevelDomainListStorage(string $cachePrefix = '', $cacheTtl = null): TopLevelDomainListStorage;
}
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Pdp\Storage;
use Pdp\TopLevelDomainList;
final class TopLevelDomainsStorage implements TopLevelDomainListStorage
{
public function __construct(
private readonly TopLevelDomainListCache $cache,
private readonly TopLevelDomainListClient $client
) {
}
public function get(string $uri): TopLevelDomainList
{
$topLevelDomains = $this->cache->fetch($uri);
if (null !== $topLevelDomains) {
return $topLevelDomains;
}
$topLevelDomains = $this->client->get($uri);
$this->cache->remember($uri, $topLevelDomains);
return $topLevelDomains;
}
public function delete(string $uri): bool
{
return $this->cache->forget($uri);
}
}