build: полная сборка в git — серверные PHP-зависимости lib/Vendor (438 файлов, из пиннированного релиза v22.0.12)
Type checking / changes (push) Has been cancelled
Type checking / test (push) Has been cancelled
Type checking / typescript-summary (push) Has been cancelled
Node tests / changes (push) Has been cancelled
Node tests / test (push) Has been cancelled
Node tests / test-summary (push) Has been cancelled

Теперь master f7_talk = ПОЛНЫЙ деплой-артефакт: built js (наша сборка) + lib/Vendor (серверные
PHP-deps) + патч Listener + version 22.0.12.1. Готов к брендингу dev-f7 и подписи dev-appstore.
Апстрим-подписи (appinfo/signature.json) в репо нет — переподпишет dev-appstore при публикации.
This commit is contained in:
2026-07-06 22:20:16 +00:00
parent 9dad445d11
commit 49dd055a58
439 changed files with 24952 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
use Traversable;
/**
* @internal
*
* @template EntryType
* @implements WarmupCache<EntryType>
*/
final class ChainCache implements WarmupCache
{
/** @var array<CacheInterface<EntryType>> */
private array $delegates;
private int $count;
/**
* @param CacheInterface<EntryType> ...$delegates
*/
public function __construct(CacheInterface ...$delegates)
{
$this->delegates = $delegates;
$this->count = count($delegates);
}
public function warmup(): void
{
foreach ($this->delegates as $delegate) {
if ($delegate instanceof WarmupCache) {
$delegate->warmup();
}
}
}
public function get($key, $default = null): mixed
{
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<string, EntryType|null>
*/
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;
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception;
use RuntimeException;
/** @internal */
final class CacheDirectoryNotWritable extends RuntimeException
{
public function __construct(string $directory)
{
parent::__construct(
"Provided directory `$directory` is not writable.",
1616445016
);
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception;
use RuntimeException;
/** @internal */
final class CompiledPhpCacheFileNotWritten extends RuntimeException
{
public function __construct(string $file)
{
parent::__construct(
"File `$file` could not be written.",
1616445695
);
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception;
use RuntimeException;
/** @internal */
final class CorruptedCompiledPhpCacheFile extends RuntimeException
{
public function __construct(string $filename)
{
parent::__construct(
"Compiled php cache file `$filename` has corrupted value.",
1628949607
);
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use RuntimeException;
/** @internal */
final class InvalidSignatureToWarmup extends RuntimeException
{
public function __construct(string $signature, InvalidType $exception)
{
parent::__construct(
"Cannot warm up invalid signature `$signature`: {$exception->getMessage()}",
1653330261,
$exception
);
}
}
+223
View File
@@ -0,0 +1,223 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache;
use OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception\CacheDirectoryNotWritable;
use OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception\CompiledPhpCacheFileNotWritten;
use OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception\CorruptedCompiledPhpCacheFile;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler\ClassDefinitionCompiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler\FunctionDefinitionCompiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Normalizer\Transformer\EvaluatedTransformer;
use Error;
use FilesystemIterator;
use Traversable;
use function bin2hex;
use function file_exists;
use function file_put_contents;
use function is_dir;
use function mkdir;
use function random_bytes;
use function rename;
use function rmdir;
use function str_contains;
use function unlink;
use function var_export;
/**
* @api
*
* @template EntryType
* @implements WarmupCache<EntryType>
*/
final class FileSystemCache implements WarmupCache
{
private const TEMPORARY_DIR_PERMISSION = 510;
private const GENERATED_MESSAGE = 'Generated by ' . self::class;
private string $cacheDir;
private ClassDefinitionCompiler $classDefinitionCompiler;
private FunctionDefinitionCompiler $functionDefinitionCompiler;
public function __construct(string $cacheDir)
{
$this->cacheDir = $cacheDir;
$this->classDefinitionCompiler = new ClassDefinitionCompiler();
$this->functionDefinitionCompiler = new FunctionDefinitionCompiler();
}
public function warmup(): void
{
$this->createTemporaryDir();
}
public function has($key): bool
{
$filename = $this->path($key);
return file_exists($filename);
}
public function get($key, $default = null): mixed
{
$filename = $this->path($key);
if (! file_exists($filename)) {
return $default;
}
try {
return include $filename;
} catch (Error) {
throw new CorruptedCompiledPhpCacheFile($filename);
}
}
public function set($key, $value, $ttl = null): bool
{
$filename = $this->path($key);
$code = $this->compile($value);
$tmpDir = $this->createTemporaryDir();
/** @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 {
if (file_exists($tmpFilename)) {
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
{
if (! is_dir($this->cacheDir)) {
return true;
}
$success = true;
$shouldDeleteRootDir = true;
/** @var FilesystemIterator $file */
foreach (new FilesystemIterator($this->cacheDir) as $file) {
if ($file->getFilename() === '.valinor.tmp') {
$success = @rmdir($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
continue;
}
if (! $file->isFile()) {
$shouldDeleteRootDir = false;
continue;
}
$line = $file->openFile()->getCurrentLine();
if (! $line || ! str_contains($line, self::GENERATED_MESSAGE)) {
$shouldDeleteRootDir = false;
continue;
}
$success = @unlink($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
}
if ($shouldDeleteRootDir) {
$success = @rmdir($this->cacheDir) && $success;
}
return $success;
}
/**
* @return Traversable<string, EntryType|null>
*/
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;
}
private function compile(mixed $value): string
{
$generatedMessage = self::GENERATED_MESSAGE;
$code = match (true) {
$value instanceof ClassDefinition => $this->classDefinitionCompiler->compile($value),
$value instanceof FunctionDefinition => $this->functionDefinitionCompiler->compile($value),
$value instanceof EvaluatedTransformer => $value->code,
default => var_export($value, true),
};
return <<<PHP
<?php // $generatedMessage
return $code;
PHP;
}
private function createTemporaryDir(): string
{
$tmpDir = $this->cacheDir . DIRECTORY_SEPARATOR . '.valinor.tmp';
if (! is_dir($tmpDir) && ! @mkdir($tmpDir, self::TEMPORARY_DIR_PERMISSION, true)) {
throw new CacheDirectoryNotWritable($this->cacheDir);
}
return $tmpDir;
}
private function path(string $key): string
{
/** @infection-ignore-all */
return $this->cacheDir . DIRECTORY_SEPARATOR . $key . '.php';
}
}
+159
View File
@@ -0,0 +1,159 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
use function file_exists;
use function filemtime;
use function is_string;
/**
* This cache implementation will watch the files of the application and
* invalidate cache entries when a PHP file is modified — preventing the library
* not behaving as expected when the signature of a property or a method
* changes.
*
* This is especially useful when the application runs in a development
* environment, where source files are often modified by developers.
*
* It should decorate the original cache implementation and should be given to
* the mapper builder: @see \OCA\Talk\Vendor\CuyZ\Valinor\MapperBuilder::withCache
*
* @api
*
* @phpstan-type TimestampsArray = array<string, int>
* @template EntryType
* @implements WarmupCache<EntryType|TimestampsArray>
*/
final class FileWatchingCache implements WarmupCache
{
/** @var array<string, TimestampsArray> */
private array $timestamps = [];
public function __construct(
/** @var CacheInterface<EntryType|TimestampsArray> */
private CacheInterface $delegate
) {}
public function warmup(): void
{
if ($this->delegate instanceof WarmupCache) {
$this->delegate->warmup();
}
}
public function has($key): bool
{
foreach ($this->timestamps($key) as $fileName => $timestamp) {
if (! file_exists($fileName)) {
return false;
}
if (filemtime($fileName) !== $timestamp) {
return false;
}
}
return $this->delegate->has($key);
}
public function get($key, $default = null): mixed
{
if (! $this->has($key)) {
return $default;
}
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
}
private function saveTimestamps(string $key, mixed $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]);
}
}
}
+113
View File
@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache;
use OCA\Talk\Vendor\CuyZ\Valinor\Library\Settings;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Package;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
use Traversable;
use function hash;
use function strstr;
/**
* @internal
*
* @template EntryType
* @implements WarmupCache<EntryType>
*/
final class KeySanitizerCache implements WarmupCache
{
private static string $version;
public function __construct(
/** @var CacheInterface<EntryType> */
private CacheInterface $delegate,
private Settings $settings,
) {}
/**
* 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 hashed so that it does not contain illegal characters.
* @see https://www.php-fig.org/psr/psr-16/#12-definitions
*
* @infection-ignore-all
*/
private function sanitize(string $key): string
{
self::$version ??= PHP_VERSION . '/' . Package::version();
$firstPart = strstr($key, "\0", before_needle: true);
return $firstPart . hash('xxh128', $key . $this->settings->hash() . self::$version);
}
public function warmup(): void
{
if ($this->delegate instanceof WarmupCache) {
$this->delegate->warmup();
}
}
public function get($key, $default = null): mixed
{
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<string, EntryType|null>
*/
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);
}
}
+91
View File
@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache;
use OCA\Talk\Vendor\CuyZ\Valinor\Normalizer\Transformer\EvaluatedTransformer;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
/**
* Simple PSR-16-compatible runtime cache implementation.
*
* Used by default by the library so that entries can be cached in memory during
* runtime.
*
* @link http://www.php-fig.org/psr/psr-16/
*
* @internal
*
* @template EntryType
* @implements CacheInterface<EntryType>
*/
final class RuntimeCache implements CacheInterface
{
/** @var array<string, EntryType> */
private array $entries = [];
public function get($key, $default = null): mixed
{
return $this->entries[$key] ?? $default;
}
public function set($key, $value, $ttl = null): bool
{
if ($value instanceof EvaluatedTransformer) {
return false;
}
$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]);
}
}
@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache\Warmup;
use OCA\Talk\Vendor\CuyZ\Valinor\Cache\Exception\InvalidSignatureToWarmup;
use OCA\Talk\Vendor\CuyZ\Valinor\Cache\WarmupCache;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Tree\Builder\ObjectImplementations;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ClassType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\CompositeType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\TypeParser;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\InterfaceType;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
use function in_array;
/** @internal */
final class RecursiveCacheWarmupService
{
/** @var list<class-string> */
private array $classesWarmedUp = [];
private bool $warmupWasDone = false;
public function __construct(
private TypeParser $parser,
/** @var CacheInterface<mixed> */
private CacheInterface $cache,
private ObjectImplementations $implementations,
private ClassDefinitionRepository $classDefinitionRepository,
private ObjectBuilderFactory $objectBuilderFactory
) {}
public function warmup(string ...$signatures): void
{
if (! $this->warmupWasDone) {
$this->warmupWasDone = true;
if ($this->cache instanceof WarmupCache) {
$this->cache->warmup();
}
}
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
{
$interfaceName = $type->className();
if (! $this->implementations->has($interfaceName)) {
return;
}
$function = $this->implementations->function($interfaceName);
$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());
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace OCA\Talk\Vendor\CuyZ\Valinor\Cache;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
/**
* @internal
*
* @template T
* @extends CacheInterface<T>
*/
interface WarmupCache extends CacheInterface
{
public function warmup(): void;
}
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler;
use function str_repeat;
use function str_replace;
/** @internal */
final class Compiler
{
private string $code = '';
/** @var non-negative-int */
private int $indentation = 0;
public function compile(Node ...$nodes): self
{
$compiler = $this;
while ($current = array_shift($nodes)) {
$compiler = $current->compile($compiler);
if ($nodes !== []) {
$compiler = $compiler->write("\n");
}
}
return $compiler;
}
public function sub(): self
{
return new self();
}
public function write(string $code): self
{
$self = clone $this;
$self->code .= $code;
return $self;
}
public function indent(): self
{
$self = clone $this;
$self->indentation++;
return $self;
}
public function code(): string
{
$indent = str_repeat(' ', $this->indentation);
return $indent . str_replace("\n", "\n" . $indent, $this->code);
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Library;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\AttributeDefinition;
use function array_map;
use function serialize;
/** @internal */
final class NewAttributeNode extends Node
{
public function __construct(private AttributeDefinition $attribute) {}
public function compile(Compiler $compiler): Compiler
{
$argumentNodes = self::argumentNode($this->attribute->arguments);
return $compiler->compile(
Node::newClass(
$this->attribute->class->name,
...$argumentNodes,
),
);
}
/**
* @param array<mixed> $arguments
* @return array<Node>
*/
private static function argumentNode(array $arguments): array
{
return array_map(static function (mixed $argument) {
if (is_object($argument)) {
return Node::functionCall(
name: 'unserialize',
arguments: [Node::value(serialize($argument))],
);
}
if (is_array($argument)) {
return Node::array(self::argumentNode($argument));
}
/** @var scalar $argument */
return Node::value($argument);
}, $arguments);
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Library;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ComplianceNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
/** @internal */
final class TypeAcceptNode extends Node
{
public function __construct(
private ComplianceNode $node,
private Type $type,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile($this->type->compiledAccept($this->node));
}
}
@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function array_map;
use function implode;
/** @internal */
final class AnonymousClassNode extends Node
{
/** @var array<Node> */
private array $arguments = [];
/** @var array<interface-string> */
private array $interfaces = [];
/** @var array<PropertyDeclarationNode> */
private array $properties = [];
/** @var array<non-empty-string, MethodNode> */
private array $methods = [];
public function withArguments(Node ...$arguments): self
{
$self = clone $this;
$self->arguments = $arguments;
return $self;
}
/**
* @param interface-string ...$interfaces
*/
public function implements(string ...$interfaces): self
{
$self = clone $this;
$self->interfaces = $interfaces;
return $self;
}
public function withProperties(PropertyDeclarationNode ...$properties): self
{
$self = clone $this;
$self->properties = $properties;
return $self;
}
public function withMethods(MethodNode ...$methods): self
{
$self = clone $this;
foreach ($methods as $method) {
$self->methods[$method->name()] = $method;
}
return $self;
}
public function hasMethod(string $name): bool
{
return isset($this->methods[$name]);
}
public function compile(Compiler $compiler): Compiler
{
$arguments = implode(', ', array_map(
fn (Node $argument) => $compiler->sub()->compile($argument)->code(),
$this->arguments,
));
$compiler = $compiler->write("new class ($arguments)");
if ($this->interfaces !== []) {
$compiler = $compiler->write(
' implements ' . implode(', ', $this->interfaces),
);
}
$body = [
...array_map(
fn (PropertyDeclarationNode $property) => $compiler->sub()->indent()->compile($property)->code(),
$this->properties,
),
...array_map(
fn (MethodNode $method) => $compiler->sub()->indent()->compile($method)->code(),
$this->methods,
),
];
$compiler = $compiler->write(' {');
if ($body !== []) {
$compiler = $compiler->write(PHP_EOL . implode(PHP_EOL . PHP_EOL, $body) . PHP_EOL);
}
return $compiler->write('}');
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ArrayKeyAccessNode extends Node
{
public function __construct(
private Node $node,
private Node $key,
) {}
public function compile(Compiler $compiler): Compiler
{
$key = $compiler->sub()->compile($this->key)->code();
return $compiler
->compile($this->node)
->write('[' . $key . ']');
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function var_export;
/** @internal */
final class ArrayNode extends Node
{
public function __construct(
/** @var array<Node> */
private array $assignments
) {}
public function compile(Compiler $compiler): Compiler
{
if ($this->assignments === []) {
return $compiler->write('[]');
}
$sub = [];
foreach ($this->assignments as $key => $assignment) {
$sub[] = ' ' . var_export($key, true) . ' => ' . $compiler->sub()->compile($assignment)->code() . ",";
}
$sub = implode("\n", $sub);
return $compiler->write("[\n$sub\n]");
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class AssignNode extends Node
{
public function __construct(
private Node $node,
private Node $value,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->node)
->write(' = ')
->compile($this->value);
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function array_map;
use function implode;
/** @internal */
final class CallNode extends Node
{
public function __construct(
private Node $node,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
$compiler = $compiler
->compile($this->node)
->write('(');
if ($this->arguments !== []) {
$arguments = array_map(
fn (Node $argument) => $compiler->sub()->compile($argument)->code(),
$this->arguments,
);
$compiler = $compiler->write(implode(', ', $arguments));
}
return $compiler->write(')');
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class CastNode extends Node
{
private function __construct(
private string $type,
private Node $node,
) {}
public static function toArray(Node $node): self
{
return new self('array', $node);
}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('(' . $this->type . ')')
->compile($this->node);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ClassNode extends Node
{
public function __construct(
/** @var class-string */
private string $name,
) {}
/**
* @param non-empty-string $method
* @param array<Node> $arguments
*/
public function callStaticMethod(
string $method,
array $arguments = [],
): Node {
return new StaticMethodCallNode($this, $method, $arguments);
}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($this->name);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class CloneNode extends Node
{
public function __construct(
private Node $node,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('clone ')
->compile($this->node);
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function array_map;
use function implode;
/** @internal */
final class ClosureNode extends Node
{
/** @var list<string> */
private array $use = [];
/** @var array<Node> */
private array $nodes;
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
/**
* @no-named-arguments
* @param non-empty-string ...$names
*/
public function uses(string ...$names): self
{
$self = clone $this;
$self->use = array_map(fn (string $name) => '$' . $name, $names);
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$use = $this->use !== [] ? ' use (' . implode(', ', $this->use) . ')' : '';
$body = $compiler->sub()->indent()->compile(...$this->nodes)->code();
$code = <<<PHP
function ()$use {
$body
}
PHP;
return $compiler->write($code);
}
}
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ComplianceNode extends Node
{
public function __construct(private Node $node) {}
/**
* @param non-empty-string $value
*/
public function access(string $value): self
{
return new self(new VariableAccessNode($this, $value));
}
/**
* @no-named-arguments
*/
public function and(Node ...$nodes): self
{
return new self(new LogicalAndNode($this, ...$nodes));
}
public function castToArray(): self
{
return new self(CastNode::toArray($this));
}
public function clone(): self
{
return new self(new CloneNode($this));
}
public function key(Node $key): self
{
return new self(new ArrayKeyAccessNode($this, $key));
}
public function assign(Node $value): self
{
return new self(new AssignNode($this, $value));
}
/**
* @param array<Node> $arguments
*/
public function call(array $arguments = []): self
{
return new self(new CallNode($this, $arguments));
}
/**
* @param non-empty-string $method
* @param array<Node> $arguments
*/
public function callMethod(string $method, array $arguments = []): self
{
return new self(new MethodCallNode($this, $method, $arguments));
}
public function different(Node $right): self
{
return new self(new DifferentNode($this, $right));
}
public function equals(Node $right): self
{
return new self(new EqualsNode($this, $right));
}
/**
* @no-named-arguments
*/
public function or(Node ...$nodes): self
{
return new self(new LogicalOrNode($this, ...$nodes));
}
/**
* @param class-string $className
*/
public function instanceOf(string $className): self
{
return new self(new InstanceOfNode($this, $className));
}
public function isLessThan(Node $right): self
{
return new self(new LessThanNode($this, $right));
}
public function isLessOrEqualsTo(Node $right): self
{
return new self(new LessOrEqualsToNode($this, $right));
}
public function isGreaterThan(Node $right): self
{
return new self(new GreaterThanNode($this, $right));
}
public function isGreaterOrEqualsTo(Node $right): self
{
return new self(new GreaterOrEqualsToNode($this, $right));
}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile($this->node);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class DifferentNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' !== ')
->compile($this->right);
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class EqualsNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' === ')
->compile($this->right);
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ExpressionNode extends Node
{
public function __construct(private Node $node) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($compiler->sub()->compile($this->node)->code() . ';');
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ForEachNode extends Node
{
public function __construct(
private Node $value,
/** @var non-empty-string */
private string $key,
/** @var non-empty-string */
private string $item,
private Node $body,
) {}
public function compile(Compiler $compiler): Compiler
{
$value = $compiler->sub()->compile($this->value)->code();
$body = $compiler->sub()->indent()->compile($this->body)->code();
return $compiler->write(
<<<PHP
foreach ($value as $$this->key => $$this->item) {
$body
}
PHP
);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class FunctionCallNode extends Node
{
public function __construct(
/** @var non-empty-string */
private string $name,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile(new CallNode(new FunctionNameNode($this->name), $this->arguments));
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function in_array;
/** @internal */
final class FunctionNameNode extends Node
{
private const RESERVED_FUNCTIONS = [
'isset',
];
public function __construct(
/** @var non-empty-string */
private string $name
) {}
public function compile(Compiler $compiler): Compiler
{
$function = in_array($this->name, self::RESERVED_FUNCTIONS, true)
? $this->name
: '\\' . $this->name;
return $compiler->write($function);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class GreaterOrEqualsToNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' >= ')
->compile($this->right);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class GreaterThanNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' > ')
->compile($this->right);
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class IfNode extends Node
{
public function __construct(
private Node $condition,
private Node $body,
) {}
public function compile(Compiler $compiler): Compiler
{
$condition = $compiler->sub()->compile($this->condition)->code();
$body = $compiler->sub()->indent()->compile($this->body)->code();
return $compiler->write(
<<<PHP
if ($condition) {
$body
}
PHP,
);
}
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class InstanceOfNode extends Node
{
public function __construct(
private Node $node,
/** @var class-string */
private string $className,
) {}
public function compile(Compiler $compiler): Compiler
{
$className = $this->className;
return $compiler
->compile($this->node)
->write(' instanceof ')
->write($className);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LessOrEqualsToNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' <= ')
->compile($this->right);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LessThanNode extends Node
{
public function __construct(
private Node $left,
private Node $right,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write(' < ')
->compile($this->right);
}
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LogicalAndNode extends Node
{
/** @var list<Node> */
private array $nodes;
/**
* @no-named-arguments
*/
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
public function compile(Compiler $compiler): Compiler
{
$nodes = $this->nodes;
while ($node = array_shift($nodes)) {
$compiler = $compiler->compile($node);
if ($nodes !== []) {
$compiler = $compiler->write(' && ');
}
}
return $compiler;
}
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class LogicalOrNode extends Node
{
/** @var list<Node> */
private array $nodes;
/**
* @no-named-arguments
*/
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
public function compile(Compiler $compiler): Compiler
{
$nodes = $this->nodes;
while ($node = array_shift($nodes)) {
$compiler = $compiler->compile($node);
if ($nodes !== []) {
$compiler = $compiler->write(' || ');
}
}
return $compiler;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class MatchNode extends Node
{
/** @var list<array{condition: Node, body: Node}> */
private array $cases = [];
private Node $defaultCase;
public function __construct(private Node $value) {}
public function withCase(Node $condition, Node $body): self
{
$self = clone $this;
$self->cases[] = ['condition' => $condition, 'body' => $body];
return $self;
}
public function withDefaultCase(Node $defaultCase): self
{
$self = clone $this;
$self->defaultCase = $defaultCase;
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$value = $compiler->sub()->compile($this->value)->code();
$body = [];
foreach ($this->cases as $case) {
$body[] = $compiler->sub()->indent()->compile($case['condition'])->code() .
' => ' .
$compiler->sub()->compile($case['body'])->code() .
',';
}
if (isset($this->defaultCase)) {
$body[] = $compiler->sub()->indent()->write('default')->code() .
' => ' .
$compiler->sub()->compile($this->defaultCase)->code() .
',';
}
$body = implode("\n", $body);
return $compiler->write(
<<<PHP
match ($value) {
$body
}
PHP,
);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class MethodCallNode extends Node
{
public function __construct(
private Node $node,
/** @var non-empty-string */
private string $method,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile(
new CallNode(new VariableAccessNode($this->node, $this->method), $this->arguments)
);
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function array_map;
/** @internal */
final class MethodNode extends Node
{
/** @var 'public'|'private' */
private string $visibility = 'private';
/** @var non-empty-string */
private string $name;
private string $returnType;
/** @var array<ParameterDeclarationNode> */
private array $parameters = [];
/** @var array<Node> */
private array $nodes = [];
/**
* @param non-empty-string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
public static function constructor(): self
{
return new self('__construct');
}
/**
* @return non-empty-string
*/
public function name(): string
{
return $this->name;
}
public function witParameters(ParameterDeclarationNode ...$parameters): self
{
$self = clone $this;
$self->parameters = $parameters;
return $self;
}
/**
* @param 'public'|'private' $visibility
*/
public function withVisibility(string $visibility): self
{
$self = clone $this;
$self->visibility = $visibility;
return $self;
}
/**
* @param non-empty-string $type
*/
public function withReturnType(string $type): self
{
$self = clone $this;
$self->returnType = $type;
return $self;
}
public function withBody(Node ...$nodes): self
{
$self = clone $this;
$self->nodes = $nodes;
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$parameters = implode(', ', array_map(
fn (ParameterDeclarationNode $parameter) => $compiler->sub()->compile($parameter)->code(),
$this->parameters,
));
$compiler = $compiler->write("$this->visibility function $this->name($parameters)");
if ($this->name !== '__construct') {
$compiler = $compiler->write(': ' . ($this->returnType ?? 'void'));
}
if ($this->nodes === []) {
return $compiler->write(' {}');
}
$body = $compiler->sub()->indent()->compile(...$this->nodes)->code();
return $compiler->write(PHP_EOL . '{' . PHP_EOL . $body . PHP_EOL . '}');
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class NegateNode extends Node
{
public function __construct(private Node $node) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('! ')->compile($this->node);
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function array_map;
/** @internal */
final class NewClassNode extends Node
{
/** @var class-string */
private string $className;
/** @var array<Node> */
private array $arguments;
/**
* @param class-string $className
*/
public function __construct(string $className, Node ...$arguments)
{
$this->className = $className;
$this->arguments = $arguments;
}
public function compile(Compiler $compiler): Compiler
{
$arguments = array_map(
fn (Node $argument) => $compiler->sub()->compile($argument)->code(),
$this->arguments,
);
$arguments = implode(', ', $arguments);
return $compiler->write("new {$this->className}($arguments)");
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ParameterDeclarationNode extends Node
{
public function __construct(
/** @var non-empty-string */
private string $name,
private string $type,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write($this->type . ' $' . $this->name);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class PhpFileNode extends Node
{
/** @var array<Node> */
private array $nodes;
public function __construct(Node ...$nodes)
{
$this->nodes = $nodes;
}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write("<?php\n\ndeclare(strict_types=1);\n\n")
->compile(...$this->nodes);
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class PropertyDeclarationNode extends Node
{
public function __construct(
/** @var non-empty-string */
private string $name,
private string $type,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('private ' . $this->type . ' $' . $this->name . ';');
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class PropertyNode extends Node
{
public function __construct(private string $name) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('$this->' . $this->name);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ReturnNode extends Node
{
public function __construct(private ?Node $node = null) {}
public function compile(Compiler $compiler): Compiler
{
$code = $this->node ? ' ' . $compiler->sub()->compile($this->node)->code() : '';
return $compiler->write("return$code;");
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ShortClosureNode extends Node
{
private Node $returnNode;
/** @var array<ParameterDeclarationNode> */
private array $parameters = [];
public function __construct(Node $returnNode)
{
$this->returnNode = $returnNode;
}
public function witParameters(ParameterDeclarationNode ...$parameters): self
{
$self = clone $this;
$self->parameters = $parameters;
return $self;
}
public function compile(Compiler $compiler): Compiler
{
$parameters = implode(', ', array_map(
fn (ParameterDeclarationNode $parameter) => $compiler->sub()->compile($parameter)->code(),
$this->parameters,
));
$return = $compiler->sub()->compile($this->returnNode)->code();
return $compiler->write(
<<<PHP
fn ($parameters) => $return
PHP,
);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class StaticAccessNode extends Node
{
public function __construct(
private Node $left,
/** @var non-empty-string */
private string $name,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->left)
->write("::$this->name");
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class StaticMethodCallNode extends Node
{
public function __construct(
private Node $node,
/** @var non-empty-string */
private string $method,
/** @var array<Node> */
private array $arguments = [],
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->compile(
new CallNode(new StaticAccessNode($this->node, $this->method), $this->arguments)
);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class TernaryNode extends Node
{
public function __construct(
private Node $condition,
private Node $ifTrue,
private Node $ifFalse,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->condition)
->write(' ? ')
->compile($this->ifTrue)
->write(' : ')
->compile($this->ifFalse);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class ThrowNode extends Node
{
public function __construct(
private Node $node,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('throw ')
->compile($this->node);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
use function is_array;
use function var_export;
/** @internal */
final class ValueNode extends Node
{
public function __construct(
/** @var array<mixed>|bool|float|int|string|null */
private array|bool|float|int|string|null $value,
) {}
public function compile(Compiler $compiler): Compiler
{
return $this->compileValue($this->value, $compiler);
}
private function compileValue(mixed $value, Compiler $compiler): Compiler
{
if (is_array($value)) {
$compiler = $compiler->write('[');
$i = 0;
$numItems = count($value);
foreach ($value as $key => $item) {
$compiler = $compiler->write(var_export($key, true) . ' => ');
$compiler = $this->compileValue($item, $compiler);
if (++$i !== $numItems) {
$compiler = $compiler->write(', ');
}
}
$compiler = $compiler->write(']');
} else {
$compiler = $compiler->write(var_export($value, true));
}
return $compiler;
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class VariableAccessNode extends Node
{
public function __construct(
private Node $node,
/** @var non-empty-string */
private string $value
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->compile($this->node)
->write('->' . $this->value);
}
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class VariableNode extends Node
{
public function __construct(private string $name) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler->write('$' . $this->name);
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class WrapNode extends Node
{
public function __construct(private Node $node) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('(')
->compile($this->node)
->write(')');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Node;
/** @internal */
final class YieldNode extends Node
{
public function __construct(
private Node $key,
private Node $value,
) {}
public function compile(Compiler $compiler): Compiler
{
return $compiler
->write('yield ')
->compile($this->key)
->write(' => ')
->compile($this->value);
}
}
+203
View File
@@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\AnonymousClassNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ArrayNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ClassNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ClosureNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ComplianceNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ExpressionNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ForEachNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\FunctionCallNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\IfNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\LogicalAndNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\LogicalOrNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\MatchNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\MethodNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\NegateNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\NewClassNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ParameterDeclarationNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\PropertyDeclarationNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\PropertyNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ReturnNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ShortClosureNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\TernaryNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ThrowNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\ValueNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\VariableNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\WrapNode;
use OCA\Talk\Vendor\CuyZ\Valinor\Compiler\Native\YieldNode;
/** @internal */
abstract class Node
{
abstract public function compile(Compiler $compiler): Compiler;
public function asExpression(): ExpressionNode
{
return new ExpressionNode($this);
}
public function wrap(): ComplianceNode
{
return new ComplianceNode(new WrapNode($this));
}
/**
* @param array<Node> $assignments
*/
public static function array(array $assignments = []): ArrayNode
{
return new ArrayNode($assignments);
}
public static function anonymousClass(): AnonymousClassNode
{
return new AnonymousClassNode();
}
/**
* @param class-string $name
*/
public static function class(string $name): ClassNode
{
return new ClassNode($name);
}
public static function closure(Node ...$nodes): ClosureNode
{
return new ClosureNode(...$nodes);
}
/**
* @param non-empty-string $key
* @param non-empty-string $item
*/
public static function forEach(Node $value, string $key, string $item, Node $body): ForEachNode
{
return new ForEachNode($value, $key, $item, $body);
}
/**
* @param non-empty-string $name
* @param array<Node> $arguments
*/
public static function functionCall(string $name, array $arguments = []): ComplianceNode
{
return new ComplianceNode(new FunctionCallNode($name, $arguments));
}
public static function if(Node $condition, Node $body): IfNode
{
return new IfNode($condition, $body);
}
/**
* @no-named-arguments
*/
public static function logicalAnd(Node ...$nodes): ComplianceNode
{
return new ComplianceNode(new LogicalAndNode(...$nodes));
}
/**
* @no-named-arguments
*/
public static function logicalOr(Node ...$nodes): ComplianceNode
{
return new ComplianceNode(new LogicalOrNode(...$nodes));
}
public static function match(Node $value): MatchNode
{
return new MatchNode($value);
}
/**
* @param non-empty-string $name
*/
public static function method(string $name): MethodNode
{
return new MethodNode($name);
}
public static function negate(Node $node): NegateNode
{
return new NegateNode($node);
}
/**
* @param class-string $className
*/
public static function newClass(string $className, Node ...$arguments): NewClassNode
{
return new NewClassNode($className, ...$arguments);
}
/**
* @param non-empty-string $name
*/
public static function parameterDeclaration(string $name, string $type): ParameterDeclarationNode
{
return new ParameterDeclarationNode($name, $type);
}
public static function property(string $name): ComplianceNode
{
return new ComplianceNode(new PropertyNode($name));
}
/**
* @param non-empty-string $name
*/
public static function propertyDeclaration(string $name, string $type): PropertyDeclarationNode
{
return new PropertyDeclarationNode($name, $type);
}
public static function return(Node $node): ReturnNode
{
return new ReturnNode($node);
}
public static function shortClosure(Node $return): ShortClosureNode
{
return new ShortClosureNode($return);
}
public static function ternary(Node $condition, Node $ifTrue, Node $ifFalse): TernaryNode
{
return new TernaryNode($condition, $ifTrue, $ifFalse);
}
public static function this(): ComplianceNode
{
return self::variable('this');
}
public static function throw(Node $node): ThrowNode
{
return new ThrowNode($node);
}
/**
* @param array<mixed>|bool|float|int|string|null $value
*/
public static function value(array|bool|float|int|string|null $value): ComplianceNode
{
return new ComplianceNode(new ValueNode($value));
}
public static function variable(string $name): ComplianceNode
{
return new ComplianceNode(new VariableNode($name));
}
public static function yield(Node $key, Node $value): YieldNode
{
return new YieldNode($key, $value);
}
}
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
/** @internal */
final class AttributeDefinition
{
public function __construct(
public readonly ClassDefinition $class,
/** @var list<mixed> */
public readonly array $arguments,
) {}
public function instantiate(): object
{
return new ($this->class->type->className())(...$this->arguments);
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use Countable;
use IteratorAggregate;
use Traversable;
use function array_filter;
use function count;
use function is_a;
/**
* @internal
*
* @implements IteratorAggregate<AttributeDefinition>
*/
final class Attributes implements IteratorAggregate, Countable
{
private static self $empty;
/** @var list<AttributeDefinition> */
private array $attributes;
/**
* @no-named-arguments
*/
public function __construct(AttributeDefinition ...$attributes)
{
$this->attributes = $attributes;
}
public static function empty(): self
{
return self::$empty ??= new self();
}
public function has(string $className): bool
{
foreach ($this->attributes as $attribute) {
if (is_a($attribute->class->type->className(), $className, true)) {
return true;
}
}
return false;
}
/**
* @param callable(AttributeDefinition): bool $callback
*/
public function filter(callable $callback): self
{
return new self(
...array_filter($this->attributes, $callback)
);
}
public function count(): int
{
return count($this->attributes);
}
/**
* @return list<AttributeDefinition>
*/
public function toArray(): array
{
return $this->attributes;
}
/**
* @return Traversable<AttributeDefinition>
*/
public function getIterator(): Traversable
{
yield from $this->attributes;
}
}
+22
View File
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
/** @internal */
final class ClassDefinition
{
public function __construct(
/** @var class-string */
public readonly string $name,
public readonly ObjectType $type,
public readonly Attributes $attributes,
public readonly Properties $properties,
public readonly Methods $methods,
public readonly bool $isFinal,
public readonly bool $isAbstract,
) {}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use LogicException;
use function implode;
/** @internal */
final class ClassTypeAliasesDuplication extends LogicException
{
/**
* @param class-string $className
*/
public function __construct(string $className, string ...$names)
{
$names = implode('`, `', $names);
parent::__construct(
"The following type aliases already exist in class `$className`: `$names`.",
1638477604
);
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use ReflectionClass;
use RuntimeException;
/** @internal */
final class ExtendTagTypeError extends RuntimeException
{
/**
* @param ReflectionClass<object> $reflection
*/
public function __construct(ReflectionClass $reflection, InvalidType $previous)
{
parent::__construct(
"The `@extends` tag of the class `$reflection->name` is not valid: {$previous->getMessage()}",
1670193574,
$previous,
);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use ReflectionClass;
use RuntimeException;
/** @internal */
final class InvalidExtendTagClassName extends RuntimeException
{
/**
* @param ReflectionClass<object> $reflection
*/
public function __construct(ReflectionClass $reflection, Type $invalidExtendTag)
{
/** @var ReflectionClass<object> $parentClass */
$parentClass = $reflection->getParentClass();
parent::__construct(
"The `@extends` tag of the class `$reflection->name` has invalid class `{$invalidExtendTag->toString()}`, it should be `$parentClass->name`.",
1670183564,
);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use ReflectionClass;
use RuntimeException;
/** @internal */
final class InvalidExtendTagType extends RuntimeException
{
/**
* @param ReflectionClass<object> $reflection
*/
public function __construct(ReflectionClass $reflection, Type $invalidExtendTag)
{
/** @var ReflectionClass<object> $parentClass */
$parentClass = $reflection->getParentClass();
parent::__construct(
"The `@extends` tag of the class `$reflection->name` has invalid type `{$invalidExtendTag->toString()}`, it should be `{$parentClass->name}`.",
1670181134,
);
}
}
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use LogicException;
/** @internal */
final class InvalidTypeAliasImportClass extends LogicException
{
public function __construct(ObjectType $type, string $className)
{
parent::__construct(
"Cannot import a type alias from unknown class `$className` in class `{$type->className()}`.",
1638535486
);
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use LogicException;
/** @internal */
final class InvalidTypeAliasImportClassType extends LogicException
{
public function __construct(ObjectType $classType, Type $type)
{
parent::__construct(
"Importing a type alias can only be done with classes, `{$type->toString()}` was given in class `{$classType->className()}`.",
1638535608
);
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use ReflectionClass;
use RuntimeException;
/** @internal */
final class SeveralExtendTagsFound extends RuntimeException
{
/**
* @param ReflectionClass<object> $reflection
*/
public function __construct(ReflectionClass $reflection)
{
parent::__construct(
"Only one `@extends` tag should be set for the class `$reflection->name`.",
1670195494,
);
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use LogicException;
/** @internal */
final class UnknownTypeAliasImport extends LogicException
{
/**
* @param class-string $importClassName
*/
public function __construct(ObjectType $type, string $importClassName, string $alias)
{
parent::__construct(
"Type alias `$alias` imported in `{$type->className()}` could not be found in `$importClassName`",
1638535757
);
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
/** @internal */
final class FunctionDefinition
{
public function __construct(
/** @var non-empty-string */
public readonly string $name,
/** @var non-empty-string */
public readonly string $signature,
public readonly Attributes $attributes,
/** @var non-empty-string|null */
public readonly ?string $fileName,
/** @var class-string|null */
public readonly ?string $class,
public readonly bool $isStatic,
public readonly bool $isClosure,
public readonly Parameters $parameters,
public readonly Type $returnType
) {}
}
+20
View File
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
/** @internal */
final class FunctionObject
{
public readonly FunctionDefinition $definition;
/** @var callable */
public readonly mixed $callback;
public function __construct(FunctionDefinition $definition, callable $callback)
{
$this->definition = $definition;
$this->callback = $callback;
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use Countable;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use IteratorAggregate;
use Traversable;
use function array_keys;
use function count;
/**
* @internal
*
* @implements IteratorAggregate<string|int, FunctionObject>
*/
final class FunctionsContainer implements IteratorAggregate, Countable
{
/** @var array<FunctionObject> */
private array $functions = [];
public function __construct(
private FunctionDefinitionRepository $functionDefinitionRepository,
/** @var array<callable> */
private array $callables
) {}
public function has(string|int $key): bool
{
return isset($this->callables[$key]);
}
public function get(string|int $key): FunctionObject
{
return $this->function($key);
}
public function getIterator(): Traversable
{
foreach (array_keys($this->callables) as $key) {
yield $key => $this->function($key);
}
}
private function function(string|int $key): FunctionObject
{
return $this->functions[$key] ??= new FunctionObject(
$this->functionDefinitionRepository->for($this->callables[$key]),
$this->callables[$key]
);
}
public function count(): int
{
return count($this->callables);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
/** @internal */
final class MethodDefinition
{
public function __construct(
/** @var non-empty-string */
public readonly string $name,
/** @var non-empty-string */
public readonly string $signature,
public readonly Attributes $attributes,
public readonly Parameters $parameters,
public readonly bool $isStatic,
public readonly bool $isPublic,
public readonly Type $returnType
) {}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use Countable;
use IteratorAggregate;
use Traversable;
/**
* @internal
*
* @implements IteratorAggregate<string, MethodDefinition>
*/
final class Methods implements IteratorAggregate, Countable
{
/** @var MethodDefinition[] */
private array $methods = [];
public function __construct(MethodDefinition ...$methods)
{
foreach ($methods as $method) {
$this->methods[$method->name] = $method;
}
}
public function has(string $name): bool
{
return isset($this->methods[$name]);
}
public function get(string $name): MethodDefinition
{
return $this->methods[$name];
}
public function hasConstructor(): bool
{
return $this->has('__construct');
}
public function constructor(): MethodDefinition
{
return $this->get('__construct');
}
public function count(): int
{
return count($this->methods);
}
/**
* @return Traversable<string, MethodDefinition>
*/
public function getIterator(): Traversable
{
yield from $this->methods;
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
/** @internal */
final class ParameterDefinition
{
public function __construct(
/** @var non-empty-string */
public readonly string $name,
/** @var non-empty-string */
public readonly string $signature,
public readonly Type $type,
public readonly Type $nativeType,
public readonly bool $isOptional,
public readonly bool $isVariadic,
public readonly mixed $defaultValue,
public readonly Attributes $attributes
) {}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use Countable;
use IteratorAggregate;
use Traversable;
use function array_values;
/**
* @internal
*
* @implements IteratorAggregate<string, ParameterDefinition>
*/
final class Parameters implements IteratorAggregate, Countable
{
/** @var ParameterDefinition[] */
private array $parameters = [];
public function __construct(ParameterDefinition ...$parameters)
{
foreach ($parameters as $parameter) {
$this->parameters[$parameter->name] = $parameter;
}
}
public function has(string $name): bool
{
return isset($this->parameters[$name]);
}
public function get(string $name): ParameterDefinition
{
return $this->parameters[$name];
}
/**
* @param int<0, max> $index
*/
public function at(int $index): ParameterDefinition
{
return array_values($this->parameters)[$index];
}
/**
* @return list<ParameterDefinition>
*/
public function toList(): array
{
return array_values($this->parameters);
}
public function count(): int
{
return count($this->parameters);
}
/**
* @return Traversable<string, ParameterDefinition>
*/
public function getIterator(): Traversable
{
yield from $this->parameters;
}
}
+50
View File
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use Countable;
use IteratorAggregate;
use Traversable;
/**
* @internal
*
* @implements IteratorAggregate<string, PropertyDefinition>
*/
final class Properties implements IteratorAggregate, Countable
{
/** @var PropertyDefinition[] */
private array $properties = [];
public function __construct(PropertyDefinition ...$properties)
{
foreach ($properties as $property) {
$this->properties[$property->name] = $property;
}
}
public function has(string $name): bool
{
return isset($this->properties[$name]);
}
public function get(string $name): PropertyDefinition
{
return $this->properties[$name];
}
public function count(): int
{
return count($this->properties);
}
/**
* @return Traversable<string, PropertyDefinition>
*/
public function getIterator(): Traversable
{
yield from $this->properties;
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
/** @internal */
final class PropertyDefinition
{
public function __construct(
/** @var non-empty-string */
public readonly string $name,
/** @var non-empty-string */
public readonly string $signature,
public readonly Type $type,
public readonly Type $nativeType,
public readonly bool $hasDefaultValue,
public readonly mixed $defaultValue,
public readonly bool $isPublic,
public readonly Attributes $attributes
) {}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\AttributeDefinition;
use ReflectionClass;
use ReflectionFunction;
use ReflectionMethod;
use ReflectionParameter;
use ReflectionProperty;
use Reflector;
/** @internal */
interface AttributesRepository
{
/**
* @param ReflectionClass<object>|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflection
* @return list<AttributeDefinition>
*/
public function for(Reflector $reflection): array;
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
/** @internal */
final class CacheClassDefinitionRepository implements ClassDefinitionRepository
{
public function __construct(
private ClassDefinitionRepository $delegate,
/** @var CacheInterface<ClassDefinition> */
private CacheInterface $cache
) {}
public function for(ObjectType $type): ClassDefinition
{
// @infection-ignore-all
$key = "class-definition-\0" . $type->toString();
$entry = $this->cache->get($key);
if ($entry) {
return $entry;
}
$class = $this->delegate->for($type);
$this->cache->set($key, $class);
return $class;
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use OCA\Talk\Vendor\Psr\SimpleCache\CacheInterface;
/** @internal */
final class CacheFunctionDefinitionRepository implements FunctionDefinitionRepository
{
public function __construct(
private FunctionDefinitionRepository $delegate,
/** @var CacheInterface<FunctionDefinition> */
private CacheInterface $cache
) {}
public function for(callable $function): FunctionDefinition
{
$reflection = Reflection::function($function);
// @infection-ignore-all
$key = "function-definition-\0" . $reflection->getFileName() . ':' . $reflection->getStartLine() . '-' . $reflection->getEndLine();
$entry = $this->cache->get($key);
if ($entry) {
return $entry;
}
$definition = $this->delegate->for($function);
$this->cache->set($key, $definition);
return $definition;
}
}
@@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes;
use function array_map;
use function count;
use function implode;
use function is_array;
use function is_object;
use function var_export;
/** @internal */
final class AttributesCompiler
{
public function __construct(private ClassDefinitionCompiler $classDefinitionCompiler) {}
public function compile(Attributes $attributes): string
{
if (count($attributes) === 0) {
return Attributes::class . '::empty()';
}
$attributesListCode = $this->compileAttributes($attributes);
return <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes($attributesListCode)
PHP;
}
private function compileAttributes(Attributes $attributes): string
{
$attributesListCode = [];
foreach ($attributes as $attribute) {
$class = $this->classDefinitionCompiler->compile($attribute->class);
if ($attribute->arguments === []) {
$arguments = '';
} else {
$arguments = implode(', ', array_map(
fn (mixed $argument) => $this->compileAttributeArguments($argument),
$attribute->arguments,
));
}
$attributesListCode[] = <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\AttributeDefinition(
$class,
[$arguments],
)
PHP;
}
return implode(', ', $attributesListCode);
}
private function compileAttributeArguments(mixed $value): string
{
if (is_object($value)) {
return 'unserialize(' . var_export(serialize($value), true) . ')';
}
if (is_array($value)) {
$parts = [];
foreach ($value as $key => $subValue) {
$parts[] = var_export($key, true) . ' => ' . $this->compileAttributeArguments($subValue);
}
return '[' . implode(', ', $parts) . ']';
}
return var_export($value, true);
}
}
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\MethodDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\PropertyDefinition;
use function array_map;
use function assert;
use function implode;
use function iterator_to_array;
use function var_export;
/** @internal */
final class ClassDefinitionCompiler
{
private TypeCompiler $typeCompiler;
private AttributesCompiler $attributesCompiler;
private MethodDefinitionCompiler $methodCompiler;
private PropertyDefinitionCompiler $propertyCompiler;
public function __construct()
{
$this->typeCompiler = new TypeCompiler();
$this->attributesCompiler = new AttributesCompiler($this);
$this->methodCompiler = new MethodDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
$this->propertyCompiler = new PropertyDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
}
public function compile(mixed $value): string
{
assert($value instanceof ClassDefinition);
$name = var_export($value->name, true);
$type = $this->typeCompiler->compile($value->type);
$properties = array_map(
fn (PropertyDefinition $property) => $this->propertyCompiler->compile($property),
iterator_to_array($value->properties)
);
$properties = implode(', ', $properties);
$methods = array_map(
fn (MethodDefinition $method) => $this->methodCompiler->compile($method),
iterator_to_array($value->methods)
);
$methods = implode(', ', $methods);
$attributes = $this->attributesCompiler->compile($value->attributes);
$isFinal = var_export($value->isFinal, true);
$isAbstract = var_export($value->isAbstract, true);
return <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition(
$name,
$type,
$attributes,
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Properties($properties),
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Methods($methods),
$isFinal,
$isAbstract,
)
PHP;
}
}
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler\Exception;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use LogicException;
/** @internal */
final class TypeCannotBeCompiled extends LogicException
{
public function __construct(Type $type)
{
$class = $type::class;
parent::__construct(
"The type `$class` cannot be compiled.",
1616926126
);
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ParameterDefinition;
use function var_export;
/** @internal */
final class FunctionDefinitionCompiler
{
private TypeCompiler $typeCompiler;
private AttributesCompiler $attributesCompiler;
private ParameterDefinitionCompiler $parameterCompiler;
public function __construct()
{
$this->typeCompiler = new TypeCompiler();
$this->attributesCompiler = new AttributesCompiler(new ClassDefinitionCompiler());
$this->parameterCompiler = new ParameterDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
}
public function compile(mixed $value): string
{
assert($value instanceof FunctionDefinition);
$parameters = array_map(
fn (ParameterDefinition $parameter) => $this->parameterCompiler->compile($parameter),
iterator_to_array($value->parameters)
);
$attributes = $this->attributesCompiler->compile($value->attributes);
$fileName = var_export($value->fileName, true);
$class = var_export($value->class, true);
$isStatic = var_export($value->isStatic, true);
$isClosure = var_export($value->isClosure, true);
$parameters = implode(', ', $parameters);
$returnType = $this->typeCompiler->compile($value->returnType);
return <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition(
'{$value->name}',
'{$value->signature}',
$attributes,
$fileName,
$class,
$isStatic,
$isClosure,
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Parameters($parameters),
$returnType
)
PHP;
}
}
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\MethodDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ParameterDefinition;
use function var_export;
/** @internal */
final class MethodDefinitionCompiler
{
private TypeCompiler $typeCompiler;
private AttributesCompiler $attributesCompiler;
private ParameterDefinitionCompiler $parameterCompiler;
public function __construct(TypeCompiler $typeCompiler, AttributesCompiler $attributesCompiler)
{
$this->typeCompiler = $typeCompiler;
$this->attributesCompiler = $attributesCompiler;
$this->parameterCompiler = new ParameterDefinitionCompiler($typeCompiler, $attributesCompiler);
}
public function compile(MethodDefinition $method): string
{
$attributes = $this->attributesCompiler->compile($method->attributes);
$parameters = array_map(
fn (ParameterDefinition $parameter) => $this->parameterCompiler->compile($parameter),
iterator_to_array($method->parameters)
);
$parameters = implode(', ', $parameters);
$isStatic = var_export($method->isStatic, true);
$isPublic = var_export($method->isPublic, true);
$returnType = $this->typeCompiler->compile($method->returnType);
return <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\MethodDefinition(
'{$method->name}',
'{$method->signature}',
$attributes,
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\Parameters($parameters),
$isStatic,
$isPublic,
$returnType
)
PHP;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ParameterDefinition;
/** @internal */
final class ParameterDefinitionCompiler
{
public function __construct(
private TypeCompiler $typeCompiler,
private AttributesCompiler $attributesCompiler
) {}
public function compile(ParameterDefinition $parameter): string
{
$isOptional = var_export($parameter->isOptional, true);
$isVariadic = var_export($parameter->isVariadic, true);
$defaultValue = $this->defaultValue($parameter);
$type = $this->typeCompiler->compile($parameter->type);
$nativeType = $this->typeCompiler->compile($parameter->nativeType);
$attributes = $this->attributesCompiler->compile($parameter->attributes);
return <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\ParameterDefinition(
'{$parameter->name}',
'{$parameter->signature}',
$type,
$nativeType,
$isOptional,
$isVariadic,
$defaultValue,
$attributes
)
PHP;
}
private function defaultValue(ParameterDefinition $parameter): string
{
$defaultValue = $parameter->defaultValue;
return is_object($defaultValue)
? 'unserialize(' . var_export(serialize($defaultValue), true) . ')'
: var_export($defaultValue, true);
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\PropertyDefinition;
/** @internal */
final class PropertyDefinitionCompiler
{
public function __construct(
private TypeCompiler $typeCompiler,
private AttributesCompiler $attributesCompiler
) {}
public function compile(PropertyDefinition $property): string
{
$type = $this->typeCompiler->compile($property->type);
$nativeType = $this->typeCompiler->compile($property->nativeType);
$hasDefaultValue = var_export($property->hasDefaultValue, true);
$defaultValue = var_export($property->defaultValue, true);
$isPublic = var_export($property->isPublic, true);
$attributes = $this->attributesCompiler->compile($property->attributes);
return <<<PHP
new \OCA\Talk\Vendor\CuyZ\Valinor\Definition\PropertyDefinition(
'{$property->name}',
'{$property->signature}',
$type,
$nativeType,
$hasDefaultValue,
$defaultValue,
$isPublic,
$attributes
)
PHP;
}
}
@@ -0,0 +1,193 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Cache\Compiler\Exception\TypeCannotBeCompiled;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ArrayKeyType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ArrayType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\BooleanValueType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\CallableType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ClassStringType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\EnumType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\FloatValueType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\IntegerRangeType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\IntegerValueType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\InterfaceType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\IntersectionType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\IterableType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ListType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\MixedType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NativeBooleanType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NativeClassType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NativeFloatType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NativeIntegerType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NativeStringType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NegativeIntegerType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NonEmptyArrayType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NonEmptyListType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NonEmptyStringType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NonNegativeIntegerType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NonPositiveIntegerType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NullType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NumericStringType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\PositiveIntegerType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ScalarConcreteType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ShapedArrayElement;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\ShapedArrayType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\StringValueType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UndefinedObjectType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnionType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnresolvableType;
use UnitEnum;
use function array_keys;
use function array_map;
use function implode;
use function var_export;
/** @internal */
final class TypeCompiler
{
public function compile(Type $type): string
{
$class = $type::class;
switch (true) {
case $type instanceof NullType:
case $type instanceof NativeBooleanType:
case $type instanceof NativeFloatType:
case $type instanceof NativeIntegerType:
case $type instanceof PositiveIntegerType:
case $type instanceof NegativeIntegerType:
case $type instanceof NonPositiveIntegerType:
case $type instanceof NonNegativeIntegerType:
case $type instanceof NativeStringType:
case $type instanceof NonEmptyStringType:
case $type instanceof NumericStringType:
case $type instanceof UndefinedObjectType:
case $type instanceof CallableType:
case $type instanceof MixedType:
case $type instanceof ScalarConcreteType:
return "$class::get()";
case $type instanceof BooleanValueType:
return $type->value() === true
? "$class::true()"
: "$class::false()";
case $type instanceof IntegerRangeType:
return "new $class({$type->min()}, {$type->max()})";
case $type instanceof StringValueType:
$value = var_export($type->toString(), true);
return "$class::from($value)";
case $type instanceof IntegerValueType:
case $type instanceof FloatValueType:
$value = var_export($type->value(), true);
return "new $class($value)";
case $type instanceof IntersectionType:
case $type instanceof UnionType:
$subTypes = array_map(
fn (Type $subType) => $this->compile($subType),
$type->types()
);
return "new $class(" . implode(', ', $subTypes) . ')';
case $type instanceof ArrayKeyType:
return match ($type->toString()) {
'string' => "$class::string()",
'int' => "$class::integer()",
default => "$class::default()",
};
case $type instanceof ShapedArrayType:
$elements = implode(', ', array_map(
fn (ShapedArrayElement $element) => $this->compileArrayShapeElement($element),
$type->elements()
));
if ($type->hasUnsealedType()) {
$unsealedType = $this->compile($type->unsealedType());
return "$class::unsealed($unsealedType, $elements)";
} elseif ($type->isUnsealed()) {
return "$class::unsealedWithoutType($elements)";
}
return "new $class($elements)";
case $type instanceof ArrayType:
case $type instanceof NonEmptyArrayType:
if ($type->toString() === 'array' || $type->toString() === 'non-empty-array') {
return "$class::native()";
}
$keyType = $this->compile($type->keyType());
$subType = $this->compile($type->subType());
return "new $class($keyType, $subType)";
case $type instanceof ListType:
case $type instanceof NonEmptyListType:
if ($type->toString() === 'list' || $type->toString() === 'non-empty-list') {
return "$class::native()";
}
$subType = $this->compile($type->subType());
return "new $class($subType)";
case $type instanceof IterableType:
$keyType = $this->compile($type->keyType());
$subType = $this->compile($type->subType());
return "new $class($keyType, $subType)";
case $type instanceof NativeClassType:
case $type instanceof InterfaceType:
$generics = [];
foreach ($type->generics() as $key => $generic) {
$generics[] = var_export($key, true) . ' => ' . $this->compile($generic);
}
$generics = implode(', ', $generics);
return "new $class('{$type->className()}', [$generics])";
case $type instanceof ClassStringType:
if (null === $type->subType()) {
return "new $class()";
}
$subType = $this->compile($type->subType());
return "new $class($subType)";
case $type instanceof EnumType:
$enumName = var_export($type->className(), true);
$pattern = var_export($type->pattern(), true);
$cases = array_map(
fn (string|int $key, UnitEnum $case) => var_export($key, true) . ' => ' . var_export($case, true),
array_keys($type->cases()),
$type->cases()
);
$cases = implode(', ', $cases);
return "new $class($enumName, $pattern, [$cases])";
case $type instanceof UnresolvableType:
$raw = var_export($type->toString(), true);
$message = var_export($type->message(), true);
return "new $class($raw, $message)";
default:
throw new TypeCannotBeCompiled($type);
}
}
private function compileArrayShapeElement(ShapedArrayElement $element): string
{
$class = ShapedArrayElement::class;
$key = $this->compile($element->key());
$type = $this->compile($element->type());
$optional = var_export($element->isOptional(), true);
return "new $class($key, $type, $optional)";
}
}
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
/** @internal */
interface ClassDefinitionRepository
{
public function for(ObjectType $type): ClassDefinition;
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition;
/** @internal */
interface FunctionDefinitionRepository
{
public function for(callable $function): FunctionDefinition;
}
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\AttributeDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\AttributesRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Normalizer\AsTransformer;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NativeClassType;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use Error;
use ReflectionAttribute;
use Reflector;
use function array_map;
use function array_values;
/** @internal */
final class ReflectionAttributesRepository implements AttributesRepository
{
public function __construct(
private ClassDefinitionRepository $classDefinitionRepository,
/** @var list<class-string> */
private array $allowedAttributes,
) {}
public function for(Reflector $reflection): array
{
$attributes = array_filter(
$reflection->getAttributes(),
function (ReflectionAttribute $attribute) {
foreach ($this->allowedAttributes as $allowedAttribute) {
if (is_a($attribute->getName(), $allowedAttribute, true)) {
return $this->attributeCanBeInstantiated($attribute);
}
}
$parentAttributes = Reflection::class($attribute->getName())->getAttributes(AsTransformer::class);
return $parentAttributes !== [];
},
);
return array_values(array_map(
fn (ReflectionAttribute $attribute) => new AttributeDefinition(
$this->classDefinitionRepository->for(new NativeClassType($attribute->getName())),
array_values($attribute->getArguments()),
),
$attributes,
));
}
/**
* @param ReflectionAttribute<object> $attribute
*/
private function attributeCanBeInstantiated(ReflectionAttribute $attribute): bool
{
try {
$attribute->newInstance();
return true;
} catch (Error) {
// Race condition when the attribute is affected to a property/parameter
// that was PROMOTED, in this case the attribute will be applied to both
// ParameterReflection AND PropertyReflection, BUT the target arg inside the attribute
// class is configured to support only ONE of them (parameter OR property)
// https://wiki.php.net/rfc/constructor_promotion#attributes for more details.
// Ignore attribute if the instantiation failed.
return false;
}
}
}
@@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ClassDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception\ClassTypeAliasesDuplication;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\MethodDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Methods;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Properties;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\PropertyDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\AttributesRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassImportedTypeAliasResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassLocalTypeAliasResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ClassParentTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\GenericType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
use function array_filter;
use function array_keys;
use function array_map;
/** @internal */
final class ReflectionClassDefinitionRepository implements ClassDefinitionRepository
{
private TypeParserFactory $typeParserFactory;
private AttributesRepository $attributesRepository;
private ReflectionPropertyDefinitionBuilder $propertyBuilder;
private ReflectionMethodDefinitionBuilder $methodBuilder;
/** @var array<string, ReflectionTypeResolver> */
private array $typeResolver = [];
/**
* @param list<class-string> $allowedAttributes
*/
public function __construct(
TypeParserFactory $typeParserFactory,
array $allowedAttributes,
) {
$this->typeParserFactory = $typeParserFactory;
$this->attributesRepository = new ReflectionAttributesRepository($this, $allowedAttributes);
$this->propertyBuilder = new ReflectionPropertyDefinitionBuilder($this->attributesRepository);
$this->methodBuilder = new ReflectionMethodDefinitionBuilder($this->attributesRepository);
}
public function for(ObjectType $type): ClassDefinition
{
$reflection = Reflection::class($type->className());
return new ClassDefinition(
$reflection->name,
$type,
new Attributes(...$this->attributesRepository->for($reflection)),
new Properties(...$this->properties($type)),
new Methods(...$this->methods($type)),
$reflection->isFinal(),
$reflection->isAbstract(),
);
}
/**
* @return list<PropertyDefinition>
*/
private function properties(ObjectType $type): array
{
$reflection = Reflection::class($type->className());
$properties = [];
foreach ($reflection->getProperties() as $property) {
$typeResolver = $this->typeResolver($type, $property->getDeclaringClass());
$properties[$property->name] = $this->propertyBuilder->for($property, $typeResolver);
}
// Properties will be sorted by inheritance order, from parent to child.
$sortedProperties = [];
while ($reflection) {
$currentProperties = array_map(
fn (ReflectionProperty $property) => $properties[$property->name],
array_filter(
$reflection->getProperties(),
fn (ReflectionProperty $property) => isset($properties[$property->name]),
),
);
$sortedProperties = [...$currentProperties, ...$sortedProperties];
$reflection = $reflection->getParentClass();
}
return $sortedProperties;
}
/**
* @return list<MethodDefinition>
*/
private function methods(ObjectType $type): array
{
$reflection = Reflection::class($type->className());
$methods = $reflection->getMethods();
// Because `ReflectionMethod::getMethods()` wont list the constructor if
// it comes from a parent class AND is not public, we need to manually
// fetch it and add it to the list.
if ($reflection->hasMethod('__construct')) {
$methods[] = $reflection->getMethod('__construct');
}
return array_map(function (ReflectionMethod $method) use ($type) {
$typeResolver = $this->typeResolver($type, $method->getDeclaringClass());
return $this->methodBuilder->for($method, $typeResolver);
}, $methods);
}
/**
* @param ReflectionClass<object> $target
*/
private function typeResolver(ObjectType $type, ReflectionClass $target): ReflectionTypeResolver
{
$typeKey = $target->isInterface()
? "{$type->toString()}/{$type->className()}"
: "{$type->toString()}/$target->name";
if (isset($this->typeResolver[$typeKey])) {
return $this->typeResolver[$typeKey];
}
$parentTypeResolver = new ClassParentTypeResolver($this->typeParserFactory);
while ($type->className() !== $target->name) {
$type = $parentTypeResolver->resolveParentTypeFor($type);
}
$localTypeAliasResolver = new ClassLocalTypeAliasResolver($this->typeParserFactory);
$importedTypeAliasResolver = new ClassImportedTypeAliasResolver($this->typeParserFactory);
$generics = $type instanceof GenericType ? $type->generics() : [];
$localAliases = $localTypeAliasResolver->resolveLocalTypeAliases($type);
$importedAliases = $importedTypeAliasResolver->resolveImportedTypeAliases($type);
$duplicates = [];
$keys = [...array_keys($generics), ...array_keys($localAliases), ...array_keys($importedAliases)];
foreach ($keys as $key) {
$sameKeys = array_filter($keys, fn ($value) => $value === $key);
if (count($sameKeys) > 1) {
$duplicates[$key] = null;
}
}
if (count($duplicates) > 0) {
throw new ClassTypeAliasesDuplication($type->className(), ...array_keys($duplicates));
}
$advancedParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type, $generics + $localAliases + $importedAliases);
$nativeParser = $this->typeParserFactory->buildNativeTypeParserForClass($type->className());
return $this->typeResolver[$typeKey] = new ReflectionTypeResolver($nativeParser, $advancedParser);
}
}
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\FunctionDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Parameters;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\AttributesRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\FunctionReturnTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnresolvableType;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionFunction;
use ReflectionParameter;
use function array_map;
use function str_ends_with;
use function str_starts_with;
/** @internal */
final class ReflectionFunctionDefinitionRepository implements FunctionDefinitionRepository
{
private TypeParserFactory $typeParserFactory;
private AttributesRepository $attributesRepository;
private ReflectionParameterDefinitionBuilder $parameterBuilder;
public function __construct(TypeParserFactory $typeParserFactory, AttributesRepository $attributesRepository)
{
$this->typeParserFactory = $typeParserFactory;
$this->attributesRepository = $attributesRepository;
$this->parameterBuilder = new ReflectionParameterDefinitionBuilder($attributesRepository);
}
public function for(callable $function): FunctionDefinition
{
$reflection = Reflection::function($function);
$nativeParser = $this->typeParserFactory->buildNativeTypeParserForFunction($reflection);
$advancedParser = $this->typeParserFactory->buildAdvancedTypeParserForFunction($reflection);
$typeResolver = new ReflectionTypeResolver($nativeParser, $advancedParser);
$returnTypeResolver = new FunctionReturnTypeResolver($typeResolver);
$parameters = array_map(
fn (ReflectionParameter $parameter) => $this->parameterBuilder->for($parameter, $typeResolver),
$reflection->getParameters(),
);
$name = $reflection->getName();
$signature = $this->signature($reflection);
$class = $reflection->getClosureScopeClass();
$returnType = $returnTypeResolver->resolveReturnTypeFor($reflection);
$nativeReturnType = $returnTypeResolver->resolveNativeReturnTypeFor($reflection);
// PHP8.2 use `ReflectionFunction::isAnonymous()`
$isClosure = $name === '{closure}' || str_ends_with($name, '\\{closure}') || str_starts_with($name, '{closure:');
if ($returnType instanceof UnresolvableType) {
$returnType = $returnType->forFunctionReturnType($signature);
} elseif (! $returnType->matches($nativeReturnType)) {
$returnType = UnresolvableType::forNonMatchingFunctionReturnTypes($name, $nativeReturnType, $returnType);
}
return new FunctionDefinition(
$name,
$signature,
new Attributes(...$this->attributesRepository->for($reflection)),
$reflection->getFileName() ?: null,
$class?->name,
$reflection->getClosureThis() === null,
$isClosure,
new Parameters(...$parameters),
$returnType,
);
}
/**
* @return non-empty-string
*/
private function signature(ReflectionFunction $reflection): string
{
// PHP8.2 use `ReflectionFunction::isAnonymous()`
if ($reflection->name === '{closure}' || str_ends_with($reflection->name, '\\{closure}') || str_starts_with($reflection->name, '{closure:')) {
$startLine = $reflection->getStartLine();
$endLine = $reflection->getEndLine();
return $startLine === $endLine
? "Closure (line $startLine of {$reflection->getFileName()})"
: "Closure (lines $startLine to $endLine of {$reflection->getFileName()})";
}
return $reflection->getClosureScopeClass()
? $reflection->getClosureScopeClass()->name . '::' . $reflection->name . '()'
: $reflection->name . '()';
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\MethodDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Parameters;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\AttributesRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\FunctionReturnTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnresolvableType;
use ReflectionMethod;
use ReflectionParameter;
use function array_map;
/** @internal */
final class ReflectionMethodDefinitionBuilder
{
private AttributesRepository $attributesRepository;
private ReflectionParameterDefinitionBuilder $parameterBuilder;
public function __construct(AttributesRepository $attributesRepository)
{
$this->attributesRepository = $attributesRepository;
$this->parameterBuilder = new ReflectionParameterDefinitionBuilder($attributesRepository);
}
public function for(ReflectionMethod $reflection, ReflectionTypeResolver $typeResolver): MethodDefinition
{
$name = $reflection->name;
$signature = $reflection->getDeclaringClass()->name . '::' . $reflection->name . '()';
$parameters = array_map(
fn (ReflectionParameter $parameter) => $this->parameterBuilder->for($parameter, $typeResolver),
$reflection->getParameters()
);
$returnTypeResolver = new FunctionReturnTypeResolver($typeResolver);
$returnType = $returnTypeResolver->resolveReturnTypeFor($reflection);
$nativeReturnType = $returnTypeResolver->resolveNativeReturnTypeFor($reflection);
if ($returnType instanceof UnresolvableType) {
$returnType = $returnType->forMethodReturnType($signature);
} elseif (! $returnType->matches($nativeReturnType)) {
$returnType = UnresolvableType::forNonMatchingMethodReturnTypes($name, $nativeReturnType, $returnType);
}
return new MethodDefinition(
$name,
$signature,
new Attributes(...$this->attributesRepository->for($reflection)),
new Parameters(...$parameters),
$reflection->isStatic(),
$reflection->isPublic(),
$returnType
);
}
}
@@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\ParameterDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\AttributesRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ParameterTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnresolvableType;
use ReflectionParameter;
/** @internal */
final class ReflectionParameterDefinitionBuilder
{
public function __construct(private AttributesRepository $attributesRepository) {}
public function for(ReflectionParameter $reflection, ReflectionTypeResolver $typeResolver): ParameterDefinition
{
$parameterTypeResolver = new ParameterTypeResolver($typeResolver);
/** @var non-empty-string $name */
$name = $reflection->name;
$signature = $this->signature($reflection);
$type = $parameterTypeResolver->resolveTypeFor($reflection);
$nativeType = $parameterTypeResolver->resolveNativeTypeFor($reflection);
$isOptional = $reflection->isOptional();
$isVariadic = $reflection->isVariadic();
if ($reflection->isDefaultValueAvailable()) {
$defaultValue = $reflection->getDefaultValue();
} elseif ($reflection->isVariadic()) {
$defaultValue = [];
} else {
$defaultValue = null;
}
if ($type instanceof UnresolvableType) {
$type = $type->forParameter($signature);
} elseif (! $type->matches($nativeType)) {
$type = UnresolvableType::forNonMatchingParameterTypes($signature, $nativeType, $type);
} elseif ($isOptional && ! $type->accepts($defaultValue)) {
$type = UnresolvableType::forInvalidParameterDefaultValue($signature, $type, $defaultValue);
}
return new ParameterDefinition(
$name,
$signature,
$type,
$nativeType,
$isOptional,
$isVariadic,
$defaultValue,
new Attributes(...$this->attributesRepository->for($reflection)),
);
}
/**
* @return non-empty-string
*/
private function signature(ReflectionParameter $reflection): string
{
$signature = $reflection->getDeclaringFunction()->name . "(\$$reflection->name)";
$class = $reflection->getDeclaringClass();
if ($class) {
$signature = $class->name . '::' . $signature;
}
return $signature;
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Attributes;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\PropertyDefinition;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\AttributesRepository;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\PropertyTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver\ReflectionTypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\NullType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnresolvableType;
use ReflectionProperty;
/** @internal */
final class ReflectionPropertyDefinitionBuilder
{
public function __construct(private AttributesRepository $attributesRepository) {}
public function for(ReflectionProperty $reflection, ReflectionTypeResolver $typeResolver): PropertyDefinition
{
$propertyTypeResolver = new PropertyTypeResolver($typeResolver);
/** @var non-empty-string $name */
$name = $reflection->name;
$signature = $reflection->getDeclaringClass()->name . '::$' . $reflection->name;
$type = $propertyTypeResolver->resolveTypeFor($reflection);
$nativeType = $propertyTypeResolver->resolveNativeTypeFor($reflection);
$hasDefaultValue = $this->hasDefaultValue($reflection, $type);
$defaultValue = $reflection->getDefaultValue();
$isPublic = $reflection->isPublic();
if ($type instanceof UnresolvableType) {
$type = $type->forProperty($signature);
} elseif (! $type->matches($nativeType)) {
$type = UnresolvableType::forNonMatchingPropertyTypes($signature, $nativeType, $type);
} elseif ($hasDefaultValue && ! $type->accepts($defaultValue)) {
$type = UnresolvableType::forInvalidPropertyDefaultValue($signature, $type, $defaultValue);
}
return new PropertyDefinition(
$name,
$signature,
$type,
$nativeType,
$hasDefaultValue,
$defaultValue,
$isPublic,
new Attributes(...$this->attributesRepository->for($reflection)),
);
}
private function hasDefaultValue(ReflectionProperty $reflection, Type $type): bool
{
if ($reflection->hasType()) {
return $reflection->hasDefaultValue();
}
return $reflection->getDeclaringClass()->getDefaultProperties()[$reflection->name] !== null
|| NullType::get()->matches($type);
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClass;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClassType;
use OCA\Talk\Vendor\CuyZ\Valinor\Definition\Exception\UnknownTypeAliasImport;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Lexer\Annotations;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use function current;
use function key;
use function next;
/** @internal */
final class ClassImportedTypeAliasResolver
{
public function __construct(private TypeParserFactory $typeParserFactory) {}
/**
* @return array<string, Type>
*/
public function resolveImportedTypeAliases(ObjectType $type): array
{
$importedTypesRaw = $this->extractImportedAliasesFromDocBlock($type->className());
if ($importedTypesRaw === []) {
return [];
}
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type);
$importedTypes = [];
foreach ($importedTypesRaw as $class => $types) {
try {
$classType = $typeParser->parse($class);
} catch (InvalidType) {
throw new InvalidTypeAliasImportClass($type, $class);
}
if (! $classType instanceof ObjectType) {
throw new InvalidTypeAliasImportClassType($type, $classType);
}
$localTypes = (new ClassLocalTypeAliasResolver($this->typeParserFactory))->resolveLocalTypeAliases($classType);
foreach ($types as $importedType) {
if (! isset($localTypes[$importedType])) {
throw new UnknownTypeAliasImport($type, $classType->className(), $importedType);
}
$importedTypes[$importedType] = $localTypes[$importedType];
}
}
return $importedTypes;
}
/**
* @param class-string $className
* @return array<non-empty-string, list<non-empty-string>>
*/
private function extractImportedAliasesFromDocBlock(string $className): array
{
$docBlock = Reflection::class($className)->getDocComment();
if ($docBlock === false) {
return [];
}
$importedAliases = [];
$annotations = (new Annotations($docBlock))->filteredByPriority(
'@phpstan-import-type',
'@psalm-import-type',
);
foreach ($annotations as $annotation) {
$tokens = $annotation->filtered();
$name = current($tokens);
$from = next($tokens);
if ($from !== 'from') {
continue;
}
next($tokens);
$key = key($tokens);
// @phpstan-ignore identical.alwaysFalse (Somehow PHPStan does not properly infer the key)
if ($key === null) {
continue;
}
$class = $annotation->allAfter($key);
$importedAliases[$class][] = $name;
}
return $importedAliases;
}
}
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace OCA\Talk\Vendor\CuyZ\Valinor\Definition\Repository\Reflection\TypeResolver;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Parser\Lexer\Annotations;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\UnresolvableType;
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection\Reflection;
use function current;
use function key;
use function next;
/** @internal */
final class ClassLocalTypeAliasResolver
{
public function __construct(private TypeParserFactory $typeParserFactory) {}
/**
* @return array<string, Type>
*/
public function resolveLocalTypeAliases(ObjectType $type): array
{
$localAliases = $this->extractLocalAliasesFromDocBlock($type->className());
if ($localAliases === []) {
return [];
}
$types = [];
foreach ($localAliases as $name => $raw) {
try {
$typeParser = $this->typeParserFactory->buildAdvancedTypeParserForClass($type, $types);
$types[$name] = $typeParser->parse($raw);
} catch (InvalidType $exception) {
$types[$name] = UnresolvableType::forLocalAlias($raw, $name, $type, $exception);
}
}
return $types;
}
/**
* @param class-string $className
* @return array<non-empty-string, non-empty-string>
*/
private function extractLocalAliasesFromDocBlock(string $className): array
{
$docBlock = Reflection::class($className)->getDocComment();
if ($docBlock === false) {
return [];
}
$aliases = [];
$annotations = (new Annotations($docBlock))->filteredInOrder(
'@phpstan-type',
'@psalm-type',
);
foreach ($annotations as $annotation) {
$tokens = $annotation->filtered();
$name = current($tokens);
$next = next($tokens);
if ($next === '=') {
next($tokens);
}
$key = key($tokens);
// @phpstan-ignore notIdentical.alwaysTrue (Somehow PHPStan does not properly infer the key)
if ($key !== null) {
$aliases[$name] = $annotation->allAfter($key);
}
}
return $aliases;
}
}

Some files were not shown because too many files have changed in this diff Show More