LICENSE.md 0000644 00000002040 14361753014 0006150 0 ustar 00 Copyright (c) 2021 Romain Canon
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.
README.md 0000644 00000003354 14361753014 0006034 0 ustar 00 Valinor • PHP object mapper with strong type support
====================================================
[![Latest Stable Version](https://poser.pugx.org/cuyz/valinor/v)][link-packagist]
[![PHP Version Require](https://poser.pugx.org/cuyz/valinor/require/php)][link-packagist]
[![Total Downloads](https://poser.pugx.org/cuyz/valinor/downloads)][link-packagist]
[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2FCuyZ%2FValinor%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/CuyZ/Valinor/master)
---
Valinor is a PHP library that helps to map any input into a strongly-typed value
object structure.
The conversion can handle native PHP types as well as other well-known advanced
type annotations like array shapes, generics and more.
## Installation
```bash
composer require cuyz/valinor
```
## Documentation
Documentation can be found at [valinor.cuyz.io](https://valinor.cuyz.io).
## Credits & thank you
The development of this library is mainly motivated by the kind words and the
help of many people. I am grateful to everyone, especially to the [contributors]
of this repository who directly help to push the project forward.
I also want to thank
[![blackfire-logo] Blackfire](https://www.blackfire.io/?utm_source=valinor&utm_medium=readme&utm_campaign=free-open-source)
for providing a license of their awesome tool, leading to notable performance
gains when using this library.
[link-packagist]: https://packagist.org/packages/cuyz/valinor
[contributors]: https://github.com/CuyZ/Valinor/graphs/contributors
[blackfire-logo]: docs/pages/img/blackfire-logo.svg "Blackfire logo"
composer.json 0000644 00000004324 14361753014 0007275 0 ustar 00 {
"name": "cuyz/valinor",
"type": "library",
"description": "Library that helps to map any input into a strongly-typed value object structure.",
"keywords": [
"object", "tree", "mapper", "mapping", "hydrator", "array", "conversion", "json", "yaml"
],
"homepage": "https://github.com/CuyZ/Valinor",
"license": "MIT",
"authors": [
{
"name": "Romain Canon",
"email": "romain.hydrocanon@gmail.com",
"homepage": "https://github.com/romm"
}
],
"require": {
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0",
"composer-runtime-api": "^2.0",
"psr/simple-cache": "^1.0 || ^2.0",
"doctrine/annotations": "^1.11"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"infection/infection": "^0.26",
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-strict-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"friendsofphp/php-cs-fixer": "^3.4",
"marcocesarato/php-conventional-changelog": "^1.12",
"vimeo/psalm": "^4.18.1",
"mikey179/vfsstream": "^1.6.10",
"rector/rector": "^0.12.23"
},
"autoload": {
"psr-4": {
"CuyZ\\Valinor\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"CuyZ\\Valinor\\Tests\\": "tests",
"CuyZ\\Valinor\\QA\\": "qa"
}
},
"scripts": {
"check": [
"@putenv XDEBUG_MODE=off",
"@putenv PHP_CS_FIXER_IGNORE_ENV=1",
"phpunit",
"phpstan",
"psalm",
"php-cs-fixer fix --dry-run",
"rector --dry-run"
],
"fix": [
"@putenv XDEBUG_MODE=off",
"@putenv PHP_CS_FIXER_IGNORE_ENV=1",
"php-cs-fixer fix",
"rector"
],
"mutation": [
"infection --threads=12 --git-diff-lines"
],
"doc": [
"Composer\\Config::disableProcessTimeout",
"pip install -r docs/requirements.txt",
"mkdocs serve --config-file docs/mkdocs.yml"
]
},
"config": {
"allow-plugins": {
"infection/extension-installer": false
}
}
}
qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php 0000644 00000002333 14361753014 0016736 0 ustar 00
*/
final class ApiAndInternalAnnotationCheck implements Rule
{
public function getNodeType(): string
{
return InClassNode::class;
}
public function processNode(Node $node, Scope $scope): array
{
$reflection = $scope->getClassReflection();
if (! $reflection) {
return [];
}
if ($reflection->isAnonymous()) {
return [];
}
if (str_starts_with($reflection->getName(), 'CuyZ\Valinor\Tests')) {
return [];
}
if (str_starts_with($reflection->getName(), 'CuyZ\Valinor\QA')) {
return [];
}
if (! preg_match('/@(api|internal)\s+/', $reflection->getResolvedPhpDoc()?->getPhpDocString() ?? '')) {
return [
RuleErrorBuilder::message(
'Missing annotation `@api` or `@internal`.'
)->build(),
];
}
return [];
}
}
qa/PHPStan/Extension/TreeMapperPHPStanExtension.php 0000644 00000003222 14361753014 0016251 0 ustar 00 resolver = $resolver;
}
public function getClass(): string
{
return TreeMapper::class;
}
public function isMethodSupported(MethodReflection $methodReflection): bool
{
return $methodReflection->getName() === 'map';
}
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
{
$argument = $methodCall->getArgs()[0]->value;
$type = $scope->getType($argument);
if ($type instanceof UnionType) {
return $type->traverse(fn (Type $type) => $this->type($type));
}
return $this->type($type);
}
private function type(Type $type): Type
{
if ($type instanceof GenericClassStringType) {
return $type->getGenericType();
}
if ($type instanceof ConstantStringType) {
return $this->resolver->resolve($type->getValue());
}
return new MixedType();
}
}
qa/PHPStan/Stubs/Psr/SimpleCache/CacheInterface.stub 0000644 00000001700 14361753014 0016170 0 ustar 00 $keys
* @param EntryType $default
* @return iterable
*/
public function getMultiple(iterable $keys, $default = null): iterable;
/**
* @param iterable $values
* @param null|int|DateInterval $ttl
*/
public function setMultiple(iterable $values, $ttl = null): bool;
/**
* @param iterable $keys
*/
public function deleteMultiple(iterable $keys): bool;
}
qa/PHPStan/valinor-phpstan-configuration.php 0000644 00000000503 14361753014 0015127 0 ustar 00 [
[
'class' => TreeMapperPHPStanExtension::class,
'tags' => ['phpstan.broker.dynamicMethodReturnTypeExtension']
]
],
];
qa/Psalm/Plugin/TreeMapperPsalmPlugin.php 0000644 00000003461 14361753014 0014420 0 ustar 00 getMethodNameLowercase() !== 'map') {
return null;
}
$type = $event->getSource()->getNodeTypeProvider()->getType($event->getCallArgs()[0]->value);
if (! $type) {
return null;
}
$types = [];
foreach ($type->getAtomicTypes() as $node) {
$inferred = self::type($node);
if ($inferred === null) {
return null;
}
$types[] = $inferred;
}
if (count($types) === 0) {
return null;
}
return Type::combineUnionTypeArray($types, $event->getSource()->getCodebase());
}
private static function type(Atomic $node): ?Union
{
switch (true) {
case $node instanceof TLiteralString:
return Type::parseString($node->value);
case $node instanceof TDependentGetClass:
return $node->as_type;
case $node instanceof TClassString && $node->as_type:
return new Union([$node->as_type]);
default:
return null;
}
}
}
src/Attribute/Identifier.php 0000644 00000001706 14361753014 0012101 0 ustar 00 [...], // $identifier will be filled with `my first key`
* 'my second key' => [...], // $identifier will be filled with `my second key`
* ];
* ```
*
* @api
*
* @Annotation
* @Target({"PROPERTY"})
*/
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
final class Identifier implements ShellVisitor
{
public function visit(Shell $shell): Shell
{
return $shell->withValue($shell->parent()->name());
}
}
src/Attribute/StaticMethodConstructor.php 0000644 00000001637 14361753014 0014660 0 ustar 00 methodName = $methodName;
}
public function for(ClassDefinition $class): array
{
return [new MethodObjectBuilder($class->name(), $this->methodName, $class->methods()->get($this->methodName)->parameters())];
}
}
src/Cache/ChainCache.php 0000644 00000005232 14361753014 0011023 0 ustar 00
*/
final class ChainCache implements CacheInterface
{
/** @var array> */
private array $delegates;
private int $count;
/**
* @param CacheInterface ...$caches
*/
public function __construct(CacheInterface ...$caches)
{
$this->delegates = $caches;
$this->count = count($caches);
}
/**
* @PHP8.0 add `mixed` return type and remove PHPDoc
*
* @return EntryType|null
*/
public function get($key, $default = null)
{
foreach ($this->delegates as $i => $delegate) {
$value = $delegate->get($key, $default);
if (null !== $value) {
while (--$i >= 0) {
$this->delegates[$i]->set($key, $value);
}
return $value;
}
}
return $default;
}
public function set($key, $value, $ttl = null): bool
{
$saved = true;
$i = $this->count;
while ($i--) {
$saved = $this->delegates[$i]->set($key, $value, $ttl) && $saved;
}
return $saved;
}
public function delete($key): bool
{
$deleted = true;
$i = $this->count;
while ($i--) {
$deleted = $this->delegates[$i]->delete($key) && $deleted;
}
return $deleted;
}
public function clear(): bool
{
$cleared = true;
$i = $this->count;
while ($i--) {
$cleared = $this->delegates[$i]->clear() && $cleared;
}
return $cleared;
}
/**
* @return Traversable
*/
public function getMultiple($keys, $default = null): Traversable
{
foreach ($keys as $key) {
yield $key => $this->get($key, $default);
}
}
public function setMultiple($values, $ttl = null): bool
{
$saved = true;
foreach ($values as $key => $value) {
$saved = $this->set($key, $value, $ttl) && $saved;
}
return $saved;
}
public function deleteMultiple($keys): bool
{
$deleted = true;
foreach ($keys as $key) {
$deleted = $this->delete($key) && $deleted;
}
return $deleted;
}
public function has($key): bool
{
foreach ($this->delegates as $cache) {
if ($cache->has($key)) {
return true;
}
}
return false;
}
}
src/Cache/Compiled/CacheCompiler.php 0000644 00000000316 14361753014 0013305 0 ustar 00
*/
final class CompiledPhpFileCache implements CacheInterface
{
private const TEMPORARY_DIR_PERMISSION = 510;
private const GENERATED_MESSAGE = 'Generated by ' . self::class;
private string $cacheDir;
private CacheCompiler $compiler;
/** @var array> */
private array $files = [];
public function __construct(string $cacheDir, CacheCompiler $compiler)
{
$this->cacheDir = $cacheDir;
$this->compiler = $compiler;
}
public function has($key): bool
{
$filename = $this->path($key);
if (! file_exists($filename)) {
return false;
}
return $this->getFile($filename)->isValid();
}
/**
* @PHP8.0 add `mixed` return type and remove PHPDoc
*
* @return EntryType|null
*/
public function get($key, $default = null)
{
if (! $this->has($key)) {
return $default;
}
$filename = $this->path($key);
return $this->getFile($filename)->value();
}
public function set($key, $value, $ttl = null): bool
{
$filename = $this->path($key);
assert(! file_exists($filename));
$code = $this->compile($value, $ttl);
$tmpDir = $this->cacheDir . DIRECTORY_SEPARATOR . '.valinor.tmp';
if (! is_dir($tmpDir) && ! @mkdir($tmpDir, self::TEMPORARY_DIR_PERMISSION, true)) {
throw new CacheDirectoryNotWritable($this->cacheDir);
}
/** @infection-ignore-all */
$tmpFilename = $tmpDir . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16));
try {
if (! @file_put_contents($tmpFilename, $code)) {
throw new CompiledPhpCacheFileNotWritten($tmpFilename);
}
if (! file_exists($filename) && ! @rename($tmpFilename, $filename)) {
throw new CompiledPhpCacheFileNotWritten($filename);
}
} finally {
@unlink($tmpFilename);
}
return true;
}
public function delete($key): bool
{
$filename = $this->path($key);
if (file_exists($filename)) {
return @unlink($filename);
}
return true;
}
public function clear(): bool
{
$success = true;
/** @var FilesystemIterator $file */
foreach (new FilesystemIterator($this->cacheDir) as $file) {
if (! $file->isFile()) {
continue;
}
$line = $file->openFile()->getCurrentLine();
if (! $line || ! Polyfill::str_contains($line, self::GENERATED_MESSAGE)) {
continue;
}
$success = @unlink($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
}
return $success;
}
/**
* @return Traversable
*/
public function getMultiple($keys, $default = null): Traversable
{
foreach ($keys as $key) {
yield $key => $this->get($key, $default);
}
}
public function setMultiple($values, $ttl = null): bool
{
foreach ($values as $key => $value) {
$this->set($key, $value, $ttl);
}
return true;
}
public function deleteMultiple($keys): bool
{
$deleted = true;
foreach ($keys as $key) {
$deleted = $this->delete($key) && $deleted;
}
return $deleted;
}
/**
* @param mixed $value
* @param int|DateInterval|null $ttl
*/
private function compile($value, $ttl = null): string
{
$validationCode = 'true';
if ($ttl) {
$time = $ttl instanceof DateInterval
? (new DateTime())->add($ttl)->getTimestamp()
: time() + $ttl;
$validationCode = "time() < $time";
}
$generatedMessage = self::GENERATED_MESSAGE;
$code = $this->compiler->compile($value);
return <<compiler instanceof \CuyZ\Valinor\Cache\Compiled\HasArguments ? \$this->compiler->arguments() : []) implements \CuyZ\Valinor\Cache\Compiled\PhpCacheFile {
/** @var array */
private array \$arguments;
public function __construct(array \$arguments)
{
\$this->arguments = \$arguments;
}
public function value()
{
return $code;
}
public function isValid(): bool
{
return $validationCode;
}
};
PHP;
}
/**
* @return PhpCacheFile
*/
private function getFile(string $filename): PhpCacheFile
{
if (! isset($this->files[$filename])) {
try {
$object = include $filename;
} catch (Error $exception) {
// @PHP8.0 remove variable
}
if (! isset($object) || ! $object instanceof PhpCacheFile) {
throw new CorruptedCompiledPhpCacheFile($filename);
}
$this->files[$filename] = $object;
}
return $this->files[$filename];
}
private function path(string $key): string
{
/** @infection-ignore-all */
return $this->cacheDir . DIRECTORY_SEPARATOR . sha1($key) . '.php';
}
}
src/Cache/Compiled/HasArguments.php 0000644 00000000347 14361753014 0013214 0 ustar 00
*/
public function arguments(): array;
}
src/Cache/Compiled/MixedValueCacheCompiler.php 0000644 00000000426 14361753014 0015273 0 ustar 00 getMessage()}",
1653330261,
$exception
);
}
}
src/Cache/FileSystemCache.php 0000644 00000006522 14361753014 0012070 0 ustar 00
*/
final class FileSystemCache implements CacheInterface
{
/** @var array> */
private array $delegates;
public function __construct(string $cacheDir = null)
{
$cacheDir ??= sys_get_temp_dir();
// @infection-ignore-all
$this->delegates = [
'*' => new CompiledPhpFileCache($cacheDir . DIRECTORY_SEPARATOR . 'mixed', new MixedValueCacheCompiler()),
ClassDefinition::class => new CompiledPhpFileCache($cacheDir . DIRECTORY_SEPARATOR . 'classes', new ClassDefinitionCompiler()),
FunctionDefinition::class => new CompiledPhpFileCache($cacheDir . DIRECTORY_SEPARATOR . 'functions', new FunctionDefinitionCompiler()),
];
}
public function has($key): bool
{
foreach ($this->delegates as $delegate) {
if ($delegate->has($key)) {
return true;
}
}
return false;
}
/**
* @PHP8.0 add `mixed` return type and remove PHPDoc
*
* @return EntryType|null
*/
public function get($key, $default = null)
{
foreach ($this->delegates as $delegate) {
if ($delegate->has($key)) {
return $delegate->get($key, $default);
}
}
return $default;
}
public function set($key, $value, $ttl = null): bool
{
$delegate = $this->delegates['*'];
if (is_object($value) && isset($this->delegates[get_class($value)])) {
$delegate = $this->delegates[get_class($value)];
}
return $delegate->set($key, $value, $ttl);
}
public function delete($key): bool
{
$deleted = true;
foreach ($this->delegates as $delegate) {
$deleted = $delegate->delete($key) && $deleted;
}
return $deleted;
}
public function clear(): bool
{
$cleared = true;
foreach ($this->delegates as $delegate) {
$cleared = $delegate->clear() && $cleared;
}
return $cleared;
}
/**
* @return Traversable
*/
public function getMultiple($keys, $default = null): Traversable
{
foreach ($keys as $key) {
yield $key => $this->get($key, $default);
}
}
public function setMultiple($values, $ttl = null): bool
{
$set = true;
foreach ($values as $key => $value) {
$set = $this->set($key, $value, $ttl) && $set;
}
return $set;
}
public function deleteMultiple($keys): bool
{
$deleted = true;
foreach ($keys as $key) {
$deleted = $this->delete($key) && $deleted;
}
return $deleted;
}
}
src/Cache/FileWatchingCache.php 0000644 00000010033 14361753014 0012340 0 ustar 00
* @template EntryType
* @implements CacheInterface
*/
final class FileWatchingCache implements CacheInterface
{
/** @var CacheInterface */
private CacheInterface $delegate;
/** @var array */
private array $timestamps = [];
/**
* @param CacheInterface $delegate
*/
public function __construct(CacheInterface $delegate)
{
$this->delegate = $delegate;
}
public function has($key): bool
{
foreach ($this->timestamps($key) as $fileName => $timestamp) {
if (@filemtime($fileName) !== $timestamp) {
return false;
}
}
return $this->delegate->has($key);
}
/**
* @PHP8.0 add `mixed` return type and remove PHPDoc
*
* @return EntryType|TimestampsArray|null
*/
public function get($key, $default = null)
{
return $this->delegate->get($key, $default);
}
public function set($key, $value, $ttl = null): bool
{
$this->saveTimestamps($key, $value);
return $this->delegate->set($key, $value, $ttl);
}
public function delete($key): bool
{
return $this->delegate->delete($key);
}
public function clear(): bool
{
$this->timestamps = [];
return $this->delegate->clear();
}
public function getMultiple($keys, $default = null): iterable
{
return $this->delegate->getMultiple($keys, $default);
}
public function setMultiple($values, $ttl = null): bool
{
foreach ($values as $key => $value) {
$this->saveTimestamps($key, $value);
}
return $this->delegate->setMultiple($values, $ttl);
}
public function deleteMultiple($keys): bool
{
return $this->delegate->deleteMultiple($keys);
}
/**
* @return TimestampsArray
*/
private function timestamps(string $key): array
{
return $this->timestamps[$key] ??= $this->delegate->get("$key.timestamps", []); // @phpstan-ignore-line
}
/**
* @param mixed $value
*/
private function saveTimestamps(string $key, $value): void
{
$this->timestamps[$key] = [];
$fileNames = [];
if ($value instanceof ClassDefinition) {
$reflection = Reflection::class($value->name());
do {
$fileNames[] = $reflection->getFileName();
} while ($reflection = $reflection->getParentClass());
}
if ($value instanceof FunctionDefinition) {
$fileNames[] = $value->fileName();
}
foreach ($fileNames as $fileName) {
if (! is_string($fileName)) {
// @infection-ignore-all
continue;
}
$time = @filemtime($fileName);
// @infection-ignore-all
if (false === $time) {
continue;
}
$this->timestamps[$key][$fileName] = $time;
}
if (! empty($this->timestamps[$key])) {
$this->delegate->set("$key.timestamps", $this->timestamps[$key]);
}
}
}
src/Cache/KeySanitizerCache.php 0000644 00000005244 14361753014 0012425 0 ustar 00
*/
final class KeySanitizerCache implements CacheInterface
{
private static string $version;
/** @var CacheInterface */
private CacheInterface $delegate;
/** @var Closure(string): string */
private Closure $sanitize;
/**
* @param CacheInterface $delegate
*/
public function __construct(CacheInterface $delegate)
{
$this->delegate = $delegate;
// Two things:
// 1. We append the current version of the package to the cache key in
// order to avoid collisions between entries from different versions
// of the library.
// 2. The key is sha1'd so that it does not contain illegal characters.
// @see https://www.php-fig.org/psr/psr-16/#12-definitions
$this->sanitize = static fn (string $key) => sha1("$key." . self::$version ??= PHP_VERSION . '/' . Package::version());
}
/**
* @PHP8.0 add `mixed` return type and remove PHPDoc
*
* @return EntryType|null
*/
public function get($key, $default = null)
{
return $this->delegate->get(($this->sanitize)($key), $default);
}
public function set($key, $value, $ttl = null): bool
{
return $this->delegate->set(($this->sanitize)($key), $value, $ttl);
}
public function delete($key): bool
{
return $this->delegate->delete(($this->sanitize)($key));
}
public function clear(): bool
{
return $this->delegate->clear();
}
public function has($key): bool
{
return $this->delegate->has(($this->sanitize)($key));
}
/**
* @return Traversable
*/
public function getMultiple($keys, $default = null): Traversable
{
foreach ($keys as $key) {
yield $key => $this->delegate->get(($this->sanitize)($key), $default);
}
}
public function setMultiple($values, $ttl = null): bool
{
$versionedValues = [];
foreach ($values as $key => $value) {
$versionedValues[($this->sanitize)($key)] = $value;
}
return $this->delegate->setMultiple($versionedValues, $ttl);
}
public function deleteMultiple($keys): bool
{
$transformedKeys = [];
foreach ($keys as $key) {
$transformedKeys[] = ($this->sanitize)($key);
}
return $this->delegate->deleteMultiple($transformedKeys);
}
}
src/Cache/RuntimeCache.php 0000644 00000003337 14361753014 0011430 0 ustar 00
*/
final class RuntimeCache implements CacheInterface
{
/** @var array */
private array $entries = [];
/**
* @PHP8.0 add `mixed` return type and remove PHPDoc
*
* @return EntryType|null
*/
public function get($key, $default = null)
{
return $this->entries[$key] ?? $default;
}
public function set($key, $value, $ttl = null): bool
{
$this->entries[$key] = $value;
return true;
}
public function delete($key): bool
{
unset($this->entries[$key]);
return true;
}
public function clear(): bool
{
$this->entries = [];
return true;
}
public function getMultiple($keys, $default = null): iterable
{
$entries = [];
foreach ($keys as $key) {
$entries[$key] = $this->get($key, $default);
}
return $entries;
}
public function setMultiple($values, $ttl = null): bool
{
foreach ($values as $key => $value) {
$this->set($key, $value, $ttl);
}
return true;
}
public function deleteMultiple($keys): bool
{
foreach ($keys as $key) {
$this->delete($key);
}
return true;
}
public function has($key): bool
{
return isset($this->entries[$key]);
}
}
src/Cache/Warmup/RecursiveCacheWarmupService.php 0000644 00000005776 14361753014 0015755 0 ustar 00 */
private array $classesWarmedUp = [];
public function __construct(
TypeParser $parser,
ObjectImplementations $implementations,
ClassDefinitionRepository $classDefinitionRepository,
ObjectBuilderFactory $objectBuilderFactory
) {
$this->parser = $parser;
$this->implementations = $implementations;
$this->classDefinitionRepository = $classDefinitionRepository;
$this->objectBuilderFactory = $objectBuilderFactory;
}
public function warmup(string ...$signatures): void
{
foreach ($signatures as $signature) {
try {
$this->warmupType($this->parser->parse($signature));
} catch (InvalidType $exception) {
throw new InvalidSignatureToWarmup($signature, $exception);
}
}
}
private function warmupType(Type $type): void
{
if ($type instanceof InterfaceType) {
$this->warmupInterfaceType($type);
}
if ($type instanceof ClassType) {
$this->warmupClassType($type);
}
if ($type instanceof CompositeType) {
foreach ($type->traverse() as $subType) {
$this->warmupType($subType);
}
}
}
private function warmupInterfaceType(InterfaceType $type): void
{
$function = $this->implementations->function($type->className());
$this->warmupType($function->returnType());
foreach ($function->parameters() as $parameter) {
$this->warmupType($parameter->type());
}
}
private function warmupClassType(ClassType $type): void
{
if (in_array($type->className(), $this->classesWarmedUp, true)) {
return;
}
$this->classesWarmedUp[] = $type->className();
$classDefinition = $this->classDefinitionRepository->for($type);
$objectBuilders = $this->objectBuilderFactory->for($classDefinition);
foreach ($objectBuilders as $builder) {
foreach ($builder->describeArguments() as $argument) {
$this->warmupType($argument->type());
}
}
}
}
src/Definition/Attributes.php 0000644 00000000773 14361753014 0012275 0 ustar 00
*/
interface Attributes extends IteratorAggregate, Countable
{
/**
* @param class-string $className
*/
public function has(string $className): bool;
/**
* @template T of object
*
* @param class-string $className
* @return list
*/
public function ofType(string $className): array;
}
src/Definition/AttributesContainer.php 0000644 00000002147 14361753014 0014135 0 ustar 00 */
private array $attributes;
public function __construct(object ...$attributes)
{
$this->attributes = $attributes;
}
public function has(string $className): bool
{
foreach ($this->attributes as $attribute) {
if ($attribute instanceof $className) {
return true;
}
}
return false;
}
public function ofType(string $className): array
{
return array_values(array_filter(
$this->attributes,
static fn (object $attribute): bool => $attribute instanceof $className
));
}
public function count(): int
{
return count($this->attributes);
}
/**
* @return Traversable