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
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:
+16
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
|
||||
|
||||
/** @internal */
|
||||
trait IsSingleton
|
||||
{
|
||||
private static self $instance;
|
||||
|
||||
public static function get(): static
|
||||
{
|
||||
return self::$instance ??= new static();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
|
||||
/** @internal */
|
||||
final class Package
|
||||
{
|
||||
private static string $version;
|
||||
|
||||
public static function version(): string
|
||||
{
|
||||
/** @infection-ignore-all */
|
||||
return self::$version ??= InstalledVersions::getVersion('cuyz/valinor') ?? 'unknown';
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
|
||||
|
||||
/** @internal */
|
||||
final class Polyfill
|
||||
{
|
||||
/**
|
||||
* PHP8.4 use native function `array_all` instead.
|
||||
*
|
||||
* @param array<mixed> $array
|
||||
*/
|
||||
public static function array_all(array $array, callable $callback): bool
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
if (! $callback($value, $key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Priority;
|
||||
|
||||
/**
|
||||
* This interface can be implemented by objects which may be sorted
|
||||
* using @see \OCA\Talk\Vendor\CuyZ\Valinor\Utility\Priority\PrioritizedList
|
||||
*
|
||||
* The higher the priority is for a given object, the more chance it has to
|
||||
* be used first.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
interface HasPriority
|
||||
{
|
||||
public function priority(): int;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Priority;
|
||||
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* @api
|
||||
*
|
||||
* @template T of object
|
||||
* @implements IteratorAggregate<T>
|
||||
*/
|
||||
final class PrioritizedList implements IteratorAggregate
|
||||
{
|
||||
/** @var array<int, T[]> */
|
||||
private array $objects = [];
|
||||
|
||||
/**
|
||||
* @param T ...$objects
|
||||
*/
|
||||
public function __construct(object ...$objects)
|
||||
{
|
||||
foreach ($objects as $object) {
|
||||
$this->objects[$this->priority($object)][] = $object;
|
||||
}
|
||||
|
||||
krsort($this->objects, SORT_NUMERIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Traversable<T>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
foreach ($this->objects as $priority => $objects) {
|
||||
foreach ($objects as $object) {
|
||||
yield $priority => $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function priority(object $object): int
|
||||
{
|
||||
return $object instanceof HasPriority
|
||||
? $object->priority()
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
|
||||
|
||||
use PhpToken;
|
||||
|
||||
/** @internal */
|
||||
final class NamespaceFinder
|
||||
{
|
||||
public function findNamespace(string $content): ?string
|
||||
{
|
||||
$tokens = PhpToken::tokenize($content);
|
||||
$tokensCount = count($tokens);
|
||||
/* @infection-ignore-all Unneeded because of the nature of namespace-related token */
|
||||
$pointer = $tokensCount - 1;
|
||||
|
||||
while (! $tokens[$pointer]->is(T_NAMESPACE)) {
|
||||
/* @infection-ignore-all Unneeded because of the nature of namespace-related token */
|
||||
if ($pointer === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$pointer--;
|
||||
}
|
||||
|
||||
while (! $tokens[$pointer]->is([T_NAME_QUALIFIED, T_STRING])) {
|
||||
$pointer++;
|
||||
}
|
||||
|
||||
return (string)$tokens[$pointer];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
use SplFileObject;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Imported from `doctrine/annotations`:
|
||||
* @link https://github.com/doctrine/annotations/blob/4858ab786a6cb568149209a9112dad3808c8a4de/lib/Doctrine/Common/Annotations/PhpParser.php
|
||||
*/
|
||||
final class PhpParser
|
||||
{
|
||||
/** @var array<string, array<string, string>> */
|
||||
private static array $statements = [];
|
||||
|
||||
/**
|
||||
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function parseUseStatements(ReflectionClass|ReflectionFunction|ReflectionMethod $reflection): array
|
||||
{
|
||||
$signature = "{$reflection->getFileName()}:{$reflection->getStartLine()}";
|
||||
|
||||
// @infection-ignore-all
|
||||
return self::$statements[$signature] ??= self::fetchUseStatements($reflection);
|
||||
}
|
||||
|
||||
public static function parseNamespace(ReflectionFunction $reflection): ?string
|
||||
{
|
||||
$content = self::getFileContent($reflection);
|
||||
|
||||
if ($content === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (new NamespaceFinder())->findNamespace($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private static function fetchUseStatements(ReflectionClass|ReflectionFunction|ReflectionMethod $reflection): array
|
||||
{
|
||||
if ($reflection instanceof ReflectionMethod) {
|
||||
$namespaceName = $reflection->getDeclaringClass()->getNamespaceName();
|
||||
} elseif ($reflection instanceof ReflectionFunction && $reflection->getClosureScopeClass()) {
|
||||
$namespaceName = $reflection->getClosureScopeClass()->getNamespaceName();
|
||||
} else {
|
||||
$namespaceName = $reflection->getNamespaceName();
|
||||
}
|
||||
|
||||
$content = self::getFileContent($reflection);
|
||||
|
||||
if ($content === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return (new TokenParser($content))->parseUseStatements($namespaceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionClass<object>|ReflectionFunction|ReflectionMethod $reflection
|
||||
*/ private static function getFileContent(ReflectionClass|ReflectionFunction|ReflectionMethod $reflection): ?string
|
||||
{
|
||||
$filename = $reflection->getFileName();
|
||||
$startLine = $reflection->getStartLine();
|
||||
|
||||
// @infection-ignore-all these values will never be `true`
|
||||
if ($filename === false || $startLine === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! is_file($filename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// @infection-ignore-all no need to test with `-1`
|
||||
$lineCnt = 0;
|
||||
$content = '';
|
||||
$file = new SplFileObject($filename);
|
||||
|
||||
while (! $file->eof()) {
|
||||
if ($lineCnt++ === $startLine) {
|
||||
break;
|
||||
}
|
||||
|
||||
$content .= $file->fgets();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
|
||||
|
||||
use Closure;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use UnitEnum;
|
||||
|
||||
use function class_exists;
|
||||
use function enum_exists;
|
||||
use function interface_exists;
|
||||
use function ltrim;
|
||||
use function spl_object_hash;
|
||||
|
||||
/** @internal */
|
||||
final class Reflection
|
||||
{
|
||||
/** @var array<class-string, ReflectionClass<object>> */
|
||||
private static array $classReflection = [];
|
||||
|
||||
/** @var array<string, ReflectionFunction> */
|
||||
private static array $functionReflection = [];
|
||||
|
||||
/** @var array<string, bool> */
|
||||
private static array $classOrInterfaceExists = [];
|
||||
|
||||
/** @var array<string, bool> */
|
||||
private static array $enumExists = [];
|
||||
|
||||
/**
|
||||
* Case-sensitive implementation of `class_exists` and `interface_exists`.
|
||||
*
|
||||
* @phpstan-assert-if-true class-string $name
|
||||
*/
|
||||
public static function classOrInterfaceExists(string $name): bool
|
||||
{
|
||||
// @infection-ignore-all / We don't need to test the cache
|
||||
return self::$classOrInterfaceExists[$name] ??= (class_exists($name) || interface_exists($name))
|
||||
&& self::class($name)->name === ltrim($name, '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-assert-if-true class-string<UnitEnum> $name
|
||||
*/
|
||||
public static function enumExists(string $name): bool
|
||||
{
|
||||
// @infection-ignore-all / We don't need to test the cache
|
||||
return self::$enumExists[$name] ??= enum_exists($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $className
|
||||
* @return ReflectionClass<object>
|
||||
*/
|
||||
public static function class(string $className): ReflectionClass
|
||||
{
|
||||
return self::$classReflection[$className] ??= new ReflectionClass($className);
|
||||
}
|
||||
|
||||
public static function function(callable $function): ReflectionFunction
|
||||
{
|
||||
$closure = Closure::fromCallable($function);
|
||||
|
||||
return self::$functionReflection[spl_object_hash($closure)] ??= new ReflectionFunction($closure);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\Reflection;
|
||||
|
||||
use LogicException;
|
||||
use PhpToken;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Imported from `doctrine/annotations`:
|
||||
* @link https://github.com/doctrine/annotations/blob/de990c9a69782a8b15fa8c9248de0ef4d82ed701/lib/Doctrine/Common/Annotations/TokenParser.php
|
||||
*/
|
||||
final class TokenParser
|
||||
{
|
||||
/** @var array<PhpToken> */
|
||||
private array $tokens;
|
||||
|
||||
private int $numTokens;
|
||||
|
||||
private int $pointer = 0;
|
||||
|
||||
public function __construct(string $content)
|
||||
{
|
||||
$this->tokens = PhpToken::tokenize($content);
|
||||
$this->numTokens = count($this->tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function parseUseStatements(string $namespaceName): array
|
||||
{
|
||||
$currentNamespace = '';
|
||||
$statements = [];
|
||||
|
||||
while ($token = $this->next()) {
|
||||
if ($currentNamespace === $namespaceName && $token->is(T_USE)) {
|
||||
$statements = [...$statements, ...$this->parseUseStatement()];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $token->is(T_NAMESPACE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$currentNamespace = $this->parseNamespace();
|
||||
|
||||
// Get fresh array for new namespace. This is to prevent the parser
|
||||
// to collect the use statements for a previous namespace with the
|
||||
// same name (this is the case if a namespace is defined twice).
|
||||
$statements = [];
|
||||
}
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function parseUseStatement(): array
|
||||
{
|
||||
$groupRoot = '';
|
||||
$class = '';
|
||||
$alias = '';
|
||||
$statements = [];
|
||||
$explicitAlias = false;
|
||||
|
||||
while ($token = $this->next()) {
|
||||
$name = (string)$token;
|
||||
|
||||
if (! $explicitAlias && $token->is(T_STRING)) {
|
||||
$class = $alias = $name;
|
||||
} elseif ($explicitAlias && $token->is(T_STRING)) {
|
||||
$alias = $name;
|
||||
} elseif ($token->is([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED])) {
|
||||
$class = $name;
|
||||
$classSplit = explode('\\', $name);
|
||||
$alias = $classSplit[count($classSplit) - 1];
|
||||
} elseif ($token->is(T_NS_SEPARATOR)) {
|
||||
$class .= '\\';
|
||||
$alias = '';
|
||||
} elseif ($token->is(T_AS)) {
|
||||
$explicitAlias = true;
|
||||
$alias = '';
|
||||
} elseif ($name === ',') {
|
||||
$statements[strtolower($alias)] = $groupRoot . $class;
|
||||
$class = $alias = '';
|
||||
$explicitAlias = false;
|
||||
} elseif ($name === ';') {
|
||||
if ($alias !== '') {
|
||||
$statements[strtolower($alias)] = $groupRoot . $class;
|
||||
}
|
||||
break;
|
||||
} elseif ($name === '{') {
|
||||
$groupRoot = $class;
|
||||
$class = '';
|
||||
}
|
||||
}
|
||||
|
||||
return $statements;
|
||||
}
|
||||
|
||||
private function next(): ?PhpToken
|
||||
{
|
||||
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
|
||||
$this->pointer++;
|
||||
|
||||
if (! $this->tokens[$i]->isIgnorable()) {
|
||||
return $this->tokens[$i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function parseNamespace(): string
|
||||
{
|
||||
while ($token = $this->next()) {
|
||||
if ($token->is([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED, T_STRING])) {
|
||||
return (string)$token;
|
||||
}
|
||||
}
|
||||
|
||||
/** @infection-ignore-all */
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new LogicException('Namespace not found.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\String;
|
||||
|
||||
use function substr;
|
||||
|
||||
/** @internal */
|
||||
final class StringCutter
|
||||
{
|
||||
public static function cut(string $s, int $length): string
|
||||
{
|
||||
if (function_exists('mb_strcut')) {
|
||||
return mb_strcut($s, 0, $length);
|
||||
}
|
||||
|
||||
return self::cutPolyfill($s, $length);
|
||||
}
|
||||
|
||||
public static function cutPolyfill(string $s, int $length): string
|
||||
{
|
||||
$s = substr($s, 0, $length);
|
||||
$cur = strlen($s) - 1;
|
||||
// U+0000 - U+007F
|
||||
if ((ord($s[$cur]) & 0b1000_0000) === 0) {
|
||||
return $s;
|
||||
}
|
||||
$cnt = 0;
|
||||
while ((ord($s[$cur]) & 0b1100_0000) === 0b1000_0000) {
|
||||
++$cnt;
|
||||
if ($cur === 0) {
|
||||
// @infection-ignore-all // Causes infinite loop
|
||||
break;
|
||||
}
|
||||
--$cur;
|
||||
}
|
||||
|
||||
assert($cur >= 0);
|
||||
|
||||
return match (true) {
|
||||
default => substr($s, 0, $cur),
|
||||
// U+0080 - U+07FF
|
||||
$cnt === 1 && (ord($s[$cur]) & 0b1110_0000) === 0b1100_0000,
|
||||
// U+0800 - U+FFFF
|
||||
$cnt === 2 && (ord($s[$cur]) & 0b1111_0000) === 0b1110_0000,
|
||||
// U+10000 - U+10FFFF
|
||||
$cnt === 3 && (ord($s[$cur]) & 0b1111_1000) === 0b1111_0000 => $s
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\String;
|
||||
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Tree\Message\HasParameters;
|
||||
use IntlException;
|
||||
use MessageFormatter;
|
||||
|
||||
use function class_exists;
|
||||
use function preg_match;
|
||||
use function preg_quote;
|
||||
use function preg_replace;
|
||||
|
||||
/** @internal */
|
||||
final class StringFormatter
|
||||
{
|
||||
public const DEFAULT_LOCALE = 'en';
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*/
|
||||
public static function format(string $locale, string $body, array $parameters = []): string
|
||||
{
|
||||
return class_exists(MessageFormatter::class)
|
||||
? self::formatWithIntl($locale, $body, $parameters)
|
||||
: self::formatWithRegex($body, $parameters);
|
||||
}
|
||||
|
||||
public static function for(HasParameters $message): string
|
||||
{
|
||||
return self::formatWithRegex($message->body(), $message->parameters());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*/
|
||||
private static function formatWithIntl(string $locale, string $body, array $parameters): string
|
||||
{
|
||||
try {
|
||||
$formatted = MessageFormatter::formatMessage($locale, $body, $parameters);
|
||||
|
||||
if ($formatted === false) {
|
||||
throw new StringFormatterError($body, intl_get_error_message());
|
||||
}
|
||||
|
||||
return $formatted;
|
||||
} catch (IntlException $e) {
|
||||
throw new StringFormatterError($body, $e->getMessage(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $parameters
|
||||
*/
|
||||
private static function formatWithRegex(string $body, array $parameters): string
|
||||
{
|
||||
$message = $body;
|
||||
|
||||
if (preg_match('/{\s*[^}]*[^}a-z_]+\s*}?/', $body)) {
|
||||
throw new StringFormatterError($body);
|
||||
}
|
||||
|
||||
foreach ($parameters as $name => $value) {
|
||||
$name = preg_quote($name, '/');
|
||||
|
||||
/** @var string $message */
|
||||
$message = preg_replace("/{\s*$name\s*}/", $value, $message);
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility\String;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/** @internal */
|
||||
final class StringFormatterError extends RuntimeException
|
||||
{
|
||||
public function __construct(string $body, string $message = '', ?\Throwable $previous = null)
|
||||
{
|
||||
if ($message !== '') {
|
||||
$message = ": $message";
|
||||
}
|
||||
parent::__construct("Message formatter error using `$body`$message.", 1652901203, $previous);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
|
||||
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Object\Argument;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Mapper\Object\Arguments;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\BooleanType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\CompositeType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\FixedType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\FloatType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\IntegerType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\ObjectType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\StringType;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Type;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Type\Types\EnumType;
|
||||
|
||||
/** @internal */
|
||||
final class TypeHelper
|
||||
{
|
||||
/**
|
||||
* Sorting the scalar types by priority: int, float, string, bool.
|
||||
*/
|
||||
public static function typePriority(Type $type): int
|
||||
{
|
||||
return match (true) {
|
||||
$type instanceof IntegerType => 4,
|
||||
$type instanceof FloatType => 3,
|
||||
$type instanceof StringType => 2,
|
||||
$type instanceof BooleanType => 1,
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
public static function dump(Type $type, bool $surround = true): string
|
||||
{
|
||||
if ($type instanceof EnumType) {
|
||||
$text = $type->readableSignature();
|
||||
} elseif ($type instanceof FixedType) {
|
||||
return $type->toString();
|
||||
} elseif (self::containsObject($type)) {
|
||||
$text = '?';
|
||||
} else {
|
||||
$text = $type->toString();
|
||||
}
|
||||
|
||||
return $surround ? "`$text`" : $text;
|
||||
}
|
||||
|
||||
public static function dumpArguments(Arguments $arguments): string
|
||||
{
|
||||
if (count($arguments) === 0) {
|
||||
return 'array';
|
||||
}
|
||||
|
||||
if (count($arguments) === 1) {
|
||||
return self::dump($arguments->at(0)->type());
|
||||
}
|
||||
|
||||
$parameters = array_map(
|
||||
function (Argument $argument) {
|
||||
$name = $argument->name();
|
||||
$type = $argument->type();
|
||||
|
||||
$signature = self::dump($type, false);
|
||||
|
||||
return $argument->isRequired() ? "$name: $signature" : "$name?: $signature";
|
||||
},
|
||||
[...$arguments],
|
||||
);
|
||||
|
||||
return '`array{' . implode(', ', $parameters) . '}`';
|
||||
}
|
||||
|
||||
public static function containsObject(Type $type): bool
|
||||
{
|
||||
if ($type instanceof CompositeType) {
|
||||
foreach ($type->traverse() as $subType) {
|
||||
if (self::containsObject($subType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $type instanceof ObjectType;
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OCA\Talk\Vendor\CuyZ\Valinor\Utility;
|
||||
|
||||
use BackedEnum;
|
||||
use OCA\Talk\Vendor\CuyZ\Valinor\Utility\String\StringCutter;
|
||||
use DateTimeInterface;
|
||||
use Generator;
|
||||
use UnitEnum;
|
||||
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_bool;
|
||||
use function is_float;
|
||||
use function is_int;
|
||||
use function is_iterable;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function str_contains;
|
||||
use function str_replace;
|
||||
use function strlen;
|
||||
|
||||
/** @internal */
|
||||
final class ValueDumper
|
||||
{
|
||||
private const MAX_STRING_LENGTH = 50;
|
||||
private const MAX_ARRAY_ENTRIES = 5;
|
||||
private const DATE_FORMAT = 'Y/m/d H:i:s';
|
||||
|
||||
public static function dump(mixed $value): string
|
||||
{
|
||||
return self::doDump($value);
|
||||
}
|
||||
|
||||
private static function doDump(mixed $value, bool $goDeeper = true): string
|
||||
{
|
||||
if ($value === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
return $value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
if (is_int($value) || is_float($value)) {
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = self::crop($value);
|
||||
|
||||
if (str_contains($value, "'") && str_contains($value, '"')) {
|
||||
return "'" . str_replace("'", "\'", $value) . "'";
|
||||
}
|
||||
|
||||
if (str_contains($value, "'")) {
|
||||
return '"' . $value . '"';
|
||||
}
|
||||
|
||||
return "'" . $value . "'";
|
||||
}
|
||||
|
||||
if ($value instanceof BackedEnum) {
|
||||
return is_string($value->value)
|
||||
? "'$value->value'"
|
||||
: (string)$value->value;
|
||||
}
|
||||
|
||||
if ($value instanceof UnitEnum) {
|
||||
return "'$value->name'";
|
||||
}
|
||||
|
||||
if ($value instanceof DateTimeInterface) {
|
||||
return $value->format(self::DATE_FORMAT);
|
||||
}
|
||||
|
||||
if (is_iterable($value) && ! $value instanceof Generator) {
|
||||
/** @var iterable<string|int, mixed> $value */
|
||||
if (is_array($value)) {
|
||||
$type = 'array';
|
||||
} else {
|
||||
$type = 'iterable';
|
||||
}
|
||||
|
||||
$values = self::listValues($value);
|
||||
|
||||
if (empty($values)) {
|
||||
return "$type (empty)";
|
||||
}
|
||||
|
||||
if (! $goDeeper) {
|
||||
return "$type{…}";
|
||||
}
|
||||
|
||||
return "$type{" . implode(', ', $values) . '}';
|
||||
}
|
||||
|
||||
if (is_object($value)) {
|
||||
return 'object(' . $value::class . ')';
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
return 'unknown';
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterable<string|int, mixed> $iterable
|
||||
* @return array<mixed>
|
||||
*/
|
||||
private static function listValues(iterable $iterable): array
|
||||
{
|
||||
$values = [];
|
||||
$index = 0;
|
||||
|
||||
foreach ($iterable as $key => $value) {
|
||||
$values[] = "$key: " . self::doDump($value, false);
|
||||
|
||||
if ($index++ >= self::MAX_ARRAY_ENTRIES) {
|
||||
$values[] = '…';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private static function crop(string $string): string
|
||||
{
|
||||
if (strlen($string) <= self::MAX_STRING_LENGTH) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
$string = StringCutter::cut($string, self::MAX_STRING_LENGTH + 1);
|
||||
|
||||
for ($i = strlen($string) - 1; $i > 10; $i--) {
|
||||
if ($string[$i] === ' ') {
|
||||
return StringCutter::cut($string, $i) . '…';
|
||||
}
|
||||
}
|
||||
|
||||
return $string . '…';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user