.gitattributes000064400000000054151360554240007443 0ustar00/tests export-ignore /.github export-ignore LICENSE000064400000002110151360554240005550 0ustar00The MIT License (MIT) Copyright (c) Taylor Otwell Copyright (c) Hyperf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. composer.json000064400000003112151360554240007270 0ustar00{ "name": "hyperf/utils", "description": "A tools package that could help developer solved the problem quickly.", "license": "MIT", "keywords": [ "php", "swoole", "hyperf", "utils" ], "homepage": "https://hyperf.io", "support": { "docs": "https://hyperf.wiki", "issues": "https://github.com/hyperf/hyperf/issues", "pull-request": "https://github.com/hyperf/hyperf/pulls", "source": "https://github.com/hyperf/hyperf" }, "require": { "php": ">=7.2", "doctrine/inflector": "^2.0", "hyperf/contract": "~2.2.0", "hyperf/engine": "^1.1" }, "suggest": { "ext-swoole": "Required to use methods related to swoole (>=4.5).", "symfony/var-dumper": "Required to use the dd function (^5.0).", "symfony/serializer": "Required to use SymfonyNormalizer (^5.0)", "symfony/property-access": "Required to use SymfonyNormalizer (^5.0)", "hyperf/di": "Required to use ExceptionNormalizer", "nikic/php-parser": "Required to use PhpParser. (^4.0)" }, "autoload": { "files": [ "src/Functions.php" ], "psr-4": { "Hyperf\\Utils\\": "src/" } }, "autoload-dev": { "psr-4": { "HyperfTest\\Utils\\": "tests/" } }, "config": { "sort-packages": true }, "extra": { "hyperf": { "config": "Hyperf\\Utils\\ConfigProvider" }, "branch-alias": { "dev-master": "2.1-dev" } } } src/ApplicationContext.php000064400000001503151360554240011660 0ustar00all(); } elseif (! is_array($values)) { continue; } $results[] = $values; } return array_merge([], ...$results); } /** * Cross join the given arrays, returning all possible permutations. * * @param array ...$arrays */ public static function crossJoin(...$arrays): array { $results = [[]]; foreach ($arrays as $index => $array) { $append = []; foreach ($results as $product) { foreach ($array as $item) { $product[$index] = $item; $append[] = $product; } } $results = $append; } return $results; } /** * Divide an array into two arrays. One with keys and the other with values. * * @param array $array * @return array */ public static function divide($array) { return [array_keys($array), array_values($array)]; } /** * Flatten a multi-dimensional associative array with dots. */ public static function dot(array $array, string $prepend = ''): array { $results = []; foreach ($array as $key => $value) { if (is_array($value) && ! empty($value)) { $results = array_merge($results, static::dot($value, $prepend . $key . '.')); } else { $results[$prepend . $key] = $value; } } return $results; } /** * Get all of the given array except for a specified array of keys. * * @param array|string $keys */ public static function except(array $array, $keys): array { static::forget($array, $keys); return $array; } /** * Determine if the given key exists in the provided array. * * @param array|\ArrayAccess $array * @param int|string $key */ public static function exists($array, $key): bool { if ($array instanceof ArrayAccess) { return $array->offsetExists($key); } return array_key_exists($key, $array); } /** * Return the first element in an array passing a given truth test. * * @param null|mixed $default */ public static function first(array $array, callable $callback = null, $default = null) { if (is_null($callback)) { if (empty($array)) { return value($default); } foreach ($array as $item) { return $item; } } foreach ($array as $key => $value) { if (call_user_func($callback, $value, $key)) { return $value; } } return value($default); } /** * Return the last element in an array passing a given truth test. * * @param null|mixed $default */ public static function last(array $array, callable $callback = null, $default = null) { if (is_null($callback)) { return empty($array) ? value($default) : end($array); } return static::first(array_reverse($array, true), $callback, $default); } /** * Flatten a multi-dimensional array into a single level. * @param float|int $depth */ public static function flatten(array $array, $depth = INF): array { $result = []; foreach ($array as $item) { $item = $item instanceof Collection ? $item->all() : $item; if (! is_array($item)) { $result[] = $item; } elseif ($depth === 1) { $result = array_merge($result, array_values($item)); } else { $result = array_merge($result, static::flatten($item, $depth - 1)); } } return $result; } /** * Remove one or many array items from a given array using "dot" notation. * * @param array|string $keys */ public static function forget(array &$array, $keys): void { $original = &$array; $keys = (array) $keys; if (count($keys) === 0) { return; } foreach ($keys as $key) { // if the exact key exists in the top-level, remove it if (static::exists($array, $key)) { unset($array[$key]); continue; } $parts = explode('.', (string) $key); // clean up before each pass $array = &$original; while (count($parts) > 1) { $part = array_shift($parts); if (isset($array[$part]) && is_array($array[$part])) { $array = &$array[$part]; } else { continue 2; } } unset($array[array_shift($parts)]); } } /** * Get an item from an array using "dot" notation. * * @param array|\ArrayAccess $array * @param null|int|string $key * @param mixed $default */ public static function get($array, $key = null, $default = null) { if (! static::accessible($array)) { return value($default); } if (is_null($key)) { return $array; } if (static::exists($array, $key)) { return $array[$key]; } if (! is_string($key) || strpos($key, '.') === false) { return $array[$key] ?? value($default); } foreach (explode('.', $key) as $segment) { if (static::accessible($array) && static::exists($array, $segment)) { $array = $array[$segment]; } else { return value($default); } } return $array; } /** * Check if an item or items exist in an array using "dot" notation. * * @param array|\ArrayAccess $array * @param null|array|string $keys */ public static function has($array, $keys): bool { if (is_null($keys)) { return false; } $keys = (array) $keys; if (! $array) { return false; } if ($keys === []) { return false; } foreach ($keys as $key) { $subKeyArray = $array; if (static::exists($array, $key)) { continue; } foreach (explode('.', $key) as $segment) { if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { $subKeyArray = $subKeyArray[$segment]; } else { return false; } } } return true; } /** * Determines if an array is associative. * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. */ public static function isAssoc(array $array): bool { $keys = array_keys($array); return array_keys($keys) !== $keys; } /** * Get a subset of the items from the given array. * * @param array|string $keys */ public static function only(array $array, $keys): array { return array_intersect_key($array, array_flip((array) $keys)); } /** * Pluck an array of values from an array. * * @param array|string $value * @param null|array|string $key */ public static function pluck(array $array, $value, $key = null): array { $results = []; [$value, $key] = static::explodePluckParameters($value, $key); foreach ($array as $item) { $itemValue = data_get($item, $value); // If the key is "null", we will just append the value to the array and keep // looping. Otherwise we will key the array using the value of the key we // received from the developer. Then we'll return the final array form. if (is_null($key)) { $results[] = $itemValue; } else { $itemKey = data_get($item, $key); if (is_object($itemKey) && method_exists($itemKey, '__toString')) { $itemKey = (string) $itemKey; } $results[$itemKey] = $itemValue; } } return $results; } /** * Push an item onto the beginning of an array. * @param null|mixed $key * @param mixed $value */ public static function prepend(array $array, $value, $key = null): array { if (is_null($key)) { array_unshift($array, $value); } else { $array = [$key => $value] + $array; } return $array; } /** * Get a value from the array, and remove it. * * @param null|mixed $default */ public static function pull(array &$array, string $key, $default = null) { $value = static::get($array, $key, $default); static::forget($array, $key); return $value; } /** * Get one or a specified number of random values from an array. * * @throws \InvalidArgumentException */ public static function random(array $array, int $number = null) { $requested = is_null($number) ? 1 : $number; $count = count($array); if ($requested > $count) { throw new InvalidArgumentException("You requested {$requested} items, but there are only {$count} items available."); } if (is_null($number)) { return $array[array_rand($array)]; } if ((int) $number === 0) { return []; } $keys = array_rand($array, $number); $results = []; foreach ((array) $keys as $key) { $results[] = $array[$key]; } return $results; } /** * Set an array item to a given value using "dot" notation. * If no key is given to the method, the entire array will be replaced. * * @param null|int|string $key * @param mixed $value */ public static function set(array &$array, $key, $value): array { if (is_null($key)) { return $array = $value; } if (! is_string($key)) { $array[$key] = $value; return $array; } $keys = explode('.', $key); while (count($keys) > 1) { $key = array_shift($keys); // If the key doesn't exist at this depth, we will just create an empty array // to hold the next value, allowing us to create the arrays to hold final // values at the correct depth. Then we'll keep digging into the array. if (! isset($array[$key]) || ! is_array($array[$key])) { $array[$key] = []; } $array = &$array[$key]; } $array[array_shift($keys)] = $value; return $array; } /** * Shuffle the given array and return the result. */ public static function shuffle(array $array, int $seed = null): array { if (is_null($seed)) { shuffle($array); } else { srand($seed); usort($array, function () { return rand(-1, 1); }); } return $array; } /** * Sort the array using the given callback or "dot" notation. * * @param null|callable|string $callback */ public static function sort(array $array, $callback = null): array { return Collection::make($array)->sortBy($callback)->all(); } /** * Recursively sort an array by keys and values. */ public static function sortRecursive(array $array): array { foreach ($array as &$value) { if (is_array($value)) { $value = static::sortRecursive($value); } } if (static::isAssoc($array)) { ksort($array); } else { sort($array); } return $array; } /** * Convert the array into a query string. */ public static function query(array $array): string { return http_build_query($array, '', '&', PHP_QUERY_RFC3986); } /** * Filter the array using the given callback. */ public static function where(array $array, callable $callback): array { return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); } /** * If the given value is not an array and not null, wrap it in one. * @param mixed $value */ public static function wrap($value): array { if (is_null($value)) { return []; } return ! is_array($value) ? [$value] : $value; } /** * Make array elements unique. */ public static function unique(array $array): array { $result = []; foreach ($array ?? [] as $key => $item) { if (is_array($item)) { $result[$key] = self::unique($item); } else { $result[$key] = $item; } } if (! self::isAssoc($result)) { return array_unique($result); } return $result; } public static function merge(array $array1, array $array2, bool $unique = true): array { $isAssoc = static::isAssoc($array1 ?: $array2); if ($isAssoc) { foreach ($array2 as $key => $value) { if (is_array($value)) { $array1[$key] = static::merge($array1[$key] ?? [], $value, $unique); } else { $array1[$key] = $value; } } } else { foreach ($array2 as $key => $value) { if ($unique && in_array($value, $array1, true)) { continue; } $array1[] = $value; } $array1 = array_values($array1); } return $array1; } /** * Explode the "value" and "key" arguments passed to "pluck". * * @param array|string $value * @param null|array|string $key */ protected static function explodePluckParameters($value, $key): array { $value = is_string($value) ? explode('.', $value) : $value; $key = is_null($key) || is_array($key) ? $key : explode('.', $key); return [$value, $key]; } } src/Backoff.php000064400000003502151360554240007404 0ustar00 Backoff::CAP) { throw new \InvalidArgumentException( sprintf( 'first backoff interval must be less or equal than %d milliseconds', self::CAP ) ); } $this->firstMs = $firstMs; $this->currentMs = $firstMs; } /** * Sleep until the next execution. */ public function sleep(): void { if ($this->currentMs === 0) { return; } usleep($this->currentMs * 1000); // update backoff using Decorrelated Jitter // see: https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ $this->currentMs = rand($this->firstMs, $this->currentMs * 3); if ($this->currentMs > self::CAP) { $this->currentMs = self::CAP; } } /** * Get the next backoff for logging, etc. * @return int next backoff */ public function nextBackoff(): int { return $this->currentMs; } } src/Channel/Caller.php000064400000003557151360554240010635 0ustar00closure = $closure; $this->waitTimeout = $waitTimeout; $this->initInstance(); } public function call(Closure $closure) { $release = true; $channel = $this->channel; try { $instance = $channel->pop($this->waitTimeout); if ($instance === false) { if ($channel->isClosing()) { throw new ChannelClosedException('The channel was closed.'); } if ($channel->isTimeout()) { throw new WaitTimeoutException('The instance pop from channel timeout.'); } } $result = $closure($instance); } catch (ChannelClosedException | WaitTimeoutException $exception) { $release = false; throw $exception; } finally { $release && $channel->push($instance ?? null); } return $result; } public function initInstance(): void { if ($this->channel) { $this->channel->close(); } $this->channel = new Channel(1); $this->channel->push($this->closure->__invoke()); } } src/Channel/ChannelManager.php000064400000002557151360554240012275 0ustar00size = $size; } public function get(int $id, bool $initialize = false): ?Channel { if (isset($this->channels[$id])) { return $this->channels[$id]; } if ($initialize) { return $this->channels[$id] = $this->make($this->size); } return null; } public function make(int $limit): Channel { return new Channel($limit); } public function close(int $id): void { if ($channel = $this->channels[$id] ?? null) { $channel->close(); } unset($this->channels[$id]); } public function getChannels(): array { return $this->channels; } public function flush(): void { $channels = $this->getChannels(); foreach ($channels as $id => $channel) { $this->close($id); } } } src/ChannelPool.php000064400000001400151360554240010246 0ustar00isEmpty() ? new Channel(1) : $this->pop(); } public function release(Channel $channel) { $channel->errCode = 0; $this->push($channel); } } src/ClearStatCache.php000064400000002641151360554240010662 0ustar00 self::$interval || self::$lastCleared || (self::$lastCleared + self::$interval < $now) ) { self::forceClear($filename); self::$lastCleared = $now; } } public static function forceClear(?string $filename = null): void { if ($filename !== null) { clearstatcache(true, $filename); } else { clearstatcache(); } } public static function getInterval(): int { return self::$interval; } public static function setInterval(int $interval) { self::$interval = $interval; } } src/CodeGen/Package.php000064400000001060151360554240010705 0ustar00parser = $parserFactory->create(ParserFactory::ONLY_PHP7); } public static function getInstance(): PhpParser { if (static::$instance) { return static::$instance; } return static::$instance = new static(); } /** * @return null|Node\Stmt[] */ public function getNodesFromReflectionClass(ReflectionClass $reflectionClass): ?array { $code = file_get_contents($reflectionClass->getFileName()); return $this->parser->parse($code); } public function getNodeFromReflectionParameter(ReflectionParameter $parameter): Node\Param { $result = new Node\Param( new Node\Expr\Variable($parameter->getName()) ); if ($parameter->isDefaultValueAvailable()) { $result->default = $this->getExprFromValue($parameter->getDefaultValue()); } if ($parameter->hasType()) { $type = $parameter->getType()->getName(); if (! in_array($type, static::TYPES)) { $result->type = new Node\Name('\\' . $type); } else { $result->type = new Node\Identifier($parameter->getType()->getName()); } } if ($parameter->isPassedByReference()) { $result->byRef = true; } if ($parameter->isVariadic()) { $result->variadic = true; } return $result; } public function getExprFromValue($value): Node\Expr { switch (gettype($value)) { case 'array': return new Node\Expr\Array_($value); case 'string': return new Node\Scalar\String_($value); case 'integer': return new Node\Scalar\LNumber($value); case 'double': return new Node\Scalar\DNumber($value); case 'NULL': return new Node\Expr\ConstFetch(new Node\Name('null')); case 'boolean': return new Node\Expr\ConstFetch(new Node\Name($value ? 'true' : 'false')); default: throw new InvalidArgumentException($value . ' is invalid'); } } /** * @return Node\Stmt\ClassMethod[] */ public function getAllMethodsFromStmts(array $stmts): array { $methods = []; foreach ($stmts as $namespace) { if (! $namespace instanceof Node\Stmt\Namespace_) { continue; } foreach ($namespace->stmts as $class) { if (! $class instanceof Node\Stmt\Class_ && ! $class instanceof Node\Stmt\Interface_) { continue; } foreach ($class->getMethods() as $method) { $methods[] = $method; } } } return $methods; } } src/CodeGen/Project.php000064400000003576151360554240010776 0ustar00getAutoloadRules() as $prefix => $prefixPath) { if ($this->isRootNamespace($prefix) || strpos($path, $prefixPath) === 0) { return $prefix . str_replace('/', '\\', substr($path, strlen($prefixPath))); } } throw new \RuntimeException("Invalid project path: {$path}"); } public function className(string $path): string { return $this->namespace($path); } public function path(string $name, $extension = '.php'): string { if (Str::endsWith($name, '\\')) { $extension = ''; } foreach ($this->getAutoloadRules() as $prefix => $prefixPath) { if ($this->isRootNamespace($prefix) || strpos($name, $prefix) === 0) { return $prefixPath . str_replace('\\', '/', substr($name, strlen($prefix))) . $extension; } } throw new \RuntimeException("Invalid class name: {$name}"); } protected function isRootNamespace(string $namespace): bool { return $namespace === ''; } protected function getAutoloadRules(): array { return data_get(Composer::getJsonContent(), 'autoload.psr-4', []); } } src/Codec/Base62.php000064400000002012151360554240010103 0ustar00 0) { $remain = $number % self::BASE; $chars[] = self::CHARS[$remain]; $number = ($number - $remain) / self::BASE; } return implode('', array_reverse($chars)); } public static function decode(string $data): int { return array_reduce(array_map(function ($character) { return strpos(self::CHARS, $character); }, str_split($data)), function ($result, $remain) { return $result * self::BASE + $remain; }); } } src/Codec/Json.php000064400000002734151360554240010005 0ustar00toArray(); } try { $json = json_encode($data, $flags | JSON_THROW_ON_ERROR, $depth); } catch (\Throwable $exception) { throw new InvalidArgumentException($exception->getMessage(), $exception->getCode()); } return $json; } /** * @throws InvalidArgumentException */ public static function decode(string $json, bool $assoc = true, int $depth = 512, int $flags = 0) { try { $decode = json_decode($json, $assoc, $depth, $flags | JSON_THROW_ON_ERROR); } catch (\Throwable $exception) { throw new InvalidArgumentException($exception->getMessage(), $exception->getCode()); } return $decode; } } src/Codec/Xml.php000064400000003472151360554240007634 0ustar00toArray(); } else { $data = (array) $data; } if ($parentNode === null) { $xml = new SimpleXMLElement('' . "<{$root}>"); } else { $xml = $parentNode; } foreach ($data as $key => $value) { if (is_array($value)) { self::toXml($value, $xml->addChild($key)); } else { if (is_numeric($key)) { $xml->addChild('item' . $key, (string) $value); } else { $xml->addChild($key, (string) $value); } } } return trim($xml->asXML()); } public static function toArray($xml) { $disableLibxmlEntityLoader = libxml_disable_entity_loader(true); $respObject = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NOERROR); libxml_disable_entity_loader($disableLibxmlEntityLoader); if ($respObject === false) { throw new InvalidArgumentException('Syntax error.'); } return json_decode(json_encode($respObject), true); } } src/Collection.php000064400000123616151360554240010155 0ustar00items = $this->getArrayableItems($items); } /** * Convert the collection to its string representation. */ public function __toString(): string { return $this->toJson(); } /** * Dynamically access collection proxies. * * @throws \Exception */ public function __get(string $key) { if (! in_array($key, static::$proxies)) { throw new Exception("Property [{$key}] does not exist on this collection instance."); } return new HigherOrderCollectionProxy($this, $key); } /** * @param mixed $items */ public function fill($items = []) { $this->items = $this->getArrayableItems($items); return $this; } /** * Create a new collection instance if the value isn't one already. * @param mixed $items */ public static function make($items = []): self { return new static($items); } /** * Wrap the given value in a collection if applicable. * @param mixed $value */ public static function wrap($value): self { return $value instanceof self ? new static($value) : new static(Arr::wrap($value)); } /** * Get the underlying items from the given collection if applicable. * * @param array|static $value */ public static function unwrap($value): array { return $value instanceof self ? $value->all() : $value; } /** * Create a new collection by invoking the callback a given amount of times. */ public static function times(int $number, callable $callback = null): self { if ($number < 1) { return new static(); } if (is_null($callback)) { return new static(range(1, $number)); } return (new static(range(1, $number)))->map($callback); } /** * Get all of the items in the collection. */ public function all(): array { return $this->items; } /** * Get the average value of a given key. * * @param null|callable|string $callback */ public function avg($callback = null) { $callback = $this->valueRetriever($callback); $items = $this->map(function ($value) use ($callback) { return $callback($value); })->filter(function ($value) { return ! is_null($value); }); if ($count = $items->count()) { return $items->sum() / $count; } } /** * Alias for the "avg" method. * * @param null|callable|string $callback */ public function average($callback = null) { return $this->avg($callback); } /** * Get the median of a given key. * * @param null|mixed $key */ public function median($key = null) { $values = (isset($key) ? $this->pluck($key) : $this)->filter(function ($item) { return ! is_null($item); })->sort()->values(); $count = $values->count(); if ($count == 0) { return; } $middle = (int) ($count / 2); if ($count % 2) { return $values->get($middle); } return (new static([ $values->get($middle - 1), $values->get($middle), ]))->average(); } /** * Get the mode of a given key. * * @param null|mixed $key * @return null|array */ public function mode($key = null) { if ($this->count() == 0) { return null; } $collection = isset($key) ? $this->pluck($key) : $this; $counts = new self(); $collection->each(function ($value) use ($counts) { $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1; }); $sorted = $counts->sort(); $highestValue = $sorted->last(); return $sorted->filter(function ($value) use ($highestValue) { return $value == $highestValue; })->sort()->keys()->all(); } /** * Collapse the collection of items into a single array. */ public function collapse(): self { return new static(Arr::collapse($this->items)); } /** * Determine if an item exists in the collection. * @param null|mixed $operator * @param null|mixed $value * @param mixed $key */ public function contains($key, $operator = null, $value = null): bool { if (func_num_args() === 1) { if ($this->useAsCallable($key)) { $placeholder = new stdClass(); return $this->first($key, $placeholder) !== $placeholder; } return in_array($key, $this->items); } return $this->contains($this->operatorForWhere(...func_get_args())); } /** * Determine if an item exists in the collection using strict comparison. * @param null|mixed $value * @param mixed $key */ public function containsStrict($key, $value = null): bool { if (func_num_args() === 2) { return $this->contains(function ($item) use ($key, $value) { return data_get($item, $key) === $value; }); } if ($this->useAsCallable($key)) { return ! is_null($this->first($key)); } return in_array($key, $this->items, true); } /** * Cross join with the given lists, returning all possible permutations. */ public function crossJoin(...$lists): self { return new static(Arr::crossJoin($this->items, ...array_map([$this, 'getArrayableItems'], $lists))); } /** * Dump the collection and end the script. */ public function dd(...$args): void { call_user_func_array([$this, 'dump'], $args); exit(1); } /** * Dump the collection. */ public function dump(): self { (new static(func_get_args()))->push($this)->each(function ($item) { if (! class_exists(VarDumper::class)) { throw new \RuntimeException('symfony/var-dumper package required, please require the package via "composer require symfony/var-dumper"'); } VarDumper::dump($item); }); return $this; } /** * Get the items in the collection that are not present in the given items. * @param mixed $items */ public function diff($items): self { return new static(array_diff($this->items, $this->getArrayableItems($items))); } /** * Get the items in the collection that are not present in the given items. * @param mixed $items */ public function diffUsing($items, callable $callback): self { return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback)); } /** * Get the items in the collection whose keys and values are not present in the given items. * @param mixed $items */ public function diffAssoc($items): self { return new static(array_diff_assoc($this->items, $this->getArrayableItems($items))); } /** * Get the items in the collection whose keys and values are not present in the given items. * @param mixed $items */ public function diffAssocUsing($items, callable $callback): self { return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback)); } /** * Get the items in the collection whose keys are not present in the given items. * @param mixed $items */ public function diffKeys($items): self { return new static(array_diff_key($this->items, $this->getArrayableItems($items))); } /** * Get the items in the collection whose keys are not present in the given items. * @param mixed $items */ public function diffKeysUsing($items, callable $callback): self { return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); } /** * Execute a callback over each item. */ public function each(callable $callback): self { foreach ($this->items as $key => $item) { if ($callback($item, $key) === false) { break; } } return $this; } /** * Execute a callback over each nested chunk of items. */ public function eachSpread(callable $callback): self { return $this->each(function ($chunk, $key) use ($callback) { $chunk[] = $key; return $callback(...$chunk); }); } /** * Determine if all items in the collection pass the given test. * * @param callable|string $key * @param null|mixed $operator * @param null|mixed $value */ public function every($key, $operator = null, $value = null): bool { if (func_num_args() === 1) { $callback = $this->valueRetriever($key); foreach ($this->items as $k => $v) { if (! $callback($v, $k)) { return false; } } return true; } return $this->every($this->operatorForWhere(...func_get_args())); } /** * Get all items except for those with the specified keys. * * @param Collection|mixed $keys */ public function except($keys): self { if ($keys instanceof self) { $keys = $keys->all(); } elseif (! is_array($keys)) { $keys = func_get_args(); } return new static(Arr::except($this->items, $keys)); } /** * Run a filter over each of the items. */ public function filter(callable $callback = null): self { if ($callback) { return new static(Arr::where($this->items, $callback)); } return new static(array_filter($this->items)); } /** * Apply the callback if the value is truthy. */ public function when(bool $value, callable $callback, callable $default = null): self { if ($value) { return $callback($this, $value); } if ($default) { return $default($this, $value); } return $this; } /** * Apply the callback if the value is falsy. */ public function unless(bool $value, callable $callback, callable $default = null): self { return $this->when(! $value, $callback, $default); } /** * Filter items by the given key value pair. * @param null|mixed $operator * @param null|mixed $value */ public function where(string $key, $operator = null, $value = null): self { return $this->filter($this->operatorForWhere(...func_get_args())); } /** * Filter items by the given key value pair using strict comparison. * @param mixed $value */ public function whereStrict(string $key, $value): self { return $this->where($key, '===', $value); } /** * Filter items by the given key value pair. * @param mixed $values */ public function whereIn(string $key, $values, bool $strict = false): self { $values = $this->getArrayableItems($values); return $this->filter(function ($item) use ($key, $values, $strict) { return in_array(data_get($item, $key), $values, $strict); }); } /** * Filter items by the given key value pair using strict comparison. * @param mixed $values */ public function whereInStrict(string $key, $values): self { return $this->whereIn($key, $values, true); } /** * Filter items by the given key value pair. * @param mixed $values */ public function whereNotIn(string $key, $values, bool $strict = false): self { $values = $this->getArrayableItems($values); return $this->reject(function ($item) use ($key, $values, $strict) { return in_array(data_get($item, $key), $values, $strict); }); } /** * Filter items by the given key value pair using strict comparison. * @param mixed $values */ public function whereNotInStrict(string $key, $values): self { return $this->whereNotIn($key, $values, true); } /** * Filter the items, removing any items that don't match the given type. */ public function whereInstanceOf(string $type): self { return $this->filter(function ($value) use ($type) { return $value instanceof $type; }); } /** * Get the first item from the collection. * * @param null|mixed $default */ public function first(callable $callback = null, $default = null) { return Arr::first($this->items, $callback, $default); } /** * Get the first item by the given key value pair. * * @param null|mixed $value * @param mixed $operator */ public function firstWhere(string $key, $operator, $value = null) { return $this->first($this->operatorForWhere(...func_get_args())); } /** * Get a flattened array of the items in the collection. * @param float|int $depth */ public function flatten($depth = INF): self { return new static(Arr::flatten($this->items, $depth)); } /** * Flip the items in the collection. */ public function flip(): self { return new static(array_flip($this->items)); } /** * Remove an item from the collection by key. * * @param array|string $keys */ public function forget($keys): self { foreach ((array) $keys as $key) { $this->offsetUnset($key); } return $this; } /** * Get an item from the collection by key. * @param null|mixed $default * @param mixed $key */ public function get($key, $default = null) { if ($this->offsetExists($key)) { return $this->items[$key]; } return value($default); } /** * Group an associative array by a field or using a callback. * * @param callable|string $groupBy */ public function groupBy($groupBy, bool $preserveKeys = false): self { if (is_array($groupBy)) { $nextGroups = $groupBy; $groupBy = array_shift($nextGroups); } $groupBy = $this->valueRetriever($groupBy); $results = []; foreach ($this->items as $key => $value) { $groupKeys = $groupBy($value, $key); if (! is_array($groupKeys)) { $groupKeys = [$groupKeys]; } foreach ($groupKeys as $groupKey) { $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey; if (! array_key_exists($groupKey, $results)) { $results[$groupKey] = new static(); } $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); } } $result = new static($results); if (! empty($nextGroups)) { return $result->map->groupBy($nextGroups, $preserveKeys); } return $result; } /** * Key an associative array by a field or using a callback. * * @param callable|string $keyBy */ public function keyBy($keyBy): self { $keyBy = $this->valueRetriever($keyBy); $results = []; foreach ($this->items as $key => $item) { $resolvedKey = $keyBy($item, $key); if (is_object($resolvedKey)) { $resolvedKey = (string) $resolvedKey; } $results[$resolvedKey] = $item; } return new static($results); } /** * Determine if an item exists in the collection by key. * @param mixed $key */ public function has($key): bool { $keys = is_array($key) ? $key : func_get_args(); foreach ($keys as $value) { if (! $this->offsetExists($value)) { return false; } } return true; } /** * Concatenate values of a given key as a string. */ public function implode(string $value, string $glue = null): string { $first = $this->first(); if (is_array($first) || is_object($first)) { return implode($glue, $this->pluck($value)->all()); } return implode($value, $this->items); } /** * Intersect the collection with the given items. * @param mixed $items */ public function intersect($items): self { return new static(array_intersect($this->items, $this->getArrayableItems($items))); } /** * Intersect the collection with the given items by key. * @param mixed $items */ public function intersectByKeys($items): self { return new static(array_intersect_key($this->items, $this->getArrayableItems($items))); } /** * Determine if the collection is empty or not. */ public function isEmpty(): bool { return empty($this->items); } /** * Determine if the collection is not empty. */ public function isNotEmpty(): bool { return ! $this->isEmpty(); } /** * Get the keys of the collection items. */ public function keys(): self { return new static(array_keys($this->items)); } /** * Get the last item from the collection. * * @param null|mixed $default */ public function last(callable $callback = null, $default = null) { return Arr::last($this->items, $callback, $default); } /** * Get the values of a given key. * * @param array|string $value */ public function pluck($value, ?string $key = null): self { return new static(Arr::pluck($this->items, $value, $key)); } /** * Run a map over each of the items. */ public function map(callable $callback): self { $keys = array_keys($this->items); $items = array_map($callback, $this->items, $keys); return new static(array_combine($keys, $items)); } /** * Run a map over each nested chunk of items. */ public function mapSpread(callable $callback): self { return $this->map(function ($chunk, $key) use ($callback) { $chunk[] = $key; return $callback(...$chunk); }); } /** * Run a dictionary map over the items. * The callback should return an associative array with a single key/value pair. */ public function mapToDictionary(callable $callback): self { $dictionary = []; foreach ($this->items as $key => $item) { $pair = $callback($item, $key); $key = key($pair); $value = reset($pair); if (! isset($dictionary[$key])) { $dictionary[$key] = []; } $dictionary[$key][] = $value; } return new static($dictionary); } /** * Run a grouping map over the items. * The callback should return an associative array with a single key/value pair. */ public function mapToGroups(callable $callback): self { $groups = $this->mapToDictionary($callback); return $groups->map([$this, 'make']); } /** * Run an associative map over each of the items. * The callback should return an associative array with a single key/value pair. */ public function mapWithKeys(callable $callback): self { $result = []; foreach ($this->items as $key => $value) { $assoc = $callback($value, $key); foreach ($assoc as $mapKey => $mapValue) { $result[$mapKey] = $mapValue; } } return new static($result); } /** * Map a collection and flatten the result by a single level. */ public function flatMap(callable $callback): self { return $this->map($callback)->collapse(); } /** * Map the values into a new class. */ public function mapInto(string $class): self { return $this->map(function ($value, $key) use ($class) { return new $class($value, $key); }); } /** * Get the max value of a given key. * * @param null|callable|string $callback */ public function max($callback = null) { $callback = $this->valueRetriever($callback); return $this->filter(function ($value) { return ! is_null($value); })->reduce(function ($result, $item) use ($callback) { $value = $callback($item); return is_null($result) || $value > $result ? $value : $result; }); } /** * Merge the collection with the given items. * @param mixed $items */ public function merge($items): self { return new static(array_merge($this->items, $this->getArrayableItems($items))); } /** * Create a collection by using this collection for keys and another for its values. * @param mixed $values */ public function combine($values): self { return new static(array_combine($this->all(), $this->getArrayableItems($values))); } /** * Union the collection with the given items. * @param mixed $items */ public function union($items): self { return new static($this->items + $this->getArrayableItems($items)); } /** * Get the min value of a given key. * * @param null|callable|string $callback */ public function min($callback = null) { $callback = $this->valueRetriever($callback); return $this->map(function ($value) use ($callback) { return $callback($value); })->filter(function ($value) { return ! is_null($value); })->reduce(function ($result, $value) { return is_null($result) || $value < $result ? $value : $result; }); } /** * Create a new collection consisting of every n-th element. */ public function nth(int $step, int $offset = 0): self { $new = []; $position = 0; foreach ($this->items as $item) { if ($position % $step === $offset) { $new[] = $item; } ++$position; } return new static($new); } /** * Get the items with the specified keys. * @param mixed $keys */ public function only($keys): self { if (is_null($keys)) { return new static($this->items); } if ($keys instanceof self) { $keys = $keys->all(); } $keys = is_array($keys) ? $keys : func_get_args(); return new static(Arr::only($this->items, $keys)); } /** * "Paginate" the collection by slicing it into a smaller collection. */ public function forPage(int $page, int $perPage): self { $offset = max(0, ($page - 1) * $perPage); return $this->slice($offset, $perPage); } /** * Partition the collection into two arrays using the given callback or key. * * @param callable|string $key * @param null|mixed $operator * @param null|mixed $value */ public function partition($key, $operator = null, $value = null): self { $partitions = [new static(), new static()]; $callback = func_num_args() === 1 ? $this->valueRetriever($key) : $this->operatorForWhere(...func_get_args()); foreach ($this->items as $key => $item) { $partitions[(int) ! $callback($item, $key)][$key] = $item; } return new static($partitions); } /** * Pass the collection to the given callback and return the result. */ public function pipe(callable $callback) { return $callback($this); } /** * Get and remove the last item from the collection. */ public function pop() { return array_pop($this->items); } /** * Push an item onto the beginning of the collection. * @param null|mixed $key * @param mixed $value */ public function prepend($value, $key = null): self { $this->items = Arr::prepend($this->items, $value, $key); return $this; } /** * Push an item onto the end of the collection. * @param mixed $value */ public function push($value): self { $this->offsetSet(null, $value); return $this; } /** * Push all of the given items onto the collection. * * @param array|\Traversable $source */ public function concat($source): self { $result = new static($this); foreach ($source as $item) { $result->push($item); } return $result; } /** * Get and remove an item from the collection. * * @param null|mixed $default * @param mixed $key */ public function pull($key, $default = null) { return Arr::pull($this->items, $key, $default); } /** * Put an item in the collection by key. * @param mixed $key * @param mixed $value */ public function put($key, $value): self { $this->offsetSet($key, $value); return $this; } /** * Get one or a specified number of items randomly from the collection. * * @throws \InvalidArgumentException * @return mixed|self */ public function random(int $number = null) { if (is_null($number)) { return Arr::random($this->items); } return new static(Arr::random($this->items, $number)); } /** * Reduce the collection to a single value. * @param null|mixed $initial */ public function reduce(callable $callback, $initial = null) { return array_reduce($this->items, $callback, $initial); } /** * Create a collection of all elements that do not pass a given truth test. * * @param callable|mixed $callback */ public function reject($callback): self { if ($this->useAsCallable($callback)) { return $this->filter(function ($value, $key) use ($callback) { return ! $callback($value, $key); }); } return $this->filter(function ($item) use ($callback) { return $item != $callback; }); } /** * Reverse items order. */ public function reverse(): self { return new static(array_reverse($this->items, true)); } /** * Search the collection for a given value and return the corresponding key if successful. * @param mixed $value */ public function search($value, bool $strict = false) { if (! $this->useAsCallable($value)) { return array_search($value, $this->items, $strict); } foreach ($this->items as $key => $item) { if (call_user_func($value, $item, $key)) { return $key; } } return false; } /** * Get and remove the first item from the collection. */ public function shift() { return array_shift($this->items); } /** * Shuffle the items in the collection. */ public function shuffle(int $seed = null): self { return new static(Arr::shuffle($this->items, $seed)); } /** * Slice the underlying collection array. */ public function slice(int $offset, int $length = null): self { return new static(array_slice($this->items, $offset, $length, true)); } /** * Split a collection into a certain number of groups. */ public function split(int $numberOfGroups): self { if ($this->isEmpty()) { return new static(); } $groups = new static(); $groupSize = (int) floor($this->count() / $numberOfGroups); $remain = $this->count() % $numberOfGroups; $start = 0; for ($i = 0; $i < $numberOfGroups; ++$i) { $size = $groupSize; if ($i < $remain) { ++$size; } if ($size) { $groups->push(new static(array_slice($this->items, $start, $size))); $start += $size; } } return $groups; } /** * Chunk the underlying collection array. */ public function chunk(int $size): self { if ($size <= 0) { return new static(); } $chunks = []; foreach (array_chunk($this->items, $size, true) as $chunk) { $chunks[] = new static($chunk); } return new static($chunks); } /** * Sort through each item with a callback. */ public function sort(callable $callback = null): self { $items = $this->items; $callback ? uasort($items, $callback) : asort($items); return new static($items); } /** * Sort the collection using the given callback. * * @param callable|string $callback */ public function sortBy($callback, int $options = SORT_REGULAR, bool $descending = false): self { $results = []; $callback = $this->valueRetriever($callback); // First we will loop through the items and get the comparator from a callback // function which we were given. Then, we will sort the returned values and // and grab the corresponding values for the sorted keys from this array. foreach ($this->items as $key => $value) { $results[$key] = $callback($value, $key); } $descending ? arsort($results, $options) : asort($results, $options); // Once we have sorted all of the keys in the array, we will loop through them // and grab the corresponding model so we can set the underlying items list // to the sorted version. Then we'll just return the collection instance. foreach (array_keys($results) as $key) { $results[$key] = $this->items[$key]; } return new static($results); } /** * Sort the collection in descending order using the given callback. * * @param callable|string $callback */ public function sortByDesc($callback, int $options = SORT_REGULAR): self { return $this->sortBy($callback, $options, true); } /** * Sort the collection keys. */ public function sortKeys(int $options = SORT_REGULAR, bool $descending = false): self { $items = $this->items; $descending ? krsort($items, $options) : ksort($items, $options); return new static($items); } /** * Sort the collection keys in descending order. */ public function sortKeysDesc(int $options = SORT_REGULAR): self { return $this->sortKeys($options, true); } /** * Splice a portion of the underlying collection array. * @param mixed $replacement */ public function splice(int $offset, int $length = null, $replacement = []): self { if (func_num_args() === 1) { return new static(array_splice($this->items, $offset)); } return new static(array_splice($this->items, $offset, $length, $replacement)); } /** * Get the sum of the given values. * * @param null|callable|string $callback */ public function sum($callback = null) { if (is_null($callback)) { return array_sum($this->items); } $callback = $this->valueRetriever($callback); return $this->reduce(function ($result, $item) use ($callback) { return $result + $callback($item); }, 0); } /** * Take the first or last {$limit} items. */ public function take(int $limit): self { if ($limit < 0) { return $this->slice($limit, abs($limit)); } return $this->slice(0, $limit); } /** * Pass the collection to the given callback and then return it. */ public function tap(callable $callback): self { $callback(new static($this->items)); return $this; } /** * Transform each item in the collection using a callback. */ public function transform(callable $callback): self { $this->items = $this->map($callback)->all(); return $this; } /** * Return only unique items from the collection array. * * @param null|callable|string $key */ public function unique($key = null, bool $strict = false): self { $callback = $this->valueRetriever($key); $exists = []; return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { if (in_array($id = $callback($item, $key), $exists, $strict)) { return true; } $exists[] = $id; }); } /** * Return only unique items from the collection array using strict comparison. * * @param null|callable|string $key */ public function uniqueStrict($key = null): self { return $this->unique($key, true); } /** * Reset the keys on the underlying array. */ public function values(): self { return new static(array_values($this->items)); } /** * Zip the collection together with one or more arrays. * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); * => [[1, 4], [2, 5], [3, 6]]. * * @param mixed ...$items */ public function zip($items): self { $arrayableItems = array_map(function ($items) { return $this->getArrayableItems($items); }, func_get_args()); $params = array_merge([ function () { return new static(func_get_args()); }, $this->items, ], $arrayableItems); return new static(call_user_func_array('array_map', $params)); } /** * Pad collection to the specified length with a value. * @param mixed $value */ public function pad(int $size, $value): self { return new static(array_pad($this->items, $size, $value)); } /** * Get the collection of items as a plain array. */ public function toArray(): array { return array_map(function ($value) { return $value instanceof Arrayable ? $value->toArray() : $value; }, $this->items); } /** * Convert the object into something JSON serializable. */ public function jsonSerialize(): array { return array_map(function ($value) { if ($value instanceof JsonSerializable) { return $value->jsonSerialize(); } if ($value instanceof Jsonable) { return json_decode($value->__toString(), true); } if ($value instanceof Arrayable) { return $value->toArray(); } return $value; }, $this->items); } /** * Get the collection of items as JSON. */ public function toJson(int $options = 0): string { return json_encode($this->jsonSerialize(), $options); } /** * Get an iterator for the items. */ public function getIterator(): ArrayIterator { return new ArrayIterator($this->items); } /** * Get a CachingIterator instance. */ public function getCachingIterator(int $flags = CachingIterator::CALL_TOSTRING): CachingIterator { return new CachingIterator($this->getIterator(), $flags); } /** * Count the number of items in the collection. */ public function count(): int { return count($this->items); } /** * Get a base Support collection instance from this collection. */ public function toBase() { return new self($this); } /** * Determine if an item exists at an offset. * * @param mixed $key * @return bool */ public function offsetExists($key) { return array_key_exists($key, $this->items); } /** * Get an item at a given offset. * @param mixed $key */ public function offsetGet($key) { return $this->items[$key]; } /** * Set the item at a given offset. * @param mixed $key * @param mixed $value */ public function offsetSet($key, $value) { if (is_null($key)) { $this->items[] = $value; } else { $this->items[$key] = $value; } } /** * Unset the item at a given offset. * * @param string $key */ public function offsetUnset($key) { unset($this->items[$key]); } /** * Add a method to the list of proxied methods. */ public static function proxy(string $method): void { static::$proxies[] = $method; } /** * Get an operator checker callback. * @param mixed|string $operator * @param null|mixed $value */ protected function operatorForWhere(string $key, $operator = null, $value = null): \Closure { if (func_num_args() === 1) { $value = true; $operator = '='; } if (func_num_args() === 2) { $value = $operator; $operator = '='; } return function ($item) use ($key, $operator, $value) { $retrieved = data_get($item, $key); $strings = array_filter([$retrieved, $value], function ($value) { return is_string($value) || (is_object($value) && method_exists($value, '__toString')); }); if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) { return in_array($operator, ['!=', '<>', '!==']); } switch ($operator) { default: case '=': case '==': return $retrieved == $value; case '!=': case '<>': return $retrieved != $value; case '<': return $retrieved < $value; case '>': return $retrieved > $value; case '<=': return $retrieved <= $value; case '>=': return $retrieved >= $value; case '===': return $retrieved === $value; case '!==': return $retrieved !== $value; } }; } /** * Determine if the given value is callable, but not a string. * @param mixed $value */ protected function useAsCallable($value): bool { return ! is_string($value) && is_callable($value); } /** * Get a value retrieving callback. * @param mixed $value */ protected function valueRetriever($value): callable { if ($this->useAsCallable($value)) { return $value; } return function ($item) use ($value) { return data_get($item, $value); }; } /** * Results array of items from Collection or Arrayable. * @param mixed $items */ protected function getArrayableItems($items): array { if (is_array($items)) { return $items; } if ($items instanceof self) { return $items->all(); } if ($items instanceof Arrayable) { return $items->toArray(); } if ($items instanceof Jsonable) { return json_decode($items->__toString(), true); } if ($items instanceof JsonSerializable) { return $items->jsonSerialize(); } if ($items instanceof Traversable) { return iterator_to_array($items); } return (array) $items; } } src/Composer.php000064400000010674151360554240007650 0ustar00offsetGet('packages') ?? []; $packagesDev = self::$content->offsetGet('packages-dev') ?? []; foreach (array_merge($packages, $packagesDev) as $package) { $packageName = ''; foreach ($package ?? [] as $key => $value) { if ($key === 'name') { $packageName = $value; continue; } switch ($key) { case 'extra': $packageName && self::$extra[$packageName] = $value; break; case 'scripts': $packageName && self::$scripts[$packageName] = $value; break; case 'version': $packageName && self::$versions[$packageName] = $value; break; } } } } return self::$content; } public static function getJsonContent(): Collection { if (! self::$json) { $path = BASE_PATH . '/composer.json'; if (! is_readable($path)) { throw new \RuntimeException('composer.json is not readable.'); } self::$json = collect(json_decode(file_get_contents($path), true)); } return self::$json; } public static function discoverLockFile(): string { $path = ''; if (is_readable(BASE_PATH . '/composer.lock')) { $path = BASE_PATH . '/composer.lock'; } return $path; } public static function getMergedExtra(string $key = null) { if (! self::$extra) { self::getLockContent(); } if ($key === null) { return self::$extra; } $extra = []; foreach (self::$extra ?? [] as $project => $config) { foreach ($config ?? [] as $configKey => $item) { if ($key === $configKey && $item) { foreach ($item ?? [] as $k => $v) { if (is_array($v)) { $extra[$k] = array_merge($extra[$k] ?? [], $v); } else { $extra[$k][] = $v; } } } } } return $extra; } public static function getLoader(): ClassLoader { if (! self::$classLoader) { self::$classLoader = self::findLoader(); } return self::$classLoader; } public static function setLoader(ClassLoader $classLoader): ClassLoader { self::$classLoader = $classLoader; return $classLoader; } private static function findLoader(): ClassLoader { $composerClass = ''; foreach (get_declared_classes() as $declaredClass) { if (strpos($declaredClass, 'ComposerAutoloaderInit') === 0 && method_exists($declaredClass, 'getLoader')) { $composerClass = $declaredClass; break; } } if (! $composerClass) { throw new \RuntimeException('Composer loader not found.'); } return $composerClass::getLoader(); } } src/ConfigProvider.php000064400000001606151360554240010774 0ustar00 [ Serializer::class => SerializerFactory::class, NormalizerInterface::class => SimpleNormalizer::class, ], 'annotations' => [ 'scan' => [ 'paths' => [ __DIR__, ], ], ], ]; } } src/Context.php000064400000004767151360554240007513 0ustar00exchangeArray($keys ? Arr::only($from->getArrayCopy(), $keys) : $from->getArrayCopy()); } /** * Retrieve the value and override it by closure. */ public static function override(string $id, \Closure $closure) { $value = null; if (self::has($id)) { $value = self::get($id); } $value = $closure($value); self::set($id, $value); return $value; } /** * Retrieve the value and store it if not exists. * @param mixed $value */ public static function getOrSet(string $id, $value) { if (! self::has($id)) { return self::set($id, value($value)); } return self::get($id); } public static function getContainer() { if (Coroutine::inCoroutine()) { return Co::getContextFor(); } return static::$nonCoContext; } } src/Contracts/Arrayable.php000064400000000517151360554240011716 0ustar00channel = new Channel(1); } /** * Yield the current coroutine for a given timeout, * unless the coordinator is woke up from outside. * * @param float|int $timeout * @return bool returns true if the coordinator has been woken up */ public function yield($timeout = -1): bool { $this->channel->pop((float) $timeout); return $this->channel->isClosing(); } /** * Wakeup all coroutines yielding for this coordinator. */ public function resume(): void { $this->channel->close(); } } src/Coordinator/CoordinatorManager.php000064400000002521151360554240014112 0ustar00has(StdoutLoggerInterface::class)) { /* @var LoggerInterface $logger */ $logger = $container->get(StdoutLoggerInterface::class); /* @var FormatterInterface $formatter */ if ($container->has(FormatterInterface::class)) { $formatter = $container->get(FormatterInterface::class); $logger->warning($formatter->format($throwable)); } else { $logger->warning(sprintf('Uncaptured exception[%s] detected in %s::%d.', get_class($throwable), $throwable->getFile(), $throwable->getLine())); } } } } }); try { return $coroutine->getId(); } catch (\Throwable $exception) { return -1; } } public static function inCoroutine(): bool { return Co::id() > 0; } } src/Coroutine/Concurrent.php000064400000004232151360554240012143 0ustar00limit = $limit; $this->channel = new Channel($limit); } public function __call($name, $arguments) { if (in_array($name, ['isFull', 'isEmpty'])) { return $this->channel->{$name}(...$arguments); } } public function getLimit(): int { return $this->limit; } public function length(): int { return $this->channel->getLength(); } public function getLength(): int { return $this->channel->getLength(); } public function getRunningCoroutineCount(): int { return $this->getLength(); } public function create(callable $callable): void { $this->channel->push(true); Coroutine::create(function () use ($callable) { try { $callable(); } catch (\Throwable $exception) { if (ApplicationContext::hasContainer()) { $container = ApplicationContext::getContainer(); if ($container->has(StdoutLoggerInterface::class) && $container->has(FormatterInterface::class)) { $logger = $container->get(StdoutLoggerInterface::class); $formatter = $container->get(FormatterInterface::class); $logger->error($formatter->format($exception)); } } } finally { $this->channel->pop(); } }); } } src/Coroutine/Locker.php000064400000002352151360554240011241 0ustar00 0) { SwooleCoroutine::resume($id); } } self::clear($key); } } } src/Exception/ChannelClosedException.php000064400000000514151360554240014370 0ustar00throwable = $throwable; } public function getThrowable(): Throwable { return $this->throwable; } } src/Exception/InvalidArgumentException.php000064400000000526151360554240014762 0ustar00results; } public function setResults(array $results) { $this->results = $results; } public function getThrowables() { return $this->throwables; } public function setThrowables(array $throwables) { return $this->throwables = $throwables; } } src/Exception/TimeoutException.php000064400000000506151360554240013315 0ustar00isFile($path)) { return $lock ? $this->sharedGet($path) : file_get_contents($path); } throw new FileNotFoundException("File does not exist at path {$path}"); } /** * Get contents of a file with shared access. */ public function sharedGet(string $path): string { return $this->atomic($path, function ($path) { $contents = ''; $handle = fopen($path, 'rb'); if ($handle) { $wouldBlock = false; flock($handle, LOCK_SH | LOCK_NB, $wouldBlock); while ($wouldBlock) { usleep(1000); flock($handle, LOCK_SH | LOCK_NB, $wouldBlock); } try { clearstatcache(true, $path); $contents = fread($handle, $this->size($path) ?: 1); } finally { flock($handle, LOCK_UN); fclose($handle); } } return $contents; }); } /** * Get the returned value of a file. * * @throws \Hyperf\Utils\Filesystem\FileNotFoundException */ public function getRequire(string $path) { if ($this->isFile($path)) { return require $path; } throw new FileNotFoundException("File does not exist at path {$path}"); } /** * Require the given file once. * * @return mixed */ public function requireOnce(string $file) { require_once $file; } /** * Get the MD5 hash of the file at the given path. */ public function hash(string $path): string { return md5_file($path); } /** * Write the contents of a file. * * @param resource|string $contents * @return bool|int */ public function put(string $path, $contents, bool $lock = false) { if ($lock) { return $this->atomic($path, function ($path) use ($contents) { $handle = fopen($path, 'w+'); if ($handle) { $wouldBlock = false; flock($handle, LOCK_EX | LOCK_NB, $wouldBlock); while ($wouldBlock) { usleep(1000); flock($handle, LOCK_EX | LOCK_NB, $wouldBlock); } try { fwrite($handle, $contents); } finally { flock($handle, LOCK_UN); fclose($handle); } } }); } return file_put_contents($path, $contents); } /** * Write the contents of a file, replacing it atomically if it already exists. */ public function replace(string $path, string $content) { // If the path already exists and is a symlink, get the real path... clearstatcache(true, $path); $path = realpath($path) ?: $path; $tempPath = tempnam(dirname($path), basename($path)); // Fix permissions of tempPath because `tempnam()` creates it with permissions set to 0600... chmod($tempPath, 0777 - umask()); file_put_contents($tempPath, $content); rename($tempPath, $path); } /** * Prepend to a file. */ public function prepend(string $path, string $data): int { if ($this->exists($path)) { return $this->put($path, $data . $this->get($path)); } return $this->put($path, $data); } /** * Append to a file. */ public function append(string $path, string $data): int { return file_put_contents($path, $data, FILE_APPEND); } /** * Get or set UNIX mode of a file or directory. */ public function chmod(string $path, ?int $mode = null) { if ($mode) { return chmod($path, $mode); } return substr(sprintf('%o', fileperms($path)), -4); } /** * Delete the file at a given path. * * @param array|string $paths */ public function delete($paths): bool { $paths = is_array($paths) ? $paths : func_get_args(); $success = true; foreach ($paths as $path) { try { if (! @unlink($path)) { $success = false; } } catch (ErrorException $e) { $success = false; } } return $success; } /** * Move a file to a new location. */ public function move(string $path, string $target): bool { return rename($path, $target); } /** * Copy a file to a new location. */ public function copy(string $path, string $target): bool { return copy($path, $target); } /** * Create a hard link to the target file or directory. */ public function link(string $target, string $link) { if (! $this->windowsOs()) { return symlink($target, $link); } $mode = $this->isDirectory($target) ? 'J' : 'H'; exec("mklink /{$mode} \"{$link}\" \"{$target}\""); } /** * Extract the file name from a file path. */ public function name(string $path): string { return pathinfo($path, PATHINFO_FILENAME); } /** * Extract the trailing name component from a file path. */ public function basename(string $path): string { return pathinfo($path, PATHINFO_BASENAME); } /** * Extract the parent directory from a file path. */ public function dirname(string $path): string { return pathinfo($path, PATHINFO_DIRNAME); } /** * Extract the file extension from a file path. */ public function extension(string $path): string { return pathinfo($path, PATHINFO_EXTENSION); } /** * Get the file type of a given file. */ public function type(string $path): string { return filetype($path); } /** * Get the mime-type of a given file. * * @return false|string */ public function mimeType(string $path) { return finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); } /** * Get the file size of a given file. */ public function size(string $path): int { return filesize($path); } /** * Get the file's last modification time. */ public function lastModified(string $path): int { return filemtime($path); } /** * Determine if the given path is a directory. */ public function isDirectory(string $directory): bool { return is_dir($directory); } /** * Determine if the given path is readable. */ public function isReadable(string $path): bool { return is_readable($path); } /** * Determine if the given path is writable. */ public function isWritable(string $path): bool { return is_writable($path); } /** * Determine if the given path is a file. */ public function isFile(string $file): bool { return is_file($file); } /** * Find path names matching a given pattern. */ public function glob(string $pattern, int $flags = 0): array { return glob($pattern, $flags); } /** * Get an array of all files in a directory. * * @return \Symfony\Component\Finder\SplFileInfo[] */ public function files(string $directory, bool $hidden = false): array { return iterator_to_array( Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->depth(0)->sortByName(), false ); } /** * Get all of the files from the given directory (recursive). * @return \Symfony\Component\Finder\SplFileInfo[] */ public function allFiles(string $directory, bool $hidden = false): array { return iterator_to_array( Finder::create()->files()->ignoreDotFiles(! $hidden)->in($directory)->sortByName(), false ); } /** * Get all of the directories within a given directory. */ public function directories(string $directory): array { $directories = []; foreach (Finder::create()->in($directory)->directories()->depth(0)->sortByName() as $dir) { $directories[] = $dir->getPathname(); } return $directories; } /** * Create a directory. */ public function makeDirectory(string $path, int $mode = 0755, bool $recursive = false, bool $force = false): bool { if ($force) { return @mkdir($path, $mode, $recursive); } return mkdir($path, $mode, $recursive); } /** * Move a directory. */ public function moveDirectory(string $from, string $to, bool $overwrite = false): bool { if ($overwrite && $this->isDirectory($to) && ! $this->deleteDirectory($to)) { return false; } return @rename($from, $to) === true; } /** * Copy a directory from one location to another. */ public function copyDirectory(string $directory, string $destination, int $options = null): bool { if (! $this->isDirectory($directory)) { return false; } $options = $options ?: FilesystemIterator::SKIP_DOTS; // If the destination directory does not actually exist, we will go ahead and // create it recursively, which just gets the destination prepared to copy // the files over. Once we make the directory we'll proceed the copying. if (! $this->isDirectory($destination)) { $this->makeDirectory($destination, 0777, true); } $items = new FilesystemIterator($directory, $options); foreach ($items as $item) { // As we spin through items, we will check to see if the current file is actually // a directory or a file. When it is actually a directory we will need to call // back into this function recursively to keep copying these nested folders. $target = $destination . DIRECTORY_SEPARATOR . $item->getBasename(); if ($item->isDir()) { $path = $item->getPathname(); if (! $this->copyDirectory($path, $target, $options)) { return false; } } // If the current items is just a regular file, we will just copy this to the new // location and keep looping. If for some reason the copy fails we'll bail out // and return false, so the developer is aware that the copy process failed. else { if (! $this->copy($item->getPathname(), $target)) { return false; } } } return true; } /** * Recursively delete a directory. * * The directory itself may be optionally preserved. */ public function deleteDirectory(string $directory, bool $preserve = false): bool { if (! $this->isDirectory($directory)) { return false; } $items = new FilesystemIterator($directory); foreach ($items as $item) { // If the item is a directory, we can just recurse into the function and // delete that sub-directory otherwise we'll just delete the file and // keep iterating through each file until the directory is cleaned. if ($item->isDir() && ! $item->isLink()) { $this->deleteDirectory($item->getPathname()); } // If the item is just a file, we can go ahead and delete it since we're // just looping through and waxing all of the files in this directory // and calling directories recursively, so we delete the real path. else { $this->delete($item->getPathname()); } } if (! $preserve) { @rmdir($directory); } return true; } /** * Remove all of the directories within a given directory. */ public function deleteDirectories(string $directory): bool { $allDirectories = $this->directories($directory); if (! empty($allDirectories)) { foreach ($allDirectories as $directoryName) { $this->deleteDirectory($directoryName); } return true; } return false; } /** * Empty the specified directory of all files and folders. */ public function cleanDirectory(string $directory): bool { return $this->deleteDirectory($directory, true); } /** * Detect whether it's Windows. */ public function windowsOs(): bool { return stripos(PHP_OS, 'win') === 0; } protected function atomic($path, $callback) { if (Coroutine::inCoroutine()) { try { while (! Coroutine\Locker::lock($path)) { usleep(1000); } return $callback($path); } finally { Coroutine\Locker::unlock($path); } } else { return $callback($path); } } } src/Fluent.php000075500000007775151360554240007331 0ustar00 $value) { $this->attributes[$key] = $value; } } /** * Handle dynamic calls to the fluent instance to set attributes. * * @param string $method * @param array $parameters * @return $this */ public function __call($method, $parameters) { $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true; return $this; } /** * Dynamically retrieve the value of an attribute. * * @param string $key */ public function __get($key) { return $this->get($key); } /** * Dynamically set the value of an attribute. * * @param string $key * @param mixed $value */ public function __set($key, $value) { $this->offsetSet($key, $value); } /** * Dynamically check if an attribute is set. * * @param string $key * @return bool */ public function __isset($key) { return $this->offsetExists($key); } /** * Dynamically unset an attribute. * * @param string $key */ public function __unset($key) { $this->offsetUnset($key); } public function __toString(): string { return $this->toJson(); } /** * Get an attribute from the fluent instance. * * @param string $key * @param null|mixed $default */ public function get($key, $default = null) { if (array_key_exists($key, $this->attributes)) { return $this->attributes[$key]; } return value($default); } /** * Get the attributes from the fluent instance. * * @return array */ public function getAttributes() { return $this->attributes; } /** * Convert the fluent instance to an array. */ public function toArray(): array { return $this->attributes; } /** * Convert the object into something JSON serializable. * * @return array */ public function jsonSerialize() { return $this->toArray(); } /** * Convert the fluent instance to JSON. * * @param int $options * @return string */ public function toJson($options = 0) { return json_encode($this->jsonSerialize(), $options); } /** * Determine if the given offset exists. * * @param string $offset * @return bool */ public function offsetExists($offset) { return isset($this->attributes[$offset]); } /** * Get the value for a given offset. * * @param string $offset */ public function offsetGet($offset) { return $this->get($offset); } /** * Set the value at the given offset. * * @param string $offset * @param mixed $value */ public function offsetSet($offset, $value) { $this->attributes[$offset] = $value; } /** * Unset the value at the given offset. * * @param string $offset */ public function offsetUnset($offset) { unset($this->attributes[$offset]); } } src/Functions.php000064400000031324151360554240010024 0ustar00 1 && $value[0] === '"' && $value[$valueLength - 1] === '"') { return substr($value, 1, -1); } return $value; } } if (! function_exists('retry')) { /** * Retry an operation a given number of times. * * @param float|int $times * @param int $sleep millisecond * @throws \Throwable */ function retry($times, callable $callback, int $sleep = 0) { $backoff = new Backoff($sleep); beginning: try { return $callback(); } catch (\Throwable $e) { if (--$times < 0) { throw $e; } $backoff->sleep(); goto beginning; } } } if (! function_exists('with')) { /** * Return the given value, optionally passed through the given callback. * * @param mixed $value */ function with($value, callable $callback = null) { return is_null($callback) ? $value : $callback($value); } } if (! function_exists('collect')) { /** * Create a collection from the given value. * * @param null|mixed $value * @return Collection */ function collect($value = null) { return new Collection($value); } } if (! function_exists('data_fill')) { /** * Fill in data where it's missing. * * @param mixed $target * @param array|string $key * @param mixed $value */ function data_fill(&$target, $key, $value) { return data_set($target, $key, $value, false); } } if (! function_exists('data_get')) { /** * Get an item from an array or object using "dot" notation. * * @param null|array|int|string $key * @param null|mixed $default * @param mixed $target */ function data_get($target, $key, $default = null) { if (is_null($key)) { return $target; } $key = is_array($key) ? $key : explode('.', is_int($key) ? (string) $key : $key); while (! is_null($segment = array_shift($key))) { if ($segment === '*') { if ($target instanceof Collection) { $target = $target->all(); } elseif (! is_array($target)) { return value($default); } $result = []; foreach ($target as $item) { $result[] = data_get($item, $key); } return in_array('*', $key) ? Arr::collapse($result) : $result; } if (Arr::accessible($target) && Arr::exists($target, $segment)) { $target = $target[$segment]; } elseif (is_object($target) && isset($target->{$segment})) { $target = $target->{$segment}; } else { return value($default); } } return $target; } } if (! function_exists('data_set')) { /** * Set an item on an array or object using dot notation. * * @param mixed $target * @param array|string $key * @param bool $overwrite * @param mixed $value */ function data_set(&$target, $key, $value, $overwrite = true) { $segments = is_array($key) ? $key : explode('.', $key); if (($segment = array_shift($segments)) === '*') { if (! Arr::accessible($target)) { $target = []; } if ($segments) { foreach ($target as &$inner) { data_set($inner, $segments, $value, $overwrite); } } elseif ($overwrite) { foreach ($target as &$inner) { $inner = $value; } } } elseif (Arr::accessible($target)) { if ($segments) { if (! Arr::exists($target, $segment)) { $target[$segment] = []; } data_set($target[$segment], $segments, $value, $overwrite); } elseif ($overwrite || ! Arr::exists($target, $segment)) { $target[$segment] = $value; } } elseif (is_object($target)) { if ($segments) { if (! isset($target->{$segment})) { $target->{$segment} = []; } data_set($target->{$segment}, $segments, $value, $overwrite); } elseif ($overwrite || ! isset($target->{$segment})) { $target->{$segment} = $value; } } else { $target = []; if ($segments) { $target[$segment] = []; data_set($target[$segment], $segments, $value, $overwrite); } elseif ($overwrite) { $target[$segment] = $value; } } return $target; } } if (! function_exists('head')) { /** * Get the first element of an array. Useful for method chaining. * * @param array $array */ function head($array) { return reset($array); } } if (! function_exists('last')) { /** * Get the last element from an array. * * @param array $array */ function last($array) { return end($array); } } if (! function_exists('tap')) { /** * Call the given Closure with the given value then return the value. * * @param null|callable $callback * @param mixed $value */ function tap($value, $callback = null) { if (is_null($callback)) { return new HigherOrderTapProxy($value); } $callback($value); return $value; } } if (! function_exists('call')) { /** * Call a callback with the arguments. * * @param mixed $callback * @return null|mixed */ function call($callback, array $args = []) { $result = null; if ($callback instanceof \Closure) { $result = $callback(...$args); } elseif (is_object($callback) || (is_string($callback) && function_exists($callback))) { $result = $callback(...$args); } elseif (is_array($callback)) { [$object, $method] = $callback; $result = is_object($object) ? $object->{$method}(...$args) : $object::$method(...$args); } else { $result = call_user_func_array($callback, $args); } return $result; } } if (! function_exists('go')) { /** * @return bool|int */ function go(callable $callable) { $id = Coroutine::create($callable); return $id > 0 ? $id : false; } } if (! function_exists('co')) { /** * @return bool|int */ function co(callable $callable) { $id = Coroutine::create($callable); return $id > 0 ? $id : false; } } if (! function_exists('defer')) { function defer(callable $callable): void { Coroutine::defer($callable); } } if (! function_exists('class_basename')) { /** * Get the class "basename" of the given object / class. * * @param object|string $class * @return string */ function class_basename($class) { $class = is_object($class) ? get_class($class) : $class; return basename(str_replace('\\', '/', $class)); } } if (! function_exists('trait_uses_recursive')) { /** * Returns all traits used by a trait and its traits. * * @param string $trait * @return array */ function trait_uses_recursive($trait) { $traits = class_uses($trait); foreach ($traits as $trait) { $traits += trait_uses_recursive($trait); } return $traits; } } if (! function_exists('class_uses_recursive')) { /** * Returns all traits used by a class, its parent classes and trait of their traits. * * @param object|string $class * @return array */ function class_uses_recursive($class) { if (is_object($class)) { $class = get_class($class); } $results = []; /* @phpstan-ignore-next-line */ foreach (array_reverse(class_parents($class)) + [$class => $class] as $class) { $results += trait_uses_recursive($class); } return array_unique($results); } } if (! function_exists('setter')) { /** * Create a setter string. */ function setter(string $property): string { return 'set' . Str::studly($property); } } if (! function_exists('getter')) { /** * Create a getter string. */ function getter(string $property): string { return 'get' . Str::studly($property); } } if (! function_exists('parallel')) { /** * @param callable[] $callables * @param int $concurrent if $concurrent is equal to 0, that means unlimit */ function parallel(array $callables, int $concurrent = 0) { $parallel = new Parallel($concurrent); foreach ($callables as $key => $callable) { $parallel->add($callable, $key); } return $parallel->wait(); } } if (! function_exists('make')) { /** * Create a object instance, if the DI container exist in ApplicationContext, * then the object will be create by DI container via `make()` method, if not, * the object will create by `new` keyword. */ function make(string $name, array $parameters = []) { if (ApplicationContext::hasContainer()) { $container = ApplicationContext::getContainer(); if (method_exists($container, 'make')) { return $container->make($name, $parameters); } } $parameters = array_values($parameters); return new $name(...$parameters); } } if (! function_exists('run')) { /** * Run callable in non-coroutine environment, all hook functions by Swoole only available in the callable. * * @param array|callable $callbacks */ function run($callbacks, int $flags = SWOOLE_HOOK_ALL): bool { if (Coroutine::inCoroutine()) { throw new RuntimeException('Function \'run\' only execute in non-coroutine environment.'); } \Swoole\Runtime::enableCoroutine($flags); $result = \Swoole\Coroutine\Run(...(array) $callbacks); \Swoole\Runtime::enableCoroutine(false); return $result; } } if (! function_exists('swoole_hook_flags')) { /** * Return the default swoole hook flags, you can rewrite it by defining `SWOOLE_HOOK_FLAGS`. */ function swoole_hook_flags(): int { return defined('SWOOLE_HOOK_FLAGS') ? SWOOLE_HOOK_FLAGS : SWOOLE_HOOK_ALL; } } if (! function_exists('optional')) { /** * Provide access to optional objects. * * @param mixed $value * @return mixed */ function optional($value = null, callable $callback = null) { if (is_null($callback)) { return new Optional($value); } if (! is_null($value)) { return $callback($value); } } } if (! function_exists('wait')) { function wait(Closure $closure, ?float $timeout = null) { if (ApplicationContext::hasContainer()) { $waiter = ApplicationContext::getContainer()->get(Waiter::class); return $waiter->wait($closure, $timeout); } return (new Waiter())->wait($closure, $timeout); } } src/HigherOrderCollectionProxy.php000064400000002700151360554240013330 0ustar00method = $method; $this->collection = $collection; } /** * Proxy accessing an attribute onto the collection items. */ public function __get(string $key) { return $this->collection->{$this->method}(function ($value) use ($key) { return is_array($value) ? $value[$key] : $value->{$key}; }); } /** * Proxy a method call onto the collection items. */ public function __call(string $method, array $parameters) { return $this->collection->{$this->method}(function ($value) use ($method, $parameters) { return $value->{$method}(...$parameters); }); } } src/HigherOrderTapProxy.php000064400000001540151360554240011762 0ustar00target = $target; } /** * Dynamically pass method calls to the target. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { $this->target->{$method}(...$parameters); return $this->target; } } src/InteractsWithTime.php000064400000003355151360554240011466 0ustar00parseDateInterval($delay); return $delay instanceof DateTimeInterface ? max(0, $delay->getTimestamp() - $this->currentTime()) : (int) $delay; } /** * Get the "available at" UNIX timestamp. * * @param \DateInterval|\DateTimeInterface|int $delay */ protected function availableAt($delay = 0): int { $delay = $this->parseDateInterval($delay); return $delay instanceof DateTimeInterface ? $delay->getTimestamp() : Carbon::now()->addSeconds($delay)->getTimestamp(); } /** * If the given value is an interval, convert it to a DateTime instance. * * @param \DateInterval|\DateTimeInterface|int $delay * @return \DateTimeInterface|int */ protected function parseDateInterval($delay) { if ($delay instanceof DateInterval) { $delay = Carbon::now()->add($delay); } return $delay; } /** * Get the current system time as a UNIX timestamp. */ protected function currentTime(): int { return Carbon::now()->getTimestamp(); } } src/MessageBag.php000064400000020644151360554240010055 0ustar00 $value) { $value = $value instanceof Arrayable ? $value->toArray() : (array) $value; $this->messages[$key] = array_unique($value); } } /** * Convert the message bag to its string representation. */ public function __toString(): string { return $this->toJson(); } /** * Get the keys present in the message bag. */ public function keys(): array { return array_keys($this->messages); } /** * Add a message to the message bag. */ public function add(string $key, string $message): MessageBagContract { if ($this->isUnique($key, $message)) { $this->messages[$key][] = $message; } return $this; } /** * Merge a new array of messages into the message bag. * * @param array|MessageProvider $messages * @return $this */ public function merge($messages) { if ($messages instanceof MessageProvider) { $messages = $messages->getMessageBag()->getMessages(); } $this->messages = array_merge_recursive($this->messages, $messages); return $this; } /** * Determine if messages exist for all of the given keys. * * @param null|array|string $key */ public function has($key): bool { if ($this->isEmpty()) { return false; } if (is_null($key)) { return $this->any(); } $keys = is_array($key) ? $key : func_get_args(); foreach ($keys as $key) { if ($this->first($key) === '') { return false; } } return true; } /** * Determine if messages exist for any of the given keys. * * @param array|string $keys */ public function hasAny($keys = []): bool { if ($this->isEmpty()) { return false; } $keys = is_array($keys) ? $keys : func_get_args(); foreach ($keys as $key) { if ($this->has($key)) { return true; } } return false; } /** * Get the first message from the message bag for a given key. * * @param string $key * @param string $format */ public function first($key = null, $format = null): string { $messages = is_null($key) ? $this->all($format) : $this->get($key, $format); $firstMessage = Arr::first($messages, null, ''); return is_array($firstMessage) ? Arr::first($firstMessage) : $firstMessage; } /** * Get all of the messages from the message bag for a given key. */ public function get(string $key, ?string $format = null): array { // If the message exists in the message bag, we will transform it and return // the message. Otherwise, we will check if the key is implicit & collect // all the messages that match the given key and output it as an array. if (array_key_exists($key, $this->messages)) { return $this->transform( $this->messages[$key], $this->checkFormat($format), $key ); } if (Str::contains($key, '*')) { return $this->getMessagesForWildcardKey($key, $format); } return []; } /** * Get all of the messages for every key in the message bag. */ public function all(?string $format = null): array { $format = $this->checkFormat($format); $all = []; foreach ($this->messages as $key => $messages) { $all = array_merge($all, $this->transform($messages, $format, $key)); } return $all; } /** * Get all of the unique messages for every key in the message bag. */ public function unique(?string $format = null): array { return array_unique($this->all($format)); } /** * Get the raw messages in the message bag. */ public function messages(): array { return $this->messages; } /** * Get the raw messages in the message bag. */ public function getMessages(): array { return $this->messages(); } /** * Get the messages for the instance. */ public function getMessageBag(): MessageBagContract { return $this; } /** * Get the default message format. */ public function getFormat(): string { return $this->format; } /** * Set the default message format. */ public function setFormat(string $format = ':message'): self { $this->format = $format; return $this; } /** * Determine if the message bag has any messages. */ public function isEmpty(): bool { return ! $this->any(); } /** * Determine if the message bag has any messages. */ public function isNotEmpty(): bool { return $this->any(); } /** * Determine if the message bag has any messages. */ public function any(): bool { return $this->count() > 0; } /** * Get the number of messages in the message bag. */ public function count(): int { return count($this->messages, COUNT_RECURSIVE) - count($this->messages); } /** * Get the instance as an array. */ public function toArray(): array { return $this->getMessages(); } /** * Convert the object into something JSON serializable. */ public function jsonSerialize(): array { return $this->toArray(); } /** * Convert the object to its JSON representation. */ public function toJson(int $options = 0): string { return json_encode($this->jsonSerialize(), $options); } /** * Determine if a key and message combination already exists. */ protected function isUnique(string $key, string $message): bool { $messages = (array) $this->messages; return ! isset($messages[$key]) || ! in_array($message, $messages[$key]); } /** * Get the messages for a wildcard key. */ protected function getMessagesForWildcardKey(string $key, ?string $format): array { return collect($this->messages) ->filter(function ($messages, $messageKey) use ($key) { return Str::is($key, $messageKey); }) ->map(function ($messages, $messageKey) use ($format) { return $this->transform( $messages, $this->checkFormat($format), $messageKey ); })->all(); } /** * Format an array of messages. */ protected function transform(array $messages, string $format, string $messageKey): array { return collect($messages) ->map(function ($message) use ($format, $messageKey) { // We will simply spin through the given messages and transform each one // replacing the :message place holder with the real message allowing // the messages to be easily formatted to each developer's desires. return str_replace([':message', ':key'], [$message, $messageKey], $format); })->all(); } /** * Get the appropriate format based on the given format. */ protected function checkFormat(?string $format): string { return $format ?: $this->format; } } src/MimeTypeExtensionGuesser.php000064400000107505151360554240013045 0ustar00 'ez', 'application/applixware' => 'aw', 'application/atom+xml' => 'atom', 'application/atomcat+xml' => 'atomcat', 'application/atomsvc+xml' => 'atomsvc', 'application/ccxml+xml' => 'ccxml', 'application/cdmi-capability' => 'cdmia', 'application/cdmi-container' => 'cdmic', 'application/cdmi-domain' => 'cdmid', 'application/cdmi-object' => 'cdmio', 'application/cdmi-queue' => 'cdmiq', 'application/cu-seeme' => 'cu', 'application/davmount+xml' => 'davmount', 'application/docbook+xml' => 'dbk', 'application/dssc+der' => 'dssc', 'application/dssc+xml' => 'xdssc', 'application/ecmascript' => 'ecma', 'application/emma+xml' => 'emma', 'application/epub+zip' => 'epub', 'application/exi' => 'exi', 'application/font-tdpfr' => 'pfr', 'application/gml+xml' => 'gml', 'application/gpx+xml' => 'gpx', 'application/gxf' => 'gxf', 'application/hyperstudio' => 'stk', 'application/inkml+xml' => 'ink', 'application/ipfix' => 'ipfix', 'application/java-archive' => 'jar', 'application/java-serialized-object' => 'ser', 'application/java-vm' => 'class', 'application/javascript' => 'js', 'application/json' => 'json', 'application/jsonml+json' => 'jsonml', 'application/lost+xml' => 'lostxml', 'application/mac-binhex40' => 'hqx', 'application/mac-compactpro' => 'cpt', 'application/mads+xml' => 'mads', 'application/marc' => 'mrc', 'application/marcxml+xml' => 'mrcx', 'application/mathematica' => 'ma', 'application/mathml+xml' => 'mathml', 'application/mbox' => 'mbox', 'application/mediaservercontrol+xml' => 'mscml', 'application/metalink+xml' => 'metalink', 'application/metalink4+xml' => 'meta4', 'application/mets+xml' => 'mets', 'application/mods+xml' => 'mods', 'application/mp21' => 'm21', 'application/mp4' => 'mp4s', 'application/msword' => 'doc', 'application/mxf' => 'mxf', 'application/octet-stream' => 'bin', 'application/oda' => 'oda', 'application/oebps-package+xml' => 'opf', 'application/ogg' => 'ogx', 'application/omdoc+xml' => 'omdoc', 'application/onenote' => 'onetoc', 'application/oxps' => 'oxps', 'application/patch-ops-error+xml' => 'xer', 'application/pdf' => 'pdf', 'application/pgp-encrypted' => 'pgp', 'application/pgp-signature' => 'asc', 'application/pics-rules' => 'prf', 'application/pkcs10' => 'p10', 'application/pkcs7-mime' => 'p7m', 'application/pkcs7-signature' => 'p7s', 'application/pkcs8' => 'p8', 'application/pkix-attr-cert' => 'ac', 'application/pkix-cert' => 'cer', 'application/pkix-crl' => 'crl', 'application/pkix-pkipath' => 'pkipath', 'application/pkixcmp' => 'pki', 'application/pls+xml' => 'pls', 'application/postscript' => 'ai', 'application/prs.cww' => 'cww', 'application/pskc+xml' => 'pskcxml', 'application/rdf+xml' => 'rdf', 'application/reginfo+xml' => 'rif', 'application/relax-ng-compact-syntax' => 'rnc', 'application/resource-lists+xml' => 'rl', 'application/resource-lists-diff+xml' => 'rld', 'application/rls-services+xml' => 'rs', 'application/rpki-ghostbusters' => 'gbr', 'application/rpki-manifest' => 'mft', 'application/rpki-roa' => 'roa', 'application/rsd+xml' => 'rsd', 'application/rss+xml' => 'rss', 'application/rtf' => 'rtf', 'application/sbml+xml' => 'sbml', 'application/scvp-cv-request' => 'scq', 'application/scvp-cv-response' => 'scs', 'application/scvp-vp-request' => 'spq', 'application/scvp-vp-response' => 'spp', 'application/sdp' => 'sdp', 'application/set-payment-initiation' => 'setpay', 'application/set-registration-initiation' => 'setreg', 'application/shf+xml' => 'shf', 'application/smil+xml' => 'smi', 'application/sparql-query' => 'rq', 'application/sparql-results+xml' => 'srx', 'application/srgs' => 'gram', 'application/srgs+xml' => 'grxml', 'application/sru+xml' => 'sru', 'application/ssdl+xml' => 'ssdl', 'application/ssml+xml' => 'ssml', 'application/tei+xml' => 'tei', 'application/thraud+xml' => 'tfi', 'application/timestamped-data' => 'tsd', 'application/vnd.3gpp.pic-bw-large' => 'plb', 'application/vnd.3gpp.pic-bw-small' => 'psb', 'application/vnd.3gpp.pic-bw-var' => 'pvb', 'application/vnd.3gpp2.tcap' => 'tcap', 'application/vnd.3m.post-it-notes' => 'pwn', 'application/vnd.accpac.simply.aso' => 'aso', 'application/vnd.accpac.simply.imp' => 'imp', 'application/vnd.acucobol' => 'acu', 'application/vnd.acucorp' => 'atc', 'application/vnd.adobe.air-application-installer-package+zip' => 'air', 'application/vnd.adobe.formscentral.fcdt' => 'fcdt', 'application/vnd.adobe.fxp' => 'fxp', 'application/vnd.adobe.xdp+xml' => 'xdp', 'application/vnd.adobe.xfdf' => 'xfdf', 'application/vnd.ahead.space' => 'ahead', 'application/vnd.airzip.filesecure.azf' => 'azf', 'application/vnd.airzip.filesecure.azs' => 'azs', 'application/vnd.amazon.ebook' => 'azw', 'application/vnd.americandynamics.acc' => 'acc', 'application/vnd.amiga.ami' => 'ami', 'application/vnd.android.package-archive' => 'apk', 'application/vnd.anser-web-certificate-issue-initiation' => 'cii', 'application/vnd.anser-web-funds-transfer-initiation' => 'fti', 'application/vnd.antix.game-component' => 'atx', 'application/vnd.apple.installer+xml' => 'mpkg', 'application/vnd.apple.mpegurl' => 'm3u8', 'application/vnd.aristanetworks.swi' => 'swi', 'application/vnd.astraea-software.iota' => 'iota', 'application/vnd.audiograph' => 'aep', 'application/vnd.blueice.multipass' => 'mpm', 'application/vnd.bmi' => 'bmi', 'application/vnd.businessobjects' => 'rep', 'application/vnd.chemdraw+xml' => 'cdxml', 'application/vnd.chipnuts.karaoke-mmd' => 'mmd', 'application/vnd.cinderella' => 'cdy', 'application/vnd.claymore' => 'cla', 'application/vnd.cloanto.rp9' => 'rp9', 'application/vnd.clonk.c4group' => 'c4g', 'application/vnd.cluetrust.cartomobile-config' => 'c11amc', 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz', 'application/vnd.commonspace' => 'csp', 'application/vnd.contact.cmsg' => 'cdbcmsg', 'application/vnd.cosmocaller' => 'cmc', 'application/vnd.crick.clicker' => 'clkx', 'application/vnd.crick.clicker.keyboard' => 'clkk', 'application/vnd.crick.clicker.palette' => 'clkp', 'application/vnd.crick.clicker.template' => 'clkt', 'application/vnd.crick.clicker.wordbank' => 'clkw', 'application/vnd.criticaltools.wbs+xml' => 'wbs', 'application/vnd.ctc-posml' => 'pml', 'application/vnd.cups-ppd' => 'ppd', 'application/vnd.curl.car' => 'car', 'application/vnd.curl.pcurl' => 'pcurl', 'application/vnd.dart' => 'dart', 'application/vnd.data-vision.rdz' => 'rdz', 'application/vnd.dece.data' => 'uvf', 'application/vnd.dece.ttml+xml' => 'uvt', 'application/vnd.dece.unspecified' => 'uvx', 'application/vnd.dece.zip' => 'uvz', 'application/vnd.denovo.fcselayout-link' => 'fe_launch', 'application/vnd.dna' => 'dna', 'application/vnd.dolby.mlp' => 'mlp', 'application/vnd.dpgraph' => 'dpg', 'application/vnd.dreamfactory' => 'dfac', 'application/vnd.ds-keypoint' => 'kpxx', 'application/vnd.dvb.ait' => 'ait', 'application/vnd.dvb.service' => 'svc', 'application/vnd.dynageo' => 'geo', 'application/vnd.ecowin.chart' => 'mag', 'application/vnd.enliven' => 'nml', 'application/vnd.epson.esf' => 'esf', 'application/vnd.epson.msf' => 'msf', 'application/vnd.epson.quickanime' => 'qam', 'application/vnd.epson.salt' => 'slt', 'application/vnd.epson.ssf' => 'ssf', 'application/vnd.eszigno3+xml' => 'es3', 'application/vnd.ezpix-album' => 'ez2', 'application/vnd.ezpix-package' => 'ez3', 'application/vnd.fdf' => 'fdf', 'application/vnd.fdsn.mseed' => 'mseed', 'application/vnd.fdsn.seed' => 'seed', 'application/vnd.flographit' => 'gph', 'application/vnd.fluxtime.clip' => 'ftc', 'application/vnd.framemaker' => 'fm', 'application/vnd.frogans.fnc' => 'fnc', 'application/vnd.frogans.ltf' => 'ltf', 'application/vnd.fsc.weblaunch' => 'fsc', 'application/vnd.fujitsu.oasys' => 'oas', 'application/vnd.fujitsu.oasys2' => 'oa2', 'application/vnd.fujitsu.oasys3' => 'oa3', 'application/vnd.fujitsu.oasysgp' => 'fg5', 'application/vnd.fujitsu.oasysprs' => 'bh2', 'application/vnd.fujixerox.ddd' => 'ddd', 'application/vnd.fujixerox.docuworks' => 'xdw', 'application/vnd.fujixerox.docuworks.binder' => 'xbd', 'application/vnd.fuzzysheet' => 'fzs', 'application/vnd.genomatix.tuxedo' => 'txd', 'application/vnd.geogebra.file' => 'ggb', 'application/vnd.geogebra.tool' => 'ggt', 'application/vnd.geometry-explorer' => 'gex', 'application/vnd.geonext' => 'gxt', 'application/vnd.geoplan' => 'g2w', 'application/vnd.geospace' => 'g3w', 'application/vnd.gmx' => 'gmx', 'application/vnd.google-earth.kml+xml' => 'kml', 'application/vnd.google-earth.kmz' => 'kmz', 'application/vnd.grafeq' => 'gqf', 'application/vnd.groove-account' => 'gac', 'application/vnd.groove-help' => 'ghf', 'application/vnd.groove-identity-message' => 'gim', 'application/vnd.groove-injector' => 'grv', 'application/vnd.groove-tool-message' => 'gtm', 'application/vnd.groove-tool-template' => 'tpl', 'application/vnd.groove-vcard' => 'vcg', 'application/vnd.hal+xml' => 'hal', 'application/vnd.handheld-entertainment+xml' => 'zmm', 'application/vnd.hbci' => 'hbci', 'application/vnd.hhe.lesson-player' => 'les', 'application/vnd.hp-hpgl' => 'hpgl', 'application/vnd.hp-hpid' => 'hpid', 'application/vnd.hp-hps' => 'hps', 'application/vnd.hp-jlyt' => 'jlt', 'application/vnd.hp-pcl' => 'pcl', 'application/vnd.hp-pclxl' => 'pclxl', 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx', 'application/vnd.ibm.minipay' => 'mpy', 'application/vnd.ibm.modcap' => 'afp', 'application/vnd.ibm.rights-management' => 'irm', 'application/vnd.ibm.secure-container' => 'sc', 'application/vnd.iccprofile' => 'icc', 'application/vnd.igloader' => 'igl', 'application/vnd.immervision-ivp' => 'ivp', 'application/vnd.immervision-ivu' => 'ivu', 'application/vnd.insors.igm' => 'igm', 'application/vnd.intercon.formnet' => 'xpw', 'application/vnd.intergeo' => 'i2g', 'application/vnd.intu.qbo' => 'qbo', 'application/vnd.intu.qfx' => 'qfx', 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', 'application/vnd.irepository.package+xml' => 'irp', 'application/vnd.is-xpr' => 'xpr', 'application/vnd.isac.fcs' => 'fcs', 'application/vnd.jam' => 'jam', 'application/vnd.jcp.javame.midlet-rms' => 'rms', 'application/vnd.jisp' => 'jisp', 'application/vnd.joost.joda-archive' => 'joda', 'application/vnd.kahootz' => 'ktz', 'application/vnd.kde.karbon' => 'karbon', 'application/vnd.kde.kchart' => 'chrt', 'application/vnd.kde.kformula' => 'kfo', 'application/vnd.kde.kivio' => 'flw', 'application/vnd.kde.kontour' => 'kon', 'application/vnd.kde.kpresenter' => 'kpr', 'application/vnd.kde.kspread' => 'ksp', 'application/vnd.kde.kword' => 'kwd', 'application/vnd.kenameaapp' => 'htke', 'application/vnd.kidspiration' => 'kia', 'application/vnd.kinar' => 'kne', 'application/vnd.koan' => 'skp', 'application/vnd.kodak-descriptor' => 'sse', 'application/vnd.las.las+xml' => 'lasxml', 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', 'application/vnd.lotus-1-2-3' => '123', 'application/vnd.lotus-approach' => 'apr', 'application/vnd.lotus-freelance' => 'pre', 'application/vnd.lotus-notes' => 'nsf', 'application/vnd.lotus-organizer' => 'org', 'application/vnd.lotus-screencam' => 'scm', 'application/vnd.lotus-wordpro' => 'lwp', 'application/vnd.macports.portpkg' => 'portpkg', 'application/vnd.mcd' => 'mcd', 'application/vnd.medcalcdata' => 'mc1', 'application/vnd.mediastation.cdkey' => 'cdkey', 'application/vnd.mfer' => 'mwf', 'application/vnd.mfmp' => 'mfm', 'application/vnd.micrografx.flo' => 'flo', 'application/vnd.micrografx.igx' => 'igx', 'application/vnd.mif' => 'mif', 'application/vnd.mobius.daf' => 'daf', 'application/vnd.mobius.dis' => 'dis', 'application/vnd.mobius.mbk' => 'mbk', 'application/vnd.mobius.mqy' => 'mqy', 'application/vnd.mobius.msl' => 'msl', 'application/vnd.mobius.plc' => 'plc', 'application/vnd.mobius.txf' => 'txf', 'application/vnd.mophun.application' => 'mpn', 'application/vnd.mophun.certificate' => 'mpc', 'application/vnd.mozilla.xul+xml' => 'xul', 'application/vnd.ms-artgalry' => 'cil', 'application/vnd.ms-cab-compressed' => 'cab', 'application/vnd.ms-excel' => 'xls', 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam', 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb', 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm', 'application/vnd.ms-fontobject' => 'eot', 'application/vnd.ms-htmlhelp' => 'chm', 'application/vnd.ms-ims' => 'ims', 'application/vnd.ms-lrm' => 'lrm', 'application/vnd.ms-officetheme' => 'thmx', 'application/vnd.ms-pki.seccat' => 'cat', 'application/vnd.ms-pki.stl' => 'stl', 'application/vnd.ms-powerpoint' => 'ppt', 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam', 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm', 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm', 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm', 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm', 'application/vnd.ms-project' => 'mpp', 'application/vnd.ms-word.document.macroenabled.12' => 'docm', 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', 'application/vnd.ms-works' => 'wps', 'application/vnd.ms-wpl' => 'wpl', 'application/vnd.ms-xpsdocument' => 'xps', 'application/vnd.mseq' => 'mseq', 'application/vnd.musician' => 'mus', 'application/vnd.muvee.style' => 'msty', 'application/vnd.mynfc' => 'taglet', 'application/vnd.neurolanguage.nlu' => 'nlu', 'application/vnd.nitf' => 'ntf', 'application/vnd.noblenet-directory' => 'nnd', 'application/vnd.noblenet-sealer' => 'nns', 'application/vnd.noblenet-web' => 'nnw', 'application/vnd.nokia.n-gage.data' => 'ngdat', 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage', 'application/vnd.nokia.radio-preset' => 'rpst', 'application/vnd.nokia.radio-presets' => 'rpss', 'application/vnd.novadigm.edm' => 'edm', 'application/vnd.novadigm.edx' => 'edx', 'application/vnd.novadigm.ext' => 'ext', 'application/vnd.oasis.opendocument.chart' => 'odc', 'application/vnd.oasis.opendocument.chart-template' => 'otc', 'application/vnd.oasis.opendocument.database' => 'odb', 'application/vnd.oasis.opendocument.formula' => 'odf', 'application/vnd.oasis.opendocument.formula-template' => 'odft', 'application/vnd.oasis.opendocument.graphics' => 'odg', 'application/vnd.oasis.opendocument.graphics-template' => 'otg', 'application/vnd.oasis.opendocument.image' => 'odi', 'application/vnd.oasis.opendocument.image-template' => 'oti', 'application/vnd.oasis.opendocument.presentation' => 'odp', 'application/vnd.oasis.opendocument.presentation-template' => 'otp', 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', 'application/vnd.oasis.opendocument.text' => 'odt', 'application/vnd.oasis.opendocument.text-master' => 'odm', 'application/vnd.oasis.opendocument.text-template' => 'ott', 'application/vnd.oasis.opendocument.text-web' => 'oth', 'application/vnd.olpc-sugar' => 'xo', 'application/vnd.oma.dd2+xml' => 'dd2', 'application/vnd.openofficeorg.extension' => 'oxt', 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx', 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', 'application/vnd.osgeo.mapguide.package' => 'mgp', 'application/vnd.osgi.dp' => 'dp', 'application/vnd.osgi.subsystem' => 'esa', 'application/vnd.palm' => 'pdb', 'application/vnd.pawaafile' => 'paw', 'application/vnd.pg.format' => 'str', 'application/vnd.pg.osasli' => 'ei6', 'application/vnd.picsel' => 'efif', 'application/vnd.pmi.widget' => 'wg', 'application/vnd.pocketlearn' => 'plf', 'application/vnd.powerbuilder6' => 'pbd', 'application/vnd.previewsystems.box' => 'box', 'application/vnd.proteus.magazine' => 'mgz', 'application/vnd.publishare-delta-tree' => 'qps', 'application/vnd.pvi.ptid1' => 'ptid', 'application/vnd.quark.quarkxpress' => 'qxd', 'application/vnd.realvnc.bed' => 'bed', 'application/vnd.recordare.musicxml' => 'mxl', 'application/vnd.recordare.musicxml+xml' => 'musicxml', 'application/vnd.rig.cryptonote' => 'cryptonote', 'application/vnd.rim.cod' => 'cod', 'application/vnd.rn-realmedia' => 'rm', 'application/vnd.rn-realmedia-vbr' => 'rmvb', 'application/vnd.route66.link66+xml' => 'link66', 'application/vnd.sailingtracker.track' => 'st', 'application/vnd.seemail' => 'see', 'application/vnd.sema' => 'sema', 'application/vnd.semd' => 'semd', 'application/vnd.semf' => 'semf', 'application/vnd.shana.informed.formdata' => 'ifm', 'application/vnd.shana.informed.formtemplate' => 'itp', 'application/vnd.shana.informed.interchange' => 'iif', 'application/vnd.shana.informed.package' => 'ipk', 'application/vnd.simtech-mindmapper' => 'twd', 'application/vnd.smaf' => 'mmf', 'application/vnd.smart.teacher' => 'teacher', 'application/vnd.solent.sdkm+xml' => 'sdkm', 'application/vnd.spotfire.dxp' => 'dxp', 'application/vnd.spotfire.sfs' => 'sfs', 'application/vnd.stardivision.calc' => 'sdc', 'application/vnd.stardivision.draw' => 'sda', 'application/vnd.stardivision.impress' => 'sdd', 'application/vnd.stardivision.math' => 'smf', 'application/vnd.stardivision.writer' => 'sdw', 'application/vnd.stardivision.writer-global' => 'sgl', 'application/vnd.stepmania.package' => 'smzip', 'application/vnd.stepmania.stepchart' => 'sm', 'application/vnd.sun.xml.calc' => 'sxc', 'application/vnd.sun.xml.calc.template' => 'stc', 'application/vnd.sun.xml.draw' => 'sxd', 'application/vnd.sun.xml.draw.template' => 'std', 'application/vnd.sun.xml.impress' => 'sxi', 'application/vnd.sun.xml.impress.template' => 'sti', 'application/vnd.sun.xml.math' => 'sxm', 'application/vnd.sun.xml.writer' => 'sxw', 'application/vnd.sun.xml.writer.global' => 'sxg', 'application/vnd.sun.xml.writer.template' => 'stw', 'application/vnd.sus-calendar' => 'sus', 'application/vnd.svd' => 'svd', 'application/vnd.symbian.install' => 'sis', 'application/vnd.syncml+xml' => 'xsm', 'application/vnd.syncml.dm+wbxml' => 'bdm', 'application/vnd.syncml.dm+xml' => 'xdm', 'application/vnd.tao.intent-module-archive' => 'tao', 'application/vnd.tcpdump.pcap' => 'pcap', 'application/vnd.tmobile-livetv' => 'tmo', 'application/vnd.trid.tpt' => 'tpt', 'application/vnd.triscape.mxs' => 'mxs', 'application/vnd.trueapp' => 'tra', 'application/vnd.ufdl' => 'ufd', 'application/vnd.uiq.theme' => 'utz', 'application/vnd.umajin' => 'umj', 'application/vnd.unity' => 'unityweb', 'application/vnd.uoml+xml' => 'uoml', 'application/vnd.vcx' => 'vcx', 'application/vnd.visio' => 'vsd', 'application/vnd.visionary' => 'vis', 'application/vnd.vsf' => 'vsf', 'application/vnd.wap.wbxml' => 'wbxml', 'application/vnd.wap.wmlc' => 'wmlc', 'application/vnd.wap.wmlscriptc' => 'wmlsc', 'application/vnd.webturbo' => 'wtb', 'application/vnd.wolfram.player' => 'nbp', 'application/vnd.wordperfect' => 'wpd', 'application/vnd.wqd' => 'wqd', 'application/vnd.wt.stf' => 'stf', 'application/vnd.xara' => 'xar', 'application/vnd.xfdl' => 'xfdl', 'application/vnd.yamaha.hv-dic' => 'hvd', 'application/vnd.yamaha.hv-script' => 'hvs', 'application/vnd.yamaha.hv-voice' => 'hvp', 'application/vnd.yamaha.openscoreformat' => 'osf', 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg', 'application/vnd.yamaha.smaf-audio' => 'saf', 'application/vnd.yamaha.smaf-phrase' => 'spf', 'application/vnd.yellowriver-custom-menu' => 'cmp', 'application/vnd.zul' => 'zir', 'application/vnd.zzazz.deck+xml' => 'zaz', 'application/voicexml+xml' => 'vxml', 'application/widget' => 'wgt', 'application/winhlp' => 'hlp', 'application/wsdl+xml' => 'wsdl', 'application/wspolicy+xml' => 'wspolicy', 'application/x-7z-compressed' => '7z', 'application/x-abiword' => 'abw', 'application/x-ace-compressed' => 'ace', 'application/x-apple-diskimage' => 'dmg', 'application/x-authorware-bin' => 'aab', 'application/x-authorware-map' => 'aam', 'application/x-authorware-seg' => 'aas', 'application/x-bcpio' => 'bcpio', 'application/x-bittorrent' => 'torrent', 'application/x-blorb' => 'blb', 'application/x-bzip' => 'bz', 'application/x-bzip2' => 'bz2', 'application/x-cbr' => 'cbr', 'application/x-cdlink' => 'vcd', 'application/x-cfs-compressed' => 'cfs', 'application/x-chat' => 'chat', 'application/x-chess-pgn' => 'pgn', 'application/x-conference' => 'nsc', 'application/x-cpio' => 'cpio', 'application/x-csh' => 'csh', 'application/x-debian-package' => 'deb', 'application/x-dgc-compressed' => 'dgc', 'application/x-director' => 'dir', 'application/x-doom' => 'wad', 'application/x-dtbncx+xml' => 'ncx', 'application/x-dtbook+xml' => 'dtb', 'application/x-dtbresource+xml' => 'res', 'application/x-dvi' => 'dvi', 'application/x-envoy' => 'evy', 'application/x-eva' => 'eva', 'application/x-font-bdf' => 'bdf', 'application/x-font-ghostscript' => 'gsf', 'application/x-font-linux-psf' => 'psf', 'application/x-font-otf' => 'otf', 'application/x-font-pcf' => 'pcf', 'application/x-font-snf' => 'snf', 'application/x-font-ttf' => 'ttf', 'application/x-font-type1' => 'pfa', 'application/x-font-woff' => 'woff', 'application/x-freearc' => 'arc', 'application/x-futuresplash' => 'spl', 'application/x-gca-compressed' => 'gca', 'application/x-glulx' => 'ulx', 'application/x-gnumeric' => 'gnumeric', 'application/x-gramps-xml' => 'gramps', 'application/x-gtar' => 'gtar', 'application/x-hdf' => 'hdf', 'application/x-install-instructions' => 'install', 'application/x-iso9660-image' => 'iso', 'application/x-java-jnlp-file' => 'jnlp', 'application/x-latex' => 'latex', 'application/x-lzh-compressed' => 'lzh', 'application/x-mie' => 'mie', 'application/x-mobipocket-ebook' => 'prc', 'application/x-ms-application' => 'application', 'application/x-ms-shortcut' => 'lnk', 'application/x-ms-wmd' => 'wmd', 'application/x-ms-wmz' => 'wmz', 'application/x-ms-xbap' => 'xbap', 'application/x-msaccess' => 'mdb', 'application/x-msbinder' => 'obd', 'application/x-mscardfile' => 'crd', 'application/x-msclip' => 'clp', 'application/x-msdownload' => 'exe', 'application/x-msmediaview' => 'mvb', 'application/x-msmetafile' => 'wmf', 'application/x-msmoney' => 'mny', 'application/x-mspublisher' => 'pub', 'application/x-msschedule' => 'scd', 'application/x-msterminal' => 'trm', 'application/x-mswrite' => 'wri', 'application/x-netcdf' => 'nc', 'application/x-nzb' => 'nzb', 'application/x-pkcs12' => 'p12', 'application/x-pkcs7-certificates' => 'p7b', 'application/x-pkcs7-certreqresp' => 'p7r', 'application/x-rar-compressed' => 'rar', 'application/x-rar' => 'rar', 'application/x-research-info-systems' => 'ris', 'application/x-sh' => 'sh', 'application/x-shar' => 'shar', 'application/x-shockwave-flash' => 'swf', 'application/x-silverlight-app' => 'xap', 'application/x-sql' => 'sql', 'application/x-stuffit' => 'sit', 'application/x-stuffitx' => 'sitx', 'application/x-subrip' => 'srt', 'application/x-sv4cpio' => 'sv4cpio', 'application/x-sv4crc' => 'sv4crc', 'application/x-t3vm-image' => 't3', 'application/x-tads' => 'gam', 'application/x-tar' => 'tar', 'application/x-tcl' => 'tcl', 'application/x-tex' => 'tex', 'application/x-tex-tfm' => 'tfm', 'application/x-texinfo' => 'texinfo', 'application/x-tgif' => 'obj', 'application/x-ustar' => 'ustar', 'application/x-wais-source' => 'src', 'application/x-x509-ca-cert' => 'der', 'application/x-xfig' => 'fig', 'application/x-xliff+xml' => 'xlf', 'application/x-xpinstall' => 'xpi', 'application/x-xz' => 'xz', 'application/x-zip-compressed' => 'zip', 'application/x-zmachine' => 'z1', 'application/xaml+xml' => 'xaml', 'application/xcap-diff+xml' => 'xdf', 'application/xenc+xml' => 'xenc', 'application/xhtml+xml' => 'xhtml', 'application/xml' => 'xml', 'application/xml-dtd' => 'dtd', 'application/xop+xml' => 'xop', 'application/xproc+xml' => 'xpl', 'application/xslt+xml' => 'xslt', 'application/xspf+xml' => 'xspf', 'application/xv+xml' => 'mxml', 'application/yang' => 'yang', 'application/yin+xml' => 'yin', 'application/zip' => 'zip', 'audio/adpcm' => 'adp', 'audio/basic' => 'au', 'audio/midi' => 'mid', 'audio/mp4' => 'mp4a', 'audio/mpeg' => 'mpga', 'audio/ogg' => 'oga', 'audio/s3m' => 's3m', 'audio/silk' => 'sil', 'audio/vnd.dece.audio' => 'uva', 'audio/vnd.digital-winds' => 'eol', 'audio/vnd.dra' => 'dra', 'audio/vnd.dts' => 'dts', 'audio/vnd.dts.hd' => 'dtshd', 'audio/vnd.lucent.voice' => 'lvp', 'audio/vnd.ms-playready.media.pya' => 'pya', 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', 'audio/vnd.rip' => 'rip', 'audio/webm' => 'weba', 'audio/x-aac' => 'aac', 'audio/x-aiff' => 'aif', 'audio/x-caf' => 'caf', 'audio/x-flac' => 'flac', 'audio/x-matroska' => 'mka', 'audio/x-mpegurl' => 'm3u', 'audio/x-ms-wax' => 'wax', 'audio/x-ms-wma' => 'wma', 'audio/x-pn-realaudio' => 'ram', 'audio/x-pn-realaudio-plugin' => 'rmp', 'audio/x-wav' => 'wav', 'audio/xm' => 'xm', 'chemical/x-cdx' => 'cdx', 'chemical/x-cif' => 'cif', 'chemical/x-cmdf' => 'cmdf', 'chemical/x-cml' => 'cml', 'chemical/x-csml' => 'csml', 'chemical/x-xyz' => 'xyz', 'image/bmp' => 'bmp', 'image/x-ms-bmp' => 'bmp', 'image/cgm' => 'cgm', 'image/g3fax' => 'g3', 'image/gif' => 'gif', 'image/ief' => 'ief', 'image/jpeg' => 'jpg', 'image/pjpeg' => 'jpeg', 'image/ktx' => 'ktx', 'image/png' => 'png', 'image/prs.btif' => 'btif', 'image/sgi' => 'sgi', 'image/svg+xml' => 'svg', 'image/tiff' => 'tiff', 'image/vnd.adobe.photoshop' => 'psd', 'image/vnd.dece.graphic' => 'uvi', 'image/vnd.dvb.subtitle' => 'sub', 'image/vnd.djvu' => 'djvu', 'image/vnd.dwg' => 'dwg', 'image/vnd.dxf' => 'dxf', 'image/vnd.fastbidsheet' => 'fbs', 'image/vnd.fpx' => 'fpx', 'image/vnd.fst' => 'fst', 'image/vnd.fujixerox.edmics-mmr' => 'mmr', 'image/vnd.fujixerox.edmics-rlc' => 'rlc', 'image/vnd.ms-modi' => 'mdi', 'image/vnd.ms-photo' => 'wdp', 'image/vnd.net-fpx' => 'npx', 'image/vnd.wap.wbmp' => 'wbmp', 'image/vnd.xiff' => 'xif', 'image/webp' => 'webp', 'image/x-3ds' => '3ds', 'image/x-cmu-raster' => 'ras', 'image/x-cmx' => 'cmx', 'image/x-freehand' => 'fh', 'image/x-icon' => 'ico', 'image/x-mrsid-image' => 'sid', 'image/x-pcx' => 'pcx', 'image/x-pict' => 'pic', 'image/x-portable-anymap' => 'pnm', 'image/x-portable-bitmap' => 'pbm', 'image/x-portable-graymap' => 'pgm', 'image/x-portable-pixmap' => 'ppm', 'image/x-rgb' => 'rgb', 'image/x-tga' => 'tga', 'image/x-xbitmap' => 'xbm', 'image/x-xpixmap' => 'xpm', 'image/x-xwindowdump' => 'xwd', 'message/rfc822' => 'eml', 'model/iges' => 'igs', 'model/mesh' => 'msh', 'model/vnd.collada+xml' => 'dae', 'model/vnd.dwf' => 'dwf', 'model/vnd.gdl' => 'gdl', 'model/vnd.gtw' => 'gtw', 'model/vnd.mts' => 'mts', 'model/vnd.vtu' => 'vtu', 'model/vrml' => 'wrl', 'model/x3d+binary' => 'x3db', 'model/x3d+vrml' => 'x3dv', 'model/x3d+xml' => 'x3d', 'text/cache-manifest' => 'appcache', 'text/calendar' => 'ics', 'text/css' => 'css', 'text/csv' => 'csv', 'text/html' => 'html', 'text/n3' => 'n3', 'text/plain' => 'txt', 'text/prs.lines.tag' => 'dsc', 'text/richtext' => 'rtx', 'text/rtf' => 'rtf', 'text/sgml' => 'sgml', 'text/tab-separated-values' => 'tsv', 'text/troff' => 't', 'text/turtle' => 'ttl', 'text/uri-list' => 'uri', 'text/vcard' => 'vcard', 'text/vnd.curl' => 'curl', 'text/vnd.curl.dcurl' => 'dcurl', 'text/vnd.curl.scurl' => 'scurl', 'text/vnd.curl.mcurl' => 'mcurl', 'text/vnd.dvb.subtitle' => 'sub', 'text/vnd.fly' => 'fly', 'text/vnd.fmi.flexstor' => 'flx', 'text/vnd.graphviz' => 'gv', 'text/vnd.in3d.3dml' => '3dml', 'text/vnd.in3d.spot' => 'spot', 'text/vnd.sun.j2me.app-descriptor' => 'jad', 'text/vnd.wap.wml' => 'wml', 'text/vnd.wap.wmlscript' => 'wmls', 'text/vtt' => 'vtt', 'text/x-asm' => 's', 'text/x-c' => 'c', 'text/x-fortran' => 'f', 'text/x-pascal' => 'p', 'text/x-java-source' => 'java', 'text/x-opml' => 'opml', 'text/x-nfo' => 'nfo', 'text/x-setext' => 'etx', 'text/x-sfv' => 'sfv', 'text/x-uuencode' => 'uu', 'text/x-vcalendar' => 'vcs', 'text/x-vcard' => 'vcf', 'video/3gpp' => '3gp', 'video/3gpp2' => '3g2', 'video/h261' => 'h261', 'video/h263' => 'h263', 'video/h264' => 'h264', 'video/jpeg' => 'jpgv', 'video/jpm' => 'jpm', 'video/mj2' => 'mj2', 'video/mp4' => 'mp4', 'video/mpeg' => 'mpeg', 'video/ogg' => 'ogv', 'video/quicktime' => 'qt', 'video/vnd.dece.hd' => 'uvh', 'video/vnd.dece.mobile' => 'uvm', 'video/vnd.dece.pd' => 'uvp', 'video/vnd.dece.sd' => 'uvs', 'video/vnd.dece.video' => 'uvv', 'video/vnd.dvb.file' => 'dvb', 'video/vnd.fvt' => 'fvt', 'video/vnd.mpegurl' => 'mxu', 'video/vnd.ms-playready.media.pyv' => 'pyv', 'video/vnd.uvvu.mp4' => 'uvu', 'video/vnd.vivo' => 'viv', 'video/webm' => 'webm', 'video/x-f4v' => 'f4v', 'video/x-fli' => 'fli', 'video/x-flv' => 'flv', 'video/x-m4v' => 'm4v', 'video/x-matroska' => 'mkv', 'video/x-mng' => 'mng', 'video/x-ms-asf' => 'asf', 'video/x-ms-vob' => 'vob', 'video/x-ms-wm' => 'wm', 'video/x-ms-wmv' => 'wmv', 'video/x-ms-wmx' => 'wmx', 'video/x-ms-wvx' => 'wvx', 'video/x-msvideo' => 'avi', 'video/x-sgi-movie' => 'movie', 'video/x-smv' => 'smv', 'x-conference/x-cooltalk' => 'ice', ]; /** * Reversed from $defaultExtensions property in constructor. */ private $defaultMineTypes = []; public function __construct() { $this->defaultMineTypes = array_flip($this->defaultExtensions); } public function guessExtension(string $mimeType): ?string { return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null; } public function guessMimeType(string $extension): ?string { return isset($this->defaultMineTypes[$extension]) ? $this->defaultMineTypes[$extension] : null; } } src/Optional.php000064400000005140151360554240007636 0ustar00value = $value; } /** * Dynamically access a property on the underlying object. * * @param string $key * @return mixed */ public function __get($key) { if (is_object($this->value)) { return $this->value->{$key} ?? null; } return null; } /** * Dynamically check a property exists on the underlying object. * * @param mixed $name * @return bool */ public function __isset($name) { if (is_object($this->value)) { return isset($this->value->{$name}); } return false; } /** * Dynamically pass a method to the underlying object. * * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { if (static::hasMacro($method)) { return $this->macroCall($method, $parameters); } if (is_object($this->value)) { return $this->value->{$method}(...$parameters); } } /** * Determine if an item exists at an offset. * * @param mixed $key * @return bool */ public function offsetExists($key) { return Arr::accessible($this->value) && Arr::exists($this->value, $key); } /** * Get an item at a given offset. * * @param mixed $key * @return mixed */ public function offsetGet($key) { return Arr::get($this->value, $key); } /** * Set the item at a given offset. * * @param mixed $key * @param mixed $value */ public function offsetSet($key, $value) { if (Arr::accessible($this->value)) { $this->value[$key] = $value; } } /** * Unset the item at a given offset. * * @param string $key */ public function offsetUnset($key) { if (Arr::accessible($this->value)) { unset($this->value[$key]); } } } src/Packer/JsonPacker.php000064400000001062151360554240011314 0ustar00 0) { $this->concurrentChannel = new Channel($concurrent); } } public function add(callable $callable, $key = null) { if (is_null($key)) { $this->callbacks[] = $callable; } else { $this->callbacks[$key] = $callable; } } public function wait(bool $throw = true): array { $result = $throwables = []; $wg = new WaitGroup(); $wg->add(count($this->callbacks)); foreach ($this->callbacks as $key => $callback) { $this->concurrentChannel && $this->concurrentChannel->push(true); Coroutine::create(function () use ($callback, $key, $wg, &$result, &$throwables) { try { $result[$key] = call($callback); } catch (\Throwable $throwable) { $throwables[$key] = $throwable; } finally { $this->concurrentChannel && $this->concurrentChannel->pop(); $wg->done(); } }); } $wg->wait(); if ($throw && ($throwableCount = count($throwables)) > 0) { $message = 'Detecting ' . $throwableCount . ' throwable occurred during parallel execution:' . PHP_EOL . $this->formatThrowables($throwables); $executionException = new ParallelExecutionException($message); $executionException->setResults($result); $executionException->setThrowables($throwables); throw $executionException; } return $result; } public function count(): int { return count($this->callbacks); } public function clear(): void { $this->callbacks = []; } /** * Format throwables into a nice list. * * @param \Throwable[] $throwables */ private function formatThrowables(array $throwables): string { $output = ''; foreach ($throwables as $key => $value) { $output .= \sprintf('(%s) %s: %s' . PHP_EOL . '%s' . PHP_EOL, $key, get_class($value), $value->getMessage(), $value->getTraceAsString()); } return $output; } } src/Pipeline.php000064400000011007151360554240007615 0ustar00container = $container; } /** * Set the object being sent through the pipeline. * @param mixed $passable */ public function send($passable): self { $this->passable = $passable; return $this; } /** * Set the array of pipes. * * @param array|mixed $pipes */ public function through($pipes): self { $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; } /** * Set the method to call on the pipes. */ public function via(string $method): self { $this->method = $method; return $this; } /** * Run the pipeline with a final destination callback. */ public function then(Closure $destination) { $pipeline = array_reduce(array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)); return $pipeline($this->passable); } /** * Get the final piece of the Closure onion. */ protected function prepareDestination(Closure $destination): Closure { return static function ($passable) use ($destination) { return $destination($passable); }; } /** * Get a Closure that represents a slice of the application onion. */ protected function carry(): Closure { return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { if (is_callable($pipe)) { // If the pipe is an instance of a Closure, we will just call it directly but // otherwise we'll resolve the pipes out of the container and call it with // the appropriate method and arguments, returning the results back out. return $pipe($passable, $stack); } if (! is_object($pipe)) { [$name, $parameters] = $this->parsePipeString($pipe); // If the pipe is a string we will parse the string and resolve the class out // of the dependency injection container. We can then build a callable and // execute the pipe function giving in the parameters that are required. $pipe = $this->container->get($name); $parameters = array_merge([$passable, $stack], $parameters); } else { // If the pipe is already an object we'll just make a callable and pass it to // the pipe as-is. There is no need to do any extra parsing and formatting // since the object we're given was already a fully instantiated object. $parameters = [$passable, $stack]; } $carry = method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters); return $this->handleCarry($carry); }; }; } /** * Parse full pipe string to get name and parameters. * * @param string $pipe * @return array */ protected function parsePipeString($pipe) { [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []); if (is_string($parameters)) { $parameters = explode(',', $parameters); } return [$name, $parameters]; } /** * Handle the value returned from each pipe before passing it to the next. * * @param mixed $carry * @return mixed */ protected function handleCarry($carry) { return $carry; } } src/Pluralizer.php000064400000007246151360554240010213 0ustar00pluralize($value); return static::matchCase($plural, $value); } /** * Get the singular form of an English word. * * @param string $value * @return string */ public static function singular($value) { $singular = static::getInflector()->singularize($value); return static::matchCase($singular, $value); } public static function setInflector(?Inflector $inflector): void { static::$inflector = $inflector; } /** * Get the inflector instance. */ public static function getInflector(): Inflector { if (is_null(static::$inflector)) { static::$inflector = new Inflector( new CachedWordInflector(new RulesetInflector( English\Rules::getSingularRuleset() )), new CachedWordInflector(new RulesetInflector( English\Rules::getPluralRuleset() )) ); } return static::$inflector; } /** * Determine if the given value is uncountable. * * @param string $value * @return bool */ protected static function uncountable($value) { return in_array(strtolower($value), static::$uncountable); } /** * Attempt to match the case on two strings. * * @param string $value * @param string $comparison * @return string */ protected static function matchCase($value, $comparison) { $functions = ['mb_strtolower', 'mb_strtoupper', 'ucfirst', 'ucwords']; foreach ($functions as $function) { if (call_user_func($function, $comparison) === $comparison) { return call_user_func($function, $value); } } return $value; } } src/Reflection/ClassInvoker.php000064400000002006151360554240012544 0ustar00instance = $instance; $this->reflection = new ReflectionClass($instance); } public function __get($name) { $property = $this->reflection->getProperty($name); $property->setAccessible(true); return $property->getValue($this->instance); } public function __call($name, $arguments) { $method = $this->reflection->getMethod($name); $method->setAccessible(true); return $method->invokeArgs($this->instance, $arguments); } } src/Resource.php000064400000001501151360554240007635 0ustar00getInstantiator()->instantiate($class); foreach (['code', 'message', 'file', 'line'] as $attribute) { if (isset($data[$attribute])) { $property = ReflectionManager::reflectProperty($class, $attribute); $property->setAccessible(true); $property->setValue($exception, $data[$attribute]); } } return $exception; } catch (\ReflectionException $e) { return new \RuntimeException(sprintf( 'Bad data %s: %s', $data['class'], $data['message'] ), $data['code']); } catch (\TypeError $e) { return new \RuntimeException(sprintf( 'Uncaught data %s: %s', $data['class'], $data['message'] ), $data['code']); } } return new \RuntimeException('Bad data data: ' . json_encode($data)); } public function supportsDenormalization($data, $type, $format = null) { return class_exists($type) && is_a($type, \Throwable::class, true); } public function normalize($object, string $format = null, array $context = []) { if ($object instanceof \Serializable) { return serialize($object); } /* @var \Throwable $object */ return [ 'message' => $object->getMessage(), 'code' => $object->getCode(), 'file' => $object->getFile(), 'line' => $object->getLine(), ]; } public function supportsNormalization($data, string $format = null) { return $data instanceof \Throwable; } public function hasCacheableSupportsMethod(): bool { return \get_class($this) === __CLASS__; } protected function getInstantiator(): Instantiator { if ($this->instantiator instanceof Instantiator) { return $this->instantiator; } return $this->instantiator = new Instantiator(); } } src/Serializer/ScalarNormalizer.php000064400000003457151360554240013443 0ustar00serialize($obj, 'xml') * $serializer->decode($data, 'xml') * $serializer->denormalize($data, 'Class', 'xml') */ class Serializer implements Normalizer, SerializerInterface, ContextAwareNormalizerInterface, ContextAwareDenormalizerInterface, ContextAwareEncoderInterface, ContextAwareDecoderInterface { private const SCALAR_TYPES = [ 'int' => true, 'bool' => true, 'float' => true, 'string' => true, ]; /** * @var Encoder\ChainEncoder */ protected $encoder; /** * @var Encoder\ChainDecoder */ protected $decoder; private $normalizers = []; private $denormalizerCache = []; private $normalizerCache = []; /** * @param (NormalizerInterface|DenormalizerInterface|mixed)[] $normalizers * @param (EncoderInterface|DecoderInterface|mixed)[] $encoders */ public function __construct(array $normalizers = [], array $encoders = []) { foreach ($normalizers as $normalizer) { if ($normalizer instanceof SerializerAwareInterface) { $normalizer->setSerializer($this); } if ($normalizer instanceof DenormalizerAwareInterface) { $normalizer->setDenormalizer($this); } if ($normalizer instanceof NormalizerAwareInterface) { $normalizer->setNormalizer($this); } if (! ($normalizer instanceof NormalizerInterface || $normalizer instanceof DenormalizerInterface)) { throw new InvalidArgumentException(sprintf('The class "%s" neither implements "%s" nor "%s".', get_debug_type($normalizer), NormalizerInterface::class, DenormalizerInterface::class)); } } $this->normalizers = $normalizers; $decoders = []; $realEncoders = []; foreach ($encoders as $encoder) { if ($encoder instanceof SerializerAwareInterface) { $encoder->setSerializer($this); } if ($encoder instanceof DecoderInterface) { $decoders[] = $encoder; } if ($encoder instanceof EncoderInterface) { $realEncoders[] = $encoder; } if (! ($encoder instanceof EncoderInterface || $encoder instanceof DecoderInterface)) { throw new InvalidArgumentException(sprintf('The class "%s" neither implements "%s" nor "%s".', get_debug_type($encoder), EncoderInterface::class, DecoderInterface::class)); } } $this->encoder = new ChainEncoder($realEncoders); $this->decoder = new ChainDecoder($decoders); } final public function serialize($data, string $format, array $context = []): string { if (! $this->supportsEncoding($format, $context)) { throw new NotEncodableValueException(sprintf('Serialization for the format "%s" is not supported.', $format)); } if ($this->encoder->needsNormalization($format, $context)) { $data = $this->normalize($data, $format, $context); } return $this->encode($data, $format, $context); } final public function deserialize($data, string $type, string $format, array $context = []) { if (! $this->supportsDecoding($format, $context)) { throw new NotEncodableValueException(sprintf('Deserialization for the format "%s" is not supported.', $format)); } $data = $this->decode($data, $format, $context); return $this->denormalize($data, $type, $format, $context); } public function normalize($data, string $format = null, array $context = []) { // If a normalizer supports the given data, use it if ($normalizer = $this->getNormalizer($data, $format, $context)) { return $normalizer->normalize($data, $format, $context); } if ($data === null || is_scalar($data)) { return $data; } if (\is_array($data) || $data instanceof \Traversable) { if ($data instanceof \Countable && $data->count() === 0) { return $data; } $normalized = []; foreach ($data as $key => $val) { $normalized[$key] = $this->normalize($val, $format, $context); } return $normalized; } if (\is_object($data)) { if (! $this->normalizers) { throw new LogicException('You must register at least one normalizer to be able to normalize objects.'); } throw new NotNormalizableValueException(sprintf('Could not normalize object of type "%s", no supporting normalizer found.', get_debug_type($data))); } throw new NotNormalizableValueException('An unexpected value could not be normalized: ' . (! \is_resource($data) ? var_export($data, true) : sprintf('%s resource', get_resource_type($data)))); } /** * @param mixed $data * @throws NotNormalizableValueException */ public function denormalize($data, string $type, string $format = null, array $context = []) { if (isset(self::SCALAR_TYPES[$type])) { if (is_scalar($data)) { switch ($type) { case 'int': return (int) $data; case 'bool': return (bool) $data; case 'float': return (float) $data; case 'string': return (string) $data; } } } if (! $this->normalizers) { throw new LogicException('You must register at least one normalizer to be able to denormalize objects.'); } if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) { return $normalizer->denormalize($data, $type, $format, $context); } throw new NotNormalizableValueException(sprintf('Could not denormalize object of type "%s", no supporting normalizer found.', $type)); } public function supportsNormalization($data, string $format = null, array $context = []) { return $this->getNormalizer($data, $format, $context) !== null; } public function supportsDenormalization($data, string $type, string $format = null, array $context = []) { return isset(self::SCALAR_TYPES[$type]) || $this->getDenormalizer($data, $type, $format, $context) !== null; } final public function encode($data, string $format, array $context = []) { return $this->encoder->encode($data, $format, $context); } final public function decode(string $data, string $format, array $context = []) { return $this->decoder->decode($data, $format, $context); } public function supportsEncoding(string $format, array $context = []) { return $this->encoder->supportsEncoding($format, $context); } public function supportsDecoding(string $format, array $context = []) { return $this->decoder->supportsDecoding($format, $context); } /** * Returns a matching normalizer. * * @param mixed $data Data to get the serializer for * @param string $format Format name, present to give the option to normalizers to act differently based on formats * @param array $context Options available to the normalizer */ private function getNormalizer($data, ?string $format, array $context): ?NormalizerInterface { $type = \is_object($data) ? \get_class($data) : 'native-' . \gettype($data); if (! isset($this->normalizerCache[$format][$type])) { $this->normalizerCache[$format][$type] = []; foreach ($this->normalizers as $k => $normalizer) { if (! $normalizer instanceof NormalizerInterface) { continue; } if (! $normalizer instanceof CacheableSupportsMethodInterface || ! $normalizer->hasCacheableSupportsMethod()) { $this->normalizerCache[$format][$type][$k] = false; } elseif ($normalizer->supportsNormalization($data, $format)) { $this->normalizerCache[$format][$type][$k] = true; break; } } } foreach ($this->normalizerCache[$format][$type] as $k => $cached) { $normalizer = $this->normalizers[$k]; if ($cached || $normalizer->supportsNormalization($data, $format, $context)) { return $normalizer; } } return null; } /** * Returns a matching denormalizer. * * @param mixed $data Data to restore * @param string $class The expected class to instantiate * @param string $format Format name, present to give the option to normalizers to act differently based on formats * @param array $context Options available to the denormalizer */ private function getDenormalizer($data, string $class, ?string $format, array $context): ?DenormalizerInterface { if (! isset($this->denormalizerCache[$format][$class])) { $this->denormalizerCache[$format][$class] = []; foreach ($this->normalizers as $k => $normalizer) { if (! $normalizer instanceof DenormalizerInterface) { continue; } if (! $normalizer instanceof CacheableSupportsMethodInterface || ! $normalizer->hasCacheableSupportsMethod()) { $this->denormalizerCache[$format][$class][$k] = false; } elseif ($normalizer->supportsDenormalization(null, $class, $format)) { $this->denormalizerCache[$format][$class][$k] = true; break; } } } foreach ($this->denormalizerCache[$format][$class] as $k => $cached) { $normalizer = $this->normalizers[$k]; if ($cached || $normalizer->supportsDenormalization($data, $class, $format, $context)) { return $normalizer; } } return null; } } src/Serializer/SerializerFactory.php000064400000001610151360554240013621 0ustar00serializer = $serializer; } public function __invoke() { return new $this->serializer([ new ExceptionNormalizer(), new ObjectNormalizer(), new ArrayDenormalizer(), new ScalarNormalizer(), ]); } } src/Serializer/SimpleNormalizer.php000064400000001627151360554240013464 0ustar00serializer = $serializer; } public function normalize($object) { return $this->serializer->normalize($object); } public function denormalize($data, string $class) { return $this->serializer->denormalize($data, $class); } } src/Str.php000064400000070070151360554240006625 0ustar00 $val) { $value = str_replace($val, $key, $value); } return preg_replace('/[^\x20-\x7E]/u', '', $value); } /** * Get the portion of a string before a given value. * * @param string $subject * @param string $search * @return string */ public static function before($subject, $search) { return $search === '' ? $subject : explode($search, $subject)[0]; } /** * Convert a value to camel case. * * @param string $value * @return string */ public static function camel($value) { if (isset(static::$camelCache[$value])) { return static::$camelCache[$value]; } return static::$camelCache[$value] = lcfirst(static::studly($value)); } /** * Determine if a given string contains a given substring. * * @param string $haystack * @param array|string $needles * @return bool */ public static function contains($haystack, $needles) { foreach ((array) $needles as $needle) { if ($needle !== '' && mb_strpos($haystack, $needle) !== false) { return true; } } return false; } /** * Determine if a given string ends with a given substring. * * @param string $haystack * @param array|string $needles * @return bool */ public static function endsWith($haystack, $needles) { foreach ((array) $needles as $needle) { if (substr($haystack, -strlen($needle)) === (string) $needle) { return true; } } return false; } /** * Cap a string with a single instance of a given value. * * @param string $value * @param string $cap * @return string */ public static function finish($value, $cap) { $quoted = preg_quote($cap, '/'); return preg_replace('/(?:' . $quoted . ')+$/u', '', $value) . $cap; } /** * Determine if a given string matches a given pattern. * * @param array|string $pattern * @param string $value * @return bool */ public static function is($pattern, $value) { $patterns = Arr::wrap($pattern); if (empty($patterns)) { return false; } foreach ($patterns as $pattern) { // If the given value is an exact match we can of course return true right // from the beginning. Otherwise, we will translate asterisks and do an // actual pattern match against the two strings to see if they match. if ($pattern == $value) { return true; } $pattern = preg_quote($pattern, '#'); // Asterisks are translated into zero-or-more regular expression wildcards // to make it convenient to check if the strings starts with the given // pattern such as "library/*", making any string check convenient. $pattern = str_replace('\*', '.*', $pattern); if (preg_match('#^' . $pattern . '\z#u', $value) === 1) { return true; } } return false; } /** * Convert a string to kebab case. * * @param string $value * @return string */ public static function kebab($value) { return static::snake($value, '-'); } /** * Return the length of the given string. * * @param string $value * @param string $encoding * @return int */ public static function length($value, $encoding = null) { if ($encoding) { return mb_strlen($value, $encoding); } return mb_strlen($value); } /** * Limit the number of characters in a string. * * @param string $value * @param int $limit * @param string $end * @return string */ public static function limit($value, $limit = 100, $end = '...') { if (mb_strwidth($value, 'UTF-8') <= $limit) { return $value; } return rtrim(mb_strimwidth($value, 0, $limit, '', 'UTF-8')) . $end; } /** * Convert the given string to lower-case. * * @param string $value * @return string */ public static function lower($value) { return mb_strtolower($value, 'UTF-8'); } /** * Limit the number of words in a string. */ public static function words(string $value, int $words = 100, string $end = '...'): string { preg_match('/^\s*+(?:\S++\s*+){1,' . $words . '}/u', $value, $matches); if (! isset($matches[0]) || static::length($value) === static::length($matches[0])) { return $value; } return rtrim($matches[0]) . $end; } /** * Parse a Class@method style callback into class and method. * * @param null|string $default */ public static function parseCallback(string $callback, $default = null): array { return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default]; } /** * Pluralize the last word of an English, studly caps case string. */ public static function pluralStudly(string $value, int $count = 2): string { $parts = preg_split('/(.)(?=[A-Z])/u', $value, -1, PREG_SPLIT_DELIM_CAPTURE); $lastWord = array_pop($parts); return implode('', $parts) . self::plural($lastWord, $count); } /** * Get the plural form of an English word. */ public static function plural(string $value, int $count = 2): string { return Pluralizer::plural($value, $count); } /** * Generate a more truly "random" alpha-numeric string. */ public static function random(int $length = 16): string { $string = ''; while (($len = strlen($string)) < $length) { $size = $length - $len; $bytes = random_bytes($size); $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); } return $string; } /** * Replace a given value in the string sequentially with an array. */ public static function replaceArray(string $search, array $replace, string $subject): string { foreach ($replace as $value) { $subject = static::replaceFirst($search, (string) $value, $subject); } return $subject; } /** * Replace the first occurrence of a given value in the string. */ public static function replaceFirst(string $search, string $replace, string $subject): string { if ($search == '') { return $subject; } $position = strpos($subject, $search); if ($position !== false) { return substr_replace($subject, $replace, $position, strlen($search)); } return $subject; } /** * Replace the last occurrence of a given value in the string. */ public static function replaceLast(string $search, string $replace, string $subject): string { $position = strrpos($subject, $search); if ($position !== false) { return substr_replace($subject, $replace, $position, strlen($search)); } return $subject; } /** * Begin a string with a single instance of a given value. */ public static function start(string $value, string $prefix): string { $quoted = preg_quote($prefix, '/'); return $prefix . preg_replace('/^(?:' . $quoted . ')+/u', '', $value); } /** * Convert the given string to upper-case. */ public static function upper(string $value): string { return mb_strtoupper($value, 'UTF-8'); } /** * Convert the given string to title case. */ public static function title(string $value): string { return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8'); } /** * Get the singular form of an English word. */ public static function singular(string $value): string { return Pluralizer::singular($value); } /** * Generate a URL friendly "slug" from a given string. */ public static function slug(string $title, string $separator = '-', string $language = 'en'): string { $title = $language ? static::ascii($title, $language) : $title; // Convert all dashes/underscores into separator $flip = $separator === '-' ? '_' : '-'; $title = preg_replace('![' . preg_quote($flip) . ']+!u', $separator, $title); // Replace @ with the word 'at' $title = str_replace('@', $separator . 'at' . $separator, $title); // Remove all characters that are not the separator, letters, numbers, or whitespace. $title = preg_replace('![^' . preg_quote($separator) . '\pL\pN\s]+!u', '', mb_strtolower($title)); // Replace all separator characters and whitespace by a single separator $title = preg_replace('![' . preg_quote($separator) . '\s]+!u', $separator, $title); return trim($title, $separator); } /** * Convert a string to snake case. */ public static function snake(string $value, string $delimiter = '_'): string { $key = $value; if (isset(static::$snakeCache[$key][$delimiter])) { return static::$snakeCache[$key][$delimiter]; } if (! ctype_lower($value)) { $value = preg_replace('/\s+/u', '', ucwords($value)); $value = static::lower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value)); } return static::$snakeCache[$key][$delimiter] = $value; } /** * Determine if a given string starts with a given substring. * * @param array|string $needles */ public static function startsWith(string $haystack, $needles): bool { foreach ((array) $needles as $needle) { if ($needle !== '' && substr($haystack, 0, strlen($needle)) === (string) $needle) { return true; } } return false; } /** * Convert a value to studly caps case. */ public static function studly(string $value, string $gap = ''): string { $key = $value; if (isset(static::$studlyCache[$key])) { return static::$studlyCache[$key]; } $value = ucwords(str_replace(['-', '_'], ' ', $value)); return static::$studlyCache[$key] = str_replace(' ', $gap, $value); } /** * Returns the portion of string specified by the start and length parameters. * * @param string $string * @param int $start * @param null|int $length * @return string */ public static function substr($string, $start, $length = null) { return mb_substr($string, $start, $length, 'UTF-8'); } /** * Make a string's first character uppercase. */ public static function ucfirst(string $string): string { return static::upper(static::substr($string, 0, 1)) . static::substr($string, 1); } /** * Returns the replacements for the ascii method. * Note: Adapted from Stringy\Stringy. * * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt */ protected static function charsArray(): array { static $charsArray; if (isset($charsArray)) { return $charsArray; } return $charsArray = [ '0' => ['°', '₀', '۰', '0'], '1' => ['¹', '₁', '۱', '1'], '2' => ['²', '₂', '۲', '2'], '3' => ['³', '₃', '۳', '3'], '4' => ['⁴', '₄', '۴', '٤', '4'], '5' => ['⁵', '₅', '۵', '٥', '5'], '6' => ['⁶', '₆', '۶', '٦', '6'], '7' => ['⁷', '₇', '۷', '7'], '8' => ['⁸', '₈', '۸', '8'], '9' => ['⁹', '₉', '۹', '9'], 'a' => [ 'à', 'á', 'ả', 'ã', 'ạ', 'ă', 'ắ', 'ằ', 'ẳ', 'ẵ', 'ặ', 'â', 'ấ', 'ầ', 'ẩ', 'ẫ', 'ậ', 'ā', 'ą', 'å', 'α', 'ά', 'ἀ', 'ἁ', 'ἂ', 'ἃ', 'ἄ', 'ἅ', 'ἆ', 'ἇ', 'ᾀ', 'ᾁ', 'ᾂ', 'ᾃ', 'ᾄ', 'ᾅ', 'ᾆ', 'ᾇ', 'ὰ', 'ά', 'ᾰ', 'ᾱ', 'ᾲ', 'ᾳ', 'ᾴ', 'ᾶ', 'ᾷ', 'а', 'أ', 'အ', 'ာ', 'ါ', 'ǻ', 'ǎ', 'ª', 'ა', 'अ', 'ا', 'a', 'ä', ], 'b' => ['б', 'β', 'ب', 'ဗ', 'ბ', 'b'], 'c' => ['ç', 'ć', 'č', 'ĉ', 'ċ', 'c'], 'd' => ['ď', 'ð', 'đ', 'ƌ', 'ȡ', 'ɖ', 'ɗ', 'ᵭ', 'ᶁ', 'ᶑ', 'д', 'δ', 'د', 'ض', 'ဍ', 'ဒ', 'დ', 'd'], 'e' => [ 'é', 'è', 'ẻ', 'ẽ', 'ẹ', 'ê', 'ế', 'ề', 'ể', 'ễ', 'ệ', 'ë', 'ē', 'ę', 'ě', 'ĕ', 'ė', 'ε', 'έ', 'ἐ', 'ἑ', 'ἒ', 'ἓ', 'ἔ', 'ἕ', 'ὲ', 'έ', 'е', 'ё', 'э', 'є', 'ə', 'ဧ', 'ေ', 'ဲ', 'ე', 'ए', 'إ', 'ئ', 'e', ], 'f' => ['ф', 'φ', 'ف', 'ƒ', 'ფ', 'f'], 'g' => ['ĝ', 'ğ', 'ġ', 'ģ', 'г', 'ґ', 'γ', 'ဂ', 'გ', 'گ', 'g'], 'h' => ['ĥ', 'ħ', 'η', 'ή', 'ح', 'ه', 'ဟ', 'ှ', 'ჰ', 'h'], 'i' => [ 'í', 'ì', 'ỉ', 'ĩ', 'ị', 'î', 'ï', 'ī', 'ĭ', 'į', 'ı', 'ι', 'ί', 'ϊ', 'ΐ', 'ἰ', 'ἱ', 'ἲ', 'ἳ', 'ἴ', 'ἵ', 'ἶ', 'ἷ', 'ὶ', 'ί', 'ῐ', 'ῑ', 'ῒ', 'ΐ', 'ῖ', 'ῗ', 'і', 'ї', 'и', 'ဣ', 'ိ', 'ီ', 'ည်', 'ǐ', 'ი', 'इ', 'ی', 'i', ], 'j' => ['ĵ', 'ј', 'Ј', 'ჯ', 'ج', 'j'], 'k' => ['ķ', 'ĸ', 'к', 'κ', 'Ķ', 'ق', 'ك', 'က', 'კ', 'ქ', 'ک', 'k'], 'l' => ['ł', 'ľ', 'ĺ', 'ļ', 'ŀ', 'л', 'λ', 'ل', 'လ', 'ლ', 'l'], 'm' => ['м', 'μ', 'م', 'မ', 'მ', 'm'], 'n' => ['ñ', 'ń', 'ň', 'ņ', 'ʼn', 'ŋ', 'ν', 'н', 'ن', 'န', 'ნ', 'n'], 'o' => [ 'ó', 'ò', 'ỏ', 'õ', 'ọ', 'ô', 'ố', 'ồ', 'ổ', 'ỗ', 'ộ', 'ơ', 'ớ', 'ờ', 'ở', 'ỡ', 'ợ', 'ø', 'ō', 'ő', 'ŏ', 'ο', 'ὀ', 'ὁ', 'ὂ', 'ὃ', 'ὄ', 'ὅ', 'ὸ', 'ό', 'о', 'و', 'θ', 'ို', 'ǒ', 'ǿ', 'º', 'ო', 'ओ', 'o', 'ö', ], 'p' => ['п', 'π', 'ပ', 'პ', 'پ', 'p'], 'q' => ['ყ', 'q'], 'r' => ['ŕ', 'ř', 'ŗ', 'р', 'ρ', 'ر', 'რ', 'r'], 's' => ['ś', 'š', 'ş', 'с', 'σ', 'ș', 'ς', 'س', 'ص', 'စ', 'ſ', 'ს', 's'], 't' => ['ť', 'ţ', 'т', 'τ', 'ț', 'ت', 'ط', 'ဋ', 'တ', 'ŧ', 'თ', 'ტ', 't'], 'u' => [ 'ú', 'ù', 'ủ', 'ũ', 'ụ', 'ư', 'ứ', 'ừ', 'ử', 'ữ', 'ự', 'û', 'ū', 'ů', 'ű', 'ŭ', 'ų', 'µ', 'у', 'ဉ', 'ု', 'ူ', 'ǔ', 'ǖ', 'ǘ', 'ǚ', 'ǜ', 'უ', 'उ', 'u', 'ў', 'ü', ], 'v' => ['в', 'ვ', 'ϐ', 'v'], 'w' => ['ŵ', 'ω', 'ώ', 'ဝ', 'ွ', 'w'], 'x' => ['χ', 'ξ', 'x'], 'y' => ['ý', 'ỳ', 'ỷ', 'ỹ', 'ỵ', 'ÿ', 'ŷ', 'й', 'ы', 'υ', 'ϋ', 'ύ', 'ΰ', 'ي', 'ယ', 'y'], 'z' => ['ź', 'ž', 'ż', 'з', 'ζ', 'ز', 'ဇ', 'ზ', 'z'], 'aa' => ['ع', 'आ', 'آ'], 'ae' => ['æ', 'ǽ'], 'ai' => ['ऐ'], 'ch' => ['ч', 'ჩ', 'ჭ', 'چ'], 'dj' => ['ђ', 'đ'], 'dz' => ['џ', 'ძ'], 'ei' => ['ऍ'], 'gh' => ['غ', 'ღ'], 'ii' => ['ई'], 'ij' => ['ij'], 'kh' => ['х', 'خ', 'ხ'], 'lj' => ['љ'], 'nj' => ['њ'], 'oe' => ['ö', 'œ', 'ؤ'], 'oi' => ['ऑ'], 'oii' => ['ऒ'], 'ps' => ['ψ'], 'sh' => ['ш', 'შ', 'ش'], 'shch' => ['щ'], 'ss' => ['ß'], 'sx' => ['ŝ'], 'th' => ['þ', 'ϑ', 'ث', 'ذ', 'ظ'], 'ts' => ['ц', 'ც', 'წ'], 'ue' => ['ü'], 'uu' => ['ऊ'], 'ya' => ['я'], 'yu' => ['ю'], 'zh' => ['ж', 'ჟ', 'ژ'], '(c)' => ['©'], 'A' => [ 'Á', 'À', 'Ả', 'Ã', 'Ạ', 'Ă', 'Ắ', 'Ằ', 'Ẳ', 'Ẵ', 'Ặ', 'Â', 'Ấ', 'Ầ', 'Ẩ', 'Ẫ', 'Ậ', 'Å', 'Ā', 'Ą', 'Α', 'Ά', 'Ἀ', 'Ἁ', 'Ἂ', 'Ἃ', 'Ἄ', 'Ἅ', 'Ἆ', 'Ἇ', 'ᾈ', 'ᾉ', 'ᾊ', 'ᾋ', 'ᾌ', 'ᾍ', 'ᾎ', 'ᾏ', 'Ᾰ', 'Ᾱ', 'Ὰ', 'Ά', 'ᾼ', 'А', 'Ǻ', 'Ǎ', 'A', 'Ä', ], 'B' => ['Б', 'Β', 'ब', 'B'], 'C' => ['Ç', 'Ć', 'Č', 'Ĉ', 'Ċ', 'C'], 'D' => ['Ď', 'Ð', 'Đ', 'Ɖ', 'Ɗ', 'Ƌ', 'ᴅ', 'ᴆ', 'Д', 'Δ', 'D'], 'E' => [ 'É', 'È', 'Ẻ', 'Ẽ', 'Ẹ', 'Ê', 'Ế', 'Ề', 'Ể', 'Ễ', 'Ệ', 'Ë', 'Ē', 'Ę', 'Ě', 'Ĕ', 'Ė', 'Ε', 'Έ', 'Ἐ', 'Ἑ', 'Ἒ', 'Ἓ', 'Ἔ', 'Ἕ', 'Έ', 'Ὲ', 'Е', 'Ё', 'Э', 'Є', 'Ə', 'E', ], 'F' => ['Ф', 'Φ', 'F'], 'G' => ['Ğ', 'Ġ', 'Ģ', 'Г', 'Ґ', 'Γ', 'G'], 'H' => ['Η', 'Ή', 'Ħ', 'H'], 'I' => [ 'Í', 'Ì', 'Ỉ', 'Ĩ', 'Ị', 'Î', 'Ï', 'Ī', 'Ĭ', 'Į', 'İ', 'Ι', 'Ί', 'Ϊ', 'Ἰ', 'Ἱ', 'Ἳ', 'Ἴ', 'Ἵ', 'Ἶ', 'Ἷ', 'Ῐ', 'Ῑ', 'Ὶ', 'Ί', 'И', 'І', 'Ї', 'Ǐ', 'ϒ', 'I', ], 'J' => ['J'], 'K' => ['К', 'Κ', 'K'], 'L' => ['Ĺ', 'Ł', 'Л', 'Λ', 'Ļ', 'Ľ', 'Ŀ', 'ल', 'L'], 'M' => ['М', 'Μ', 'M'], 'N' => ['Ń', 'Ñ', 'Ň', 'Ņ', 'Ŋ', 'Н', 'Ν', 'N'], 'O' => [ 'Ó', 'Ò', 'Ỏ', 'Õ', 'Ọ', 'Ô', 'Ố', 'Ồ', 'Ổ', 'Ỗ', 'Ộ', 'Ơ', 'Ớ', 'Ờ', 'Ở', 'Ỡ', 'Ợ', 'Ø', 'Ō', 'Ő', 'Ŏ', 'Ο', 'Ό', 'Ὀ', 'Ὁ', 'Ὂ', 'Ὃ', 'Ὄ', 'Ὅ', 'Ὸ', 'Ό', 'О', 'Θ', 'Ө', 'Ǒ', 'Ǿ', 'O', 'Ö', ], 'P' => ['П', 'Π', 'P'], 'Q' => ['Q'], 'R' => ['Ř', 'Ŕ', 'Р', 'Ρ', 'Ŗ', 'R'], 'S' => ['Ş', 'Ŝ', 'Ș', 'Š', 'Ś', 'С', 'Σ', 'S'], 'T' => ['Ť', 'Ţ', 'Ŧ', 'Ț', 'Т', 'Τ', 'T'], 'U' => [ 'Ú', 'Ù', 'Ủ', 'Ũ', 'Ụ', 'Ư', 'Ứ', 'Ừ', 'Ử', 'Ữ', 'Ự', 'Û', 'Ū', 'Ů', 'Ű', 'Ŭ', 'Ų', 'У', 'Ǔ', 'Ǖ', 'Ǘ', 'Ǚ', 'Ǜ', 'U', 'Ў', 'Ü', ], 'V' => ['В', 'V'], 'W' => ['Ω', 'Ώ', 'Ŵ', 'W'], 'X' => ['Χ', 'Ξ', 'X'], 'Y' => ['Ý', 'Ỳ', 'Ỷ', 'Ỹ', 'Ỵ', 'Ÿ', 'Ῠ', 'Ῡ', 'Ὺ', 'Ύ', 'Ы', 'Й', 'Υ', 'Ϋ', 'Ŷ', 'Y'], 'Z' => ['Ź', 'Ž', 'Ż', 'З', 'Ζ', 'Z'], 'AE' => ['Æ', 'Ǽ'], 'Ch' => ['Ч'], 'Dj' => ['Ђ'], 'Dz' => ['Џ'], 'Gx' => ['Ĝ'], 'Hx' => ['Ĥ'], 'Ij' => ['IJ'], 'Jx' => ['Ĵ'], 'Kh' => ['Х'], 'Lj' => ['Љ'], 'Nj' => ['Њ'], 'Oe' => ['Œ'], 'Ps' => ['Ψ'], 'Sh' => ['Ш'], 'Shch' => ['Щ'], 'Ss' => ['ẞ'], 'Th' => ['Þ'], 'Ts' => ['Ц'], 'Ya' => ['Я'], 'Yu' => ['Ю'], 'Zh' => ['Ж'], ' ' => [ "\xC2\xA0", "\xE2\x80\x80", "\xE2\x80\x81", "\xE2\x80\x82", "\xE2\x80\x83", "\xE2\x80\x84", "\xE2\x80\x85", "\xE2\x80\x86", "\xE2\x80\x87", "\xE2\x80\x88", "\xE2\x80\x89", "\xE2\x80\x8A", "\xE2\x80\xAF", "\xE2\x81\x9F", "\xE3\x80\x80", "\xEF\xBE\xA0", ], ]; } /** * Returns the language specific replacements for the ascii method. * Note: Adapted from Stringy\Stringy. * * @see https://github.com/danielstjules/Stringy/blob/3.1.0/LICENSE.txt * @return null|array */ protected static function languageSpecificCharsArray(string $language) { static $languageSpecific; if (! isset($languageSpecific)) { $languageSpecific = [ 'bg' => [ ['х', 'Х', 'щ', 'Щ', 'ъ', 'Ъ', 'ь', 'Ь'], ['h', 'H', 'sht', 'SHT', 'a', 'А', 'y', 'Y'], ], 'de' => [ ['ä', 'ö', 'ü', 'Ä', 'Ö', 'Ü'], ['ae', 'oe', 'ue', 'AE', 'OE', 'UE'], ], ]; } return $languageSpecific[$language] ?? null; } } src/Traits/Container.php000064400000002537151360554240011250 0ustar00getTargetObject(); return $target->{$name}(...$arguments); } public function __get($name) { $target = $this->getTargetObject(); return $target->{$name}; } public function __set($name, $value) { $target = $this->getTargetObject(); return $target->{$name} = $value; } protected function getTargetObject() { if (! isset($this->proxyKey)) { throw new \RuntimeException('$proxyKey property of class missing.'); } return Context::get($this->proxyKey); } } src/Traits/ForwardsCalls.php000064400000002625151360554240012072 0ustar00{$method}(...$parameters); } catch (Error | BadMethodCallException $e) { $pattern = '~^Call to undefined method (?P[^:]+)::(?P[^\(]+)\(\)$~'; if (! preg_match($pattern, $e->getMessage(), $matches)) { throw $e; } if ($matches['class'] !== get_class($object) || $matches['method'] !== $method) { throw $e; } static::throwBadMethodCallException($method); } } /** * Throw a bad method call exception for the given method. * @throws BadMethodCallException */ protected static function throwBadMethodCallException(string $method): void { throw new BadMethodCallException(sprintf('Call to undefined method %s::%s()', static::class, $method)); } } src/Traits/Macroable.php000064400000005634151360554240011214 0ustar00bindTo($this, static::class), $parameters); } return call_user_func_array($macro, $parameters); } /** * Register a custom macro. * * @param string $name * @param callable|object $macro */ public static function macro($name, $macro) { static::$macros[$name] = $macro; } /** * Mix another object into the class. * * @param object $mixin * * @throws \ReflectionException */ public static function mixin($mixin) { $methods = (new ReflectionClass($mixin))->getMethods( ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED ); foreach ($methods as $method) { $method->setAccessible(true); static::macro($method->name, $method->invoke($mixin)); } } /** * Checks if macro is registered. * * @param string $name * @return bool */ public static function hasMacro($name) { return isset(static::$macros[$name]); } } src/Traits/StaticInstance.php000064400000001562151360554240012237 0ustar00popTimeout = $timeout; } /** * @param null|float $timeout seconds */ public function wait(Closure $closure, ?float $timeout = null) { if ($timeout === null) { $timeout = $this->popTimeout; } $channel = new Channel(1); Coroutine::create(function () use ($channel, $closure) { try { $result = $closure(); } catch (Throwable $exception) { $result = new ExceptionThrower($exception); } finally { $channel->push($result ?? null, $this->pushTimeout); } }); $result = $channel->pop($timeout); if ($result === false && $channel->isTimeout()) { throw new WaitTimeoutException(sprintf('Channel wait failed, reason: Timed out for %s s', $timeout)); } if ($result instanceof ExceptionThrower) { throw $result->getThrowable(); } return $result; } }