File manager - Edit - /home/opticamezl/www/newok/utils.tar
Back
src/File.php 0000644 00000023014 15175266533 0006740 0 ustar 00 <?php namespace YOOtheme; /** * A static class which provides utilities for working with the file system. */ abstract class File { /** * Gets an existing file or directory. * * @param string $path * * @return string|null * * @example * File::get('/path/file.php'); * // => '/path/file.php' */ public static function get($path) { $path = Path::resolveAlias($path); return file_exists($path) ? $path : null; } /** * Checks whether file or directory exists. * * @param string $path * * @return bool * * @example * File::exists('/path/resource'); * // => true * * File::exists('/path/with/no/resource'); * // => false */ public static function exists($path) { return !is_null(static::get($path)); } /** * Find file with glob pattern. * * @param string $path * * @return string|null * * @example * File::find('/path/*.php'); * // => '/path/file.php' */ public static function find($path) { return ($files = static::glob($path, GLOB_NOSORT)) ? $files[0] : null; } /** * Glob files with braces support. * * @param string $pattern * @param int $flags * * @return array<string> * * @example * File::glob('/path/{*.ext,*.php}'); * // => ['/path/file.ext', '/path/file.php'] */ public static function glob($pattern, $flags = 0) { $pattern = Path::resolveAlias($pattern); if (defined('GLOB_BRACE') && !str_starts_with($pattern, '{')) { return glob($pattern, $flags | GLOB_BRACE) ?: []; } return static::_glob($pattern, $flags); } /** * Copies file. * * @param string $from * @param string $to * * @return bool * * @example * File::copy('/path/file.ext', '/path/dest/file.ext'); * // => true */ public static function copy($from, $to) { $from = Path::resolveAlias($from); $to = Path::resolve(dirname($from), $to); return copy($from, $to); } /** * Renames a file or directory. * * @param string $from * @param string $to * * @return bool * * @example * File::rename('/path/resource', '/path/renamed'); * // => true */ public static function rename($from, $to) { $from = Path::resolveAlias($from); $to = Path::resolve(dirname($from), $to); return rename($from, $to); } /** * Deletes a file. * * @param string $path * * @return bool * * @example * File::delete('/path/file.ext'); * // => true */ public static function delete($path) { $path = Path::resolveAlias($path); return unlink($path); } /** * List files and directories inside the specified path. * * @param string $path * @param bool|string $prefix * * @return string[]|false * * @example * File::listDir('/path/dir'); * // => ['Dir1', 'Dir2', 'File.txt'] * * File::listDir('/path/dir', true); * // => ['/path/dir/Dir1', '/path/dir/Dir2', '/path/dir/File.txt'] */ public static function listDir($path, $prefix = false) { $path = Path::resolveAlias($path); if (!static::exists($path)) { return false; } if ($prefix === true) { $prefix = $path; } if ($files = scandir($path)) { $files = array_values(array_diff($files, ['.', '..'])); } if ($files && $prefix) { foreach ($files as &$file) { $file = Path::join($prefix, $file); } } return $files; } /** * Makes directory. * * @param string $path * @param int $mode * @param bool $recursive * * @return bool * * @example * File::makeDir('/path/dir/to/make'); * // => true */ public static function makeDir($path, $mode = 0777, $recursive = false) { $path = Path::resolveAlias($path); return is_dir($path) || @mkdir($path, $mode, $recursive) || is_dir($path); } /** * Removes directory recursively. * * @param string $path * * @return bool * * @example * File::deleteDir('/path/dir/to/delete'); * // => true */ public static function deleteDir($path) { $path = Path::resolveAlias($path); $files = static::listDir($path, true); if (is_bool($files)) { return $files; } foreach ($files as $file) { // delete directory recursively if (is_dir($file) && !static::deleteDir($file)) { return false; } // delete file if (is_file($file) && !unlink($file)) { return false; } } return rmdir($path); } /** * Gets the last access time of file. * * @param string $path * * @return int|null * * @example * File::getATime('/path/file.ext'); * // => 1551693515 */ public static function getATime($path) { $path = Path::resolveAlias($path); $time = fileatime($path); return is_int($time) ? $time : null; } /** * Gets the inode change time of file. * * @param string $path * * @return int|null * * @example * File::getCTime('/path/file.ext'); * // => 1551693515 */ public static function getCTime($path) { $path = Path::resolveAlias($path); $time = filectime($path); return is_int($time) ? $time : null; } /** * Gets the last modified time of file. * * @param string $path * * @return int|null * * @example * File::getMTime('/path/file.ext'); * // => 1551693515 */ public static function getMTime($path) { $path = Path::resolveAlias($path); $time = filemtime($path); return is_int($time) ? $time : null; } /** * Gets the file size. * * @param string $path * * @return int|null * * @example * File::getSize('/path/file.ext'); * // => 4 */ public static function getSize($path) { $path = Path::resolveAlias($path); $size = filesize($path); return is_int($size) ? $size : null; } /** * Gets the file mime content type. * * @param string $path * * @return false|string * * @example * File::getMimetype('/path/file.ext'); * // => text/plain */ public static function getMimetype($path) { $path = Path::resolveAlias($path); return function_exists('finfo_file') ? finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path) : mime_content_type($path); } /** * Gets the file extension. * * @param string $path * * @return string * * @example * File::getExtension('/path/file.ext'); * // => ext */ public static function getExtension($path) { $path = Path::resolveAlias($path); return pathinfo($path, PATHINFO_EXTENSION); } /** * Gets the contents from file. * * @param string $path * * @return string|null * * @example * File::getContents('/path/file.ext'); * // => filecontent */ public static function getContents($path) { $path = Path::resolveAlias($path); $data = file_get_contents($path); return is_string($data) ? $data : null; } /** * Writes the contents to file. * * @param string $path * @param mixed $data * @param int $flags * * @return int|null * * @example * File::putContents('/path/file.ext', 'content'); * // => true */ public static function putContents($path, $data, $flags = 0) { $path = Path::resolveAlias($path); $bytes = file_put_contents($path, $data, $flags); return is_int($bytes) ? $bytes : null; } /** * Checks if is a directory. * * @param string $path * * @return bool * * @example * File::isDir('/path/dir'); * // => true */ public static function isDir($path) { $path = Path::resolveAlias($path); return is_dir($path); } /** * Checks if is a file. * * @param string $path * * @return bool * * @example * File::isFile('/path/file.ext'); * // => true */ public static function isFile($path) { $path = Path::resolveAlias($path); return is_file($path); } /** * Checks if is a link. * * @param string $path * * @return bool * * @example * File::isLink('/path/link'); * // => true */ public static function isLink($path) { $path = Path::resolveAlias($path); return is_link($path); } /** * Glob files with braces support (Polyfill). * * @param string $pattern * @param int $flags * * @return array */ protected static function _glob($pattern, $flags = 0) { $files = []; foreach (Str::expandBraces($pattern) as $file) { $files = array_merge($files, glob($file, $flags | GLOB_NOSORT) ?: []); } return $files; } } src/EventDispatcher.php 0000644 00000010271 15175266533 0011152 0 ustar 00 <?php namespace YOOtheme; class EventDispatcher { /** * @var array */ protected $handlers = []; /** * @var array */ protected $listeners = []; /** * Constructor. * * @param array $handlers */ public function __construct(array $handlers = []) { $this->handlers = $handlers + [ 'filter' => [$this, 'handleFilter'], 'default' => [$this, 'handleDefault'], 'middleware' => [$this, 'handleMiddleware'], ]; } /** * Dispatches an event with arguments. * * @param string $event * @param array $arguments * * @return mixed */ public function dispatch($event, ...$arguments) { [$event, $type] = explode('|', $event, 2) + [1 => 'default']; $handler = $this->handlers[$type] ?? $this->handlers['default']; return $handler($this->getListeners($event), $arguments); } /** * Gets the event listeners. * * @param string $event * * @return array */ public function getListeners($event) { return array_merge(...$this->listeners[$event] ?? []); } /** * Adds an event listener. * * @param string $event * @param callable $listener * @param int $priority */ public function addListener($event, $listener, $priority = 0) { $exists = isset($this->listeners[$event][$priority]); $this->listeners[$event][$priority][] = $listener; if (!$exists) { krsort($this->listeners[$event]); } } /** * Removes an event listener. * * @param string $event * @param callable $listener * * @return bool */ public function removeListener($event, $listener = null) { if (($result = is_null($listener)) || !isset($this->listeners[$event])) { $this->listeners[$event] = []; } foreach ($this->listeners[$event] as &$listeners) { if (is_int($key = array_search($listener, $listeners, true))) { array_splice($listeners, $key, 1); return true; } } return $result; } /** * The default handler calls every listener ordered by the priority. * * @param array $listeners * @param array $arguments * * @return mixed */ protected function handleDefault(array $listeners, array $arguments = []) { $result = null; foreach ($listeners as $listener) { $_result = $listener(...$arguments); // stop event propagation? if ($_result === false) { break; } if (isset($_result)) { $result = $_result; } } return $result; } /** * The filter handler calls every listener ordered by the priority. It passes the return value from each listener to the next listener. * * @param array $listeners * @param array $arguments * * @return mixed */ protected function handleFilter(array $listeners, array $arguments = []) { if (!$arguments) { throw new \InvalidArgumentException('Filter must have at least one argument'); } foreach ($listeners as $listener) { $arguments[0] = $listener(...$arguments); } return $arguments[0]; } /** * The middleware handler calls every listener with arguments and a next() function which is invoked to execute the "next" middleware function. * * @param array $listeners * @param array $arguments * * @return mixed */ protected function handleMiddleware(array $listeners, array $arguments = []) { if (!$arguments) { throw new \InvalidArgumentException('Middleware must have at least one argument'); } if (!is_callable($arguments[0])) { throw new \InvalidArgumentException( 'Middleware must have a callable as first argument', ); } $middleware = new Middleware(array_shift($arguments), $listeners); return $middleware(...$arguments); } } src/Url.php 0000644 00000006452 15175266533 0006632 0 ustar 00 <?php namespace YOOtheme; use YOOtheme\Http\Uri; abstract class Url { protected static string $base; protected static ?Uri $baseUri; /** * Gets the base URL. * * @param bool $secure * * @return string */ public static function base($secure = null) { if (is_null($secure)) { return static::getBase()->getPath(); } return (string) static::getBase()->withScheme($secure ? 'https' : 'http'); } /** * Sets the base URL. * * @param string|Uri $base */ public static function setBase($base) { static::$baseUri = null; static::$base = (string) $base; } /** * Generates a URL to a path. * * @param string $path * @param array $parameters * @param bool $secure * * @return string|false */ public static function to($path, array $parameters = [], $secure = null) { try { if (empty($parameters) && is_null($secure) && static::isValid($path)) { return $path; } return (string) Event::emit( 'url.resolve|middleware', [static::class, 'generate'], $path, $parameters, $secure, ); } catch (\Exception $e) { return false; } } /** * Generates a URL to a route. * * @param string $pattern * @param array $parameters * @param bool $secure * * @return string */ public static function route($pattern = '', array $parameters = [], $secure = null) { return (string) Event::emit('url.route', $pattern, $parameters, $secure); } /** * Generates a URL to a path. * * @param string $path * @param array $parameters * @param bool $secure * * @return Uri */ public static function generate($path, array $parameters = [], $secure = null) { $url = new Uri($path); $base = static::getBase(); if (!$url->getHost() && !str_starts_with($url->getPath(), '/')) { $url = $url->withPath(Path::join($base->getPath(), $url->getPath())); } if ($query = array_replace($url->getQueryParams(), $parameters)) { $url = $url->withQueryParams($query); } if (is_bool($secure)) { if (!$url->getHost()) { $url = $url->withHost($base->getHost())->withPort($base->getPort()); } $url = $url->withScheme($secure ? 'https' : 'http'); } return $url; } public static function relative($url, $baseUrl = null) { $baseUrl ??= static::base(); return Path::relative($baseUrl ?: '/', $url); } /** * Checks if the given path is a valid URL. * * @param string $path * * @return bool */ public static function isValid($path) { $valid = ['http://', 'https://', 'mailto:', 'tel:', '//', '#']; return Str::startsWith($path, $valid) || filter_var($path, FILTER_VALIDATE_URL); } /** * Gets the base URL. * * @return Uri */ protected static function getBase() { return static::$baseUri ?? (static::$baseUri = new Uri(static::$base)); } } src/Event.php 0000644 00000002353 15175266533 0007145 0 ustar 00 <?php namespace YOOtheme; abstract class Event { /** * @var EventDispatcher|null */ protected static $dispatcher; /** * Adds an event listener. * * @param string $event * @param callable $listener * @param int $priority */ public static function on($event, $listener, $priority = 0) { static::getDispatcher()->addListener($event, $listener, $priority); } /** * Removes an event listener. * * @param string $event * @param callable $listener * * @return bool */ public static function off($event, $listener = null) { return static::getDispatcher()->removeListener($event, $listener); } /** * Emits an event with arguments. * * @param string $event * @param array $arguments * * @return mixed */ public static function emit($event, ...$arguments) { return static::getDispatcher()->dispatch($event, ...$arguments); } /** * Gets the event dispatcher instance. * * @return EventDispatcher */ public static function getDispatcher() { return static::$dispatcher ?: (static::$dispatcher = new EventDispatcher()); } } src/Memory.php 0000644 00000001624 15175266533 0007334 0 ustar 00 <?php namespace YOOtheme; abstract class Memory { /** * Try to raise memory_limit. * * @param string $memory */ public static function raise($memory = '512M') { $limit = static::toBytes(@ini_get('memory_limit')); if ($limit !== -1 && $limit < static::toBytes($memory)) { @ini_set('memory_limit', $memory); } } /** * Converts a shorthand byte value to an integer byte value. * * @param string|int $value * * @return int */ public static function toBytes($value) { $bytes = (int) $value; $value = substr(strtolower(trim($value)), -1); switch ($value) { case 'g': $bytes *= 1024; case 'm': $bytes *= 1024; case 'k': $bytes *= 1024; } return min($bytes, PHP_INT_MAX); } } src/Hook.php 0000644 00000003374 15175266533 0006770 0 ustar 00 <?php namespace YOOtheme; abstract class Hook { protected static ?HookCollection $collection; /** * Call hooks for given name. * * @param string|string[] $name * @return mixed */ public static function call($name, callable $callback, ...$arguments) { return static::getCollection()->call($name, $callback, ...$arguments); } /** * Add "wrap" hook for given name. */ public static function wrap(string $name, callable $callback): void { static::getCollection()->wrap($name, $callback); } /** * Add "before" hook for given name. */ public static function before(string $name, callable $callback): void { static::getCollection()->before($name, $callback); } /** * Add "after" hook for given name. */ public static function after(string $name, callable $callback): void { static::getCollection()->after($name, $callback); } /** * Add "filter" hook for given name. */ public static function filter(string $name, callable $callback): void { static::getCollection()->filter($name, $callback); } /** * Add "error" hook for given name. */ public static function error(string $name, callable $callback): void { static::getCollection()->error($name, $callback); } /** * Removes hook for given name. */ public static function remove(string $name, callable $callback): void { static::getCollection()->remove($name, $callback); } /** * Returns hook collection. */ public static function getCollection(): HookCollection { return static::$collection ?? (static::$collection = new HookCollection()); } } src/Reflection.php 0000644 00000015370 15175266533 0010161 0 ustar 00 <?php namespace YOOtheme; /** * A static class which provides utilities for working with class reflections. */ abstract class Reflection { public const REGEX_ANNOTATION = '/@(?<name>[\w\\\\]+)(?:\s*(?:\(\s*)?(?<value>.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/'; /** * @var array */ public static $annotations = []; /** * Gets reflector string representation. * * @param \Reflector $reflector * * @return string */ public static function toString(\Reflector $reflector) { $string = $reflector->getName(); if ($reflector instanceof \ReflectionMethod) { $string = "{$reflector->getDeclaringClass()->getName()}::{$string}()"; } if (ini_get('display_errors') && method_exists($reflector, 'getFileName')) { $string .= " in {$reflector->getFileName()}:{$reflector->getStartLine()}-{$reflector->getEndLine()}"; } return $string; } /** * Gets caller info using backtrace. * * @param string $key * @param int $index * * @return mixed */ public static function getCaller($key = null, $index = 1) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $index + 1); return $key ? $backtrace[$index][$key] : $backtrace[$index]; } /** * Gets reflection class for given classname. * * @param \ReflectionClass|object|string $class * * @throws \ReflectionException * * @return \ReflectionClass * * @example * Reflection::getClass('ClassName'); */ public static function getClass($class) { return $class instanceof \ReflectionClass ? $class : new \ReflectionClass($class); } /** * Gets the parent reflection classes for a given class. * * @param \ReflectionClass|object|string $class * * @return \ReflectionClass[] * * @example * Reflection::getParentClasses('ClassName'); */ public static function getParentClasses($class) { $class = static::getClass($class); do { $classes[] = $class; } while ($class = $class->getParentClass()); return $classes; } /** * Gets the reflection properties for given class. * * @param \ReflectionClass|object|string $class * * @return \ReflectionProperty[] * * @example * Reflection::getProperties('ClassName'); */ public static function getProperties($class) { $properties = []; foreach (static::getClass($class)->getProperties() as $property) { $property->setAccessible(true); $properties[$property->name] = $property; } return $properties; } /** * Gets the reflection function for given callback. * * @param callable|string $callback * * @throws \ReflectionException * * @return \ReflectionFunctionAbstract * * @example * Reflection::getFunction('ClassName::methodName'); */ public static function getFunction($callback) { if (is_string($callback) && strpos($callback, '::')) { $callback = explode('::', $callback); } if (is_array($callback)) { return new \ReflectionMethod($callback[0], $callback[1]); } if (is_object($callback) && !$callback instanceof \Closure) { return (new \ReflectionObject($callback))->getMethod('__invoke'); } return new \ReflectionFunction($callback); } /** * Gets the reflection parameters for given callback. * * @param callable|string $callback * * @throws \ReflectionException * * @return \ReflectionParameter[] * * @example * Reflection::getParameters('ClassName::methodName'); */ public static function getParameters($callback) { return static::getFunction($callback)->getParameters(); } /** * Gets an annotation by name for given reflector. * * @param \Reflector $reflector * @param string $name * * @return object|void * * @example * $reflector = Reflection::getAnnotation('ClassName'); * Reflection::getAnnotation($reflector, 'tag'); */ public static function getAnnotation(\Reflector $reflector, $name) { if ($annotations = static::getAnnotations($reflector, $name)) { return $annotations[0]; } } /** * Gets all annotations for given reflector. * * @param \Reflector $reflector * @param string $name * * @return array * * @example * $reflector = Reflection::getClass('ClassName'); * Reflection::getAnnotations($reflector); */ public static function getAnnotations(\Reflector $reflector, $name = null) { if ($reflector instanceof \ReflectionClass) { $key = $reflector->name; } elseif ($reflector instanceof \ReflectionProperty) { $key = "{$reflector->class}.{$reflector->name}"; } elseif ($reflector instanceof \ReflectionMethod) { $key = "{$reflector->class}:{$reflector->name}"; } else { $key = null; } if (!isset(static::$annotations[$key])) { $comment = $reflector->getDocComment() ?: ''; if (!$name || strpos($comment, "@{$name}")) { static::$annotations[$key] = static::parseAnnotations($comment); } elseif (!$comment || !strpos($comment, '@')) { return static::$annotations[$key] = []; } else { return []; } } return $name ? static::filterAnnotations(static::$annotations[$key], $name) : static::$annotations[$key]; } /** * Parses all annotations from given string. * * @param string $string * * @return array */ protected static function parseAnnotations($string) { $annotations = []; if (preg_match_all(static::REGEX_ANNOTATION, $string, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $annotations[] = (object) [ 'name' => $match['name'], 'value' => $match['value'] ?? null, ]; } } return $annotations; } /** * Filters annotations by given name. * * @param array $annotations * @param mixed $name * * @return array */ protected static function filterAnnotations(array $annotations, $name) { $results = []; foreach ($annotations as $annotation) { if ($annotation->name == $name) { $results[] = $annotation; } } return $results; } } src/HookCollection.php 0000644 00000007344 15175266533 0011005 0 ustar 00 <?php namespace YOOtheme; class HookCollection { protected const WRAP = 'wrap'; protected const BEFORE = 'before'; protected const AFTER = 'after'; protected const FILTER = 'filter'; protected const ERROR = 'error'; protected array $hooks = []; /** * Call hooks for given name. * * @param string|string[] $name * @return mixed */ public function call($name, callable $callback, ...$arguments) { $names = is_array($name) ? array_reverse($name) : [$name]; foreach ($names as $name) { if ($hooks = $this->hooks[$name] ?? null) { $callback = $this->build($callback, $hooks); } } return $callback(...$arguments); } /** * Add "wrap" hook for given name. */ public function wrap(string $name, callable $callback): void { $this->hooks[$name][] = [static::WRAP, $callback]; } /** * Add "before" hook for given name. */ public function before(string $name, callable $callback): void { $this->hooks[$name][] = [static::BEFORE, $callback]; } /** * Add "after" hook for given name. */ public function after(string $name, callable $callback): void { $this->hooks[$name][] = [static::AFTER, $callback]; } /** * Add "filter" hook for given name. */ public function filter(string $name, callable $callback): void { $this->hooks[$name][] = [static::FILTER, $callback]; } /** * Add "error" hook for given name. */ public function error(string $name, callable $callback): void { $this->hooks[$name][] = [static::ERROR, $callback]; } /** * Removes hook for given name. */ public function remove(string $name, callable $callback): void { $this->hooks[$name] = array_filter( $this->hooks[$name] ?? [], fn($hook) => $hook[1] === $callback, ); } /** * Builds a function from given hook array. */ protected function build(callable $method, array $hooks): callable { $errors = []; foreach ($hooks as $i => [$kind, $callback]) { if ($kind === static::ERROR) { $errors[] = [$kind, $callback]; unset($hooks[$i]); } } return array_reduce([...$hooks, ...$errors], [$this, 'reduce'], $method); } /** * Returns a function for given hook. */ protected function reduce(callable $method, array $hook): \Closure { [$kind, $callback] = $hook; if ($kind === static::WRAP) { return function (...$arguments) use ($method, $callback) { return $callback($method, ...$arguments); }; } if ($kind === static::BEFORE) { return function (...$arguments) use ($method, $callback) { $callback(...$arguments); return $method(...$arguments); }; } if ($kind === static::AFTER) { return function (...$arguments) use ($method, $callback) { $callback($result = $method(...$arguments), ...$arguments); return $result; }; } if ($kind === static::FILTER) { return function (...$arguments) use ($method, $callback) { return $callback($method(...$arguments), ...$arguments); }; } return function (...$arguments) use ($method, $callback) { try { $result = $method(...$arguments); } catch (\Throwable $error) { $result = $callback($error, ...$arguments); } return $result; }; } } src/Middleware.php 0000644 00000001351 15175266533 0010136 0 ustar 00 <?php namespace YOOtheme; class Middleware { /** * @var array */ public $stack; /** * Constructor. * * @param callable $kernel * @param callable[] $stack */ public function __construct(callable $kernel, array $stack = []) { $this->stack = $stack; $this->stack[] = $kernel; } /** * Invokes the next middleware callback. * * @param mixed ...$arguments * * @return mixed */ public function __invoke(...$arguments) { $callback = array_shift($this->stack); // add next() as last argument if ($this->stack) { $arguments[] = $this; } return $callback(...$arguments); } } src/Path.php 0000644 00000022073 15175266533 0006761 0 ustar 00 <?php namespace YOOtheme; /** * A static class which provides utilities for working with directory paths. */ abstract class Path { /** * @var array */ protected static $aliases = []; /** * Gets an absolute path by resolving aliases and current directory. * * @param string $path * @param ?string $base * * @return string * * @example * Path::get('~app/dir'); * // => /app/dir */ public static function get($path, $base = null) { $path = static::resolveAlias($path); // path is `.`, `..` or starts with `./`, `../` if (str_starts_with($path, '.') && preg_match('/^\.\.?(?=\/|$)/', $path)) { return static::join($base ?? dirname(Reflection::getCaller('file')), $path); } return $path; } /** * Sets a path alias. * * @param string $alias * @param string $path * * @example * Path::setAlias('~app', '/app'); * * Path::resolveAlias('~app/resource'); * // => /app/resource */ public static function setAlias($alias, $path) { if (!str_starts_with($alias, '~')) { throw new \InvalidArgumentException("The alias '{$alias}' must start with ~"); } $path = rtrim(static::resolveAlias($path), '/'); $alias = rtrim(strtr($alias, '\\', '/'), '/'); [$name] = explode('/', $alias, 2); static::$aliases[$name]["$alias/"] = "$path/"; } /** * Resolve a path with alias. * * @param string $path * * @return string * * @example * Path::setAlias('~app', '/app'); * * Path::resolveAlias('~app/resource'); * // => /app/resource */ public static function resolveAlias($path) { $path = strtr($path, '\\', '/'); [$name] = explode('/', $path, 2); if (!str_starts_with($name, '~')) { return $path; } $trim = !str_ends_with($path, '/'); $path = Event::emit("path {$name}|filter", $path, substr($path, strlen($name))); if (isset(static::$aliases[$name])) { $path = strtr($trim ? "{$path}/" : $path, static::$aliases[$name]); } return $trim ? rtrim($path, '/') : $path; } /** * Resolves a sequence of paths or path segments into an absolute path. All path segments are processed from right to left. * * @param string $paths * * @return string * * @example * Path::resolve('~app/dir/dir', '../resource'); * // => /app/dir/resource */ public static function resolve(...$paths) { $parts = []; foreach (array_reverse($paths) as $path) { $path = static::resolveAlias($path); array_unshift($parts, $path); if (static::isAbsolute($path)) { break; } } $path = static::join(...$parts); return $path !== '/' ? rtrim($path, '/') : $path; } /** * Returns trailing name component of path. * * @param string $path * @param string $suffix * * @return string * * @example * Path::basename('~app/dir/file.php'); * // => file.php */ public static function basename($path, $suffix = '') { return basename(static::resolveAlias($path), $suffix); } /** * Returns the extension of the path. * * @param string $path * * @return string * * @example * Path::extname('~app/dir/file.php'); * // => .php */ public static function extname($path) { $basename = static::basename($path); $position = strrpos($basename, '.'); return $position ? substr($basename, $position) : ''; } /** * Returns a parent directory's path. * * @param string $path * * @return string * * @example * Path::dirname('~app/dir/file.php'); * // => /app/dir */ public static function dirname($path) { return dirname(static::resolveAlias($path)); } /** * Gets the relative path to a given base path. * * @param string $from * @param string $to * * @return string * * @example * Path::relative('/path/dir/test/aaa', '/path/dir/impl/bbb'); * // => ../../impl/bbb */ public static function relative($from, $to) { $from = static::resolveAlias($from); $to = static::resolveAlias($to); if ($to === '') { return $from; } $_from = static::parse($from); $_to = static::parse($to); if ($_from['root'] !== $_to['root']) { throw new \InvalidArgumentException( "The path '{$to}' can\'t be made relative to the path '{$from}'. Path roots aren\'t equal.", ); } $fromParts = explode('/', $_from['pathname']); $toParts = explode('/', $_to['pathname']); $match = true; $prefix = ''; foreach ($fromParts as $i => $fromPart) { if ('' === $fromPart) { continue; } if ($match && isset($toParts[$i]) && $fromPart === $toParts[$i]) { unset($toParts[$i]); continue; } $match = false; $prefix .= '../'; } return rtrim($prefix . join('/', $toParts), '/'); } /** * Normalizes a path, resolving '..' and '.' segments. * * @param string $path * * @return string * * @example * Path::normalize('/path1/.././file.txt'); * // => /file.txt */ public static function normalize($path) { static $cache; if (!$path) { return ''; } if (isset($cache[$path])) { return $cache[$path]; } $result = []; $parsed = static::parse($path); $parts = explode('/', $parsed['pathname']); foreach ($parts as $i => $part) { if ('.' === $part) { continue; } if ('' === $part && isset($parts[$i + 1])) { continue; } if ($part === '..' && $result && end($result) !== '..') { array_pop($result); continue; } if ($part !== '..' || $parsed['root'] === '') { $result[] = $part; } } return $cache[$path] = $parsed['root'] . join('/', $result); } /** * Joins all given path segments together. * * @param string $parts * * @return string * * @example * Path::join('/foo', '/bar', 'baz/asdf', 'quux', '..'); * // => /foo/bar/baz/asdf */ public static function join(...$parts) { return static::normalize(join('/', $parts)); } /** * Returns information about a path. * * @param string $path * * @return array * * @example * Path::parse('/foo/file.txt'); * // => ['root' => '/', 'pathname' => 'foo/file.txt', 'dirname' => '/foo', 'basename' => 'file.txt', 'filename' => 'file', 'extension' => 'txt'] */ public static function parse($path) { $path = strtr($path, '\\', '/'); $root = static::root($path) ?: ''; return pathinfo($path) + [ 'root' => $root, 'pathname' => substr($path, strlen($root)), 'dirname' => null, 'extension' => null, ]; } /** * Checks if path is absolute. * * @param string $path * * @return bool * * @example * Path::isAbsolute('/foo/file.txt'); * // => true */ public static function isAbsolute($path) { return (bool) static::root($path); } /** * Checks if path is relative. * * @param string $path * * @return bool * * @example * Path::isRelative('foo/file.txt'); * // => true */ public static function isRelative($path) { return !static::root($path); } /** * Checks if path is a base path of another path. * * @param string $basePath * @param string $path * * @return bool * * @example * Path::isBasePath('/foo/', '/foo/file.txt'); * // => true * Path::isBasePath('/foo', '/foo'); * // => true * Path::isBasePath('/foo', '/foo/..'); * // => false */ public static function isBasePath($basePath, $path) { $basePath = static::normalize(static::resolveAlias($basePath)); $path = static::normalize(static::resolveAlias($path)); return str_starts_with("{$path}/", rtrim($basePath, '/') . '/'); } /** * Returns path root. * * @param string $path * * @return mixed */ public static function root($path) { $path = strtr($path, '\\', '/'); if ($path && $path[0] === '/') { return '/'; } if (strpos($path, ':') && preg_match('/^([a-z]*:)?(\/\/|\/)/i', $path, $matches)) { return $matches[0]; } } } src/Arr.php 0000644 00000037751 15175266533 0006622 0 ustar 00 <?php namespace YOOtheme; /** * A static class which provides utilities for working with arrays. */ abstract class Arr { /** * Checks if the given key exists. * * @param array|\ArrayAccess $array * @param string|null $key * * @return bool * * @example * $array = ['a' => ['b' => 2]]; * * Arr::has($array, 'a'); * // => true * * Arr::has($array, 'a.b'); * // => true */ public static function has($array, $key) { if (!$array || is_null($key)) { return false; } if (static::exists($array, $key)) { return true; } if (!strpos($key, '.')) { return false; } foreach (explode('.', $key) as $part) { if (static::exists($array, $part)) { $array = $array[$part]; } else { return false; } } return true; } /** * Gets a value by key. * * @param array|\ArrayAccess $array * @param string|null $key * @param mixed $default * * @return mixed * * @example * $array = ['a' => [['b' => ['c' => 3]]]]; * * Arr::get($array, 'a.0.b.c'); * // => 3 * * Arr::get($array, 'a.b.c', 'default'); * // => 'default' */ public static function get($array, $key, $default = null) { if (!static::accessible($array)) { return $default; } if ($key === null) { return $array; } if (static::exists($array, $key)) { return $array[$key]; } if (!strpos($key, '.')) { return $default; } foreach (explode('.', $key) as $part) { if (static::exists($array, $part)) { $array = $array[$part]; } else { return $default; } } return $array; } /** * Sets a value. * * @param array|\ArrayAccess $array * @param string|null $key * @param mixed $value * * @return array|\ArrayAccess * * @example * $array = ['a' => [['b' => ['c' => 3]]]]; * * Arr::set($array, 'a.0.b.c', 4); * // => ['a' => [['b' => ['c' => 4]]]] */ public static function set(&$array, $key, $value) { if ($key === null) { return $array = $value; } $arr = &$array; $parts = explode('.', $key); while (count($parts) > 1) { $part = array_shift($parts); if (!isset($arr[$part]) || !is_array($arr[$part])) { $arr[$part] = []; } $arr = &$arr[$part]; } $arr[array_shift($parts)] = $value; return $array; } /** * Deletes a value from array by key. * * @param array|\ArrayAccess $array * @param string $key * * @example * $array = ['a' => [['b' => ['c' => 3]]]]; * * Arr::del($array, 'a.0.b.c'); * * print_r($array); * // => ['a' => [['b' => []]]] */ public static function del(&$array, $key) { // if the exact key exists in the top-level, delete it if (static::exists($array, $key)) { unset($array[$key]); return; } $parts = explode('.', $key); while (count($parts) > 1) { $part = array_shift($parts); if (isset($array[$part]) && is_array($array[$part])) { $array = &$array[$part]; } else { return; } } unset($array[array_shift($parts)]); } /** * Get a value from the array, and delete it. * * @param array|\ArrayAccess $array * @param string $key * @param mixed $default * * @return mixed * * @example * $array = ['a' => [['b' => ['c' => 3]]]]; * * Arr::pull($array, 'a.0.b.c'); * // => 3 * * print_r($array); * // => ['a' => [['b' => []]]] */ public static function pull(&$array, $key, $default = null) { $value = static::get($array, $key, $default); static::del($array, $key); return $value; } /** * Set a value using an update callback. * * @param array|\ArrayAccess $array * @param string $key * @param callable $callback * * @return array * * @example * $array = ['a' => [['b' => ['c' => 3]]]]; * * Arr::update($array, 'a.0.b.c', function($n) { return $n * $n; }); * * print_r($array); * // => ['a' => [['b' => ['c' => 9]]]] */ public static function update(&$array, $key, callable $callback) { return static::set($array, $key, $callback(static::get($array, $key))); } /** * Checks if all values pass the predicate truth test. * * @param array $array * @param array|callable $predicate * * @return bool * * @example * $collection = [ * ['user' => 'barney', 'role' => 'editor', 'age' => 36, 'active' => false], * ['user' => 'joana', 'role' => 'editor', 'age' => 23, 'active' => true] * ]; * * Arr::every($collection, ['role' => 'editor']); * // true * * Arr::every($collection, function($v) { return $v['active']; }); * // false */ public static function every($array, $predicate) { $callback = is_callable($predicate) ? $predicate : static::matches($predicate); foreach ($array as $key => $value) { if (!$callback($value, $key)) { return false; } } return true; } /** * Checks if some values pass the predicate truth test. * * @param array|\ArrayAccess $array * @param array|callable $predicate * * @return bool * * @example * $collection = [ * ['user' => 'barney', 'role' => 'editor', 'age' => 36, 'active' => false], * ['user' => 'joana', 'role' => 'editor', 'age' => 23, 'active' => true] * ]; * * Arr::some($collection, ['user' => 'barney']); * // true * * Arr::some($collection, function($v) { return $v['active']; }); * // true */ public static function some($array, $predicate) { $callback = is_callable($predicate) ? $predicate : static::matches($predicate); foreach ($array as $key => $value) { if ($callback($value, $key)) { return true; } } return false; } /** * Gets the picked values from the given array. * * @param array $array * @param string|array|callable $predicate * * @return array * * @example * $array = ['a' => 1, 'b' => 2, 'c' => 3]; * * Arr::pick($array, ['a', 'c']); * // ['a' => 1, 'c' => 3]; */ public static function pick($array, $predicate) { if (is_callable($predicate)) { return array_filter($array, $predicate, ARRAY_FILTER_USE_BOTH); } return array_intersect_key($array, array_flip((array) $predicate)); } /** * Gets an array composed of the properties of the given array that are not omitted. * * @param array $array * @param string|array|callable $predicate * * @return array * * @example * $array = ['a' => 1, 'b' => 2, 'c' => 3]; * * Arr::omit($array, ['b']); * // ['a' => 1, 'c' => 3]; */ public static function omit($array, $predicate) { if (is_callable($predicate)) { return array_diff_key($array, array_filter($array, $predicate, ARRAY_FILTER_USE_BOTH)); } return array_diff_key($array, array_flip((array) $predicate)); } /** * Gets the first value in an array passing the predicate truth test. * * @param array|\ArrayAccess $array * @param array|callable $predicate * * @return mixed * * @example * $collection = [ * ['user' => 'barney', 'role' => 'editor', 'age' => 36, 'active' => false], * ['user' => 'joana', 'role' => 'editor', 'age' => 23, 'active' => true] * ]; * * Arr::find($collection, ['user' => 'barney']); * // $collection[0] * * Arr::find($collection, function($v) { return $v['age'] === 23; }); * // $collection[1] */ public static function find($array, $predicate) { $callback = is_callable($predicate) ? $predicate : static::matches($predicate); foreach ($array as $key => $value) { if ($callback($value, $key)) { return $value; } } } /** * Gets all values in an array passing the predicate truth test. * * @param array $array * @param array|callable $predicate * * @return array * * @example * $collection = [ * ['user' => 'barney', 'role' => 'editor', 'age' => 36, 'active' => false], * ['user' => 'joana', 'role' => 'editor', 'age' => 23, 'active' => true], * ['user' => 'fred', 'role' => 'editor', 'age' => 40, 'active' => false] * ]; * * Arr::filter($collection, ['active' => true]); * // [$collection[1]] * * Arr::filter($collection, function($v) { return $v['age'] > 30; }); * // [$collection[0], $collection[2]] */ public static function filter($array, $predicate = null) { if (is_null($predicate)) { return array_filter($array); } $callback = is_callable($predicate) ? $predicate : static::matches($predicate); return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); } /** * Merges two arrays recursively. * * @param array|\ArrayAccess $array1 * @param array|\ArrayAccess $array2 * * @return array|\ArrayAccess * * @example * $array = ['a' => [['b' => 2], ['d' => 4]]]; * $other = ['a' => [['c' => 3], ['e' => 5]]]; * * Arr::merge($array, $other); * // =>['a' => [['b' => 2], ['d' => 4], ['c' => 3], ['e' => 5]]] */ public static function merge($array1, $array2) { foreach ($array2 as $key => $value) { if (isset($array1[$key])) { if (is_int($key)) { $array1[] = $value; } elseif (static::accessible($value) && static::accessible($array1[$key])) { $array1[$key] = static::merge($array1[$key], $value); } else { $array1[$key] = $value; } } else { $array1[$key] = $value; } } return $array1; } /** * Flatten a multi-dimensional array into a single level. * * @param array $array * @param int $depth * * @return array * * @example * $array = [1, [2, [3, [4]], 5]]; * * Arr::flatten($array); * // => [1, 2, 3, 4, 5] * * Arr::flatten($array, 1); * // => [1, 2, [3, [4]], 5] */ public static function flatten($array, $depth = 0) { $result = []; foreach ($array as $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; } /** * Chunks an array evenly into columns. * * @param array $array * @param int $columns * * @return array * * @example * $array = [1, 2, 3, 4, 5]; * * Arr::columns($array, 2); * // => [[1, 2, 3], [4, 5]] * * Arr::columns($array, 4); * // => [[1, 2], [3], [4], [5]] */ public static function columns($array, $columns) { $count = count($array); $columns = min($count, $columns); $rows = (int) ceil($count / $columns); $remainder = $count % $columns; if (!$remainder) { return array_chunk($array, $rows); } $result = []; for ($i = 0; $i < $columns; $i++) { $result[] = array_slice( $array, $i * $rows - max($i - $remainder, 0), $rows - ($i >= $remainder ? 1 : 0), ); } return $result; } /** * Checks if the given key exists. * * @param array|\ArrayAccess $array * @param string $key * * @return bool */ public static function exists($array, $key) { if (!static::accessible($array)) { return false; } if ($array instanceof \ArrayAccess) { return $array->offsetExists($key); } return array_key_exists($key, $array); } /** * Wraps given value in array, if it is not one. * * @param mixed $value * * @return array */ public static function wrap($value) { if (is_null($value)) { return []; } return is_array($value) ? $value : [$value]; } /** * Checks if the given value is array accessible. * * @param mixed $value * * @return bool */ public static function accessible($value) { return is_array($value) || $value instanceof \ArrayAccess; } /** * Removes a portion of the array and replaces it with something else, preserving keys. * * @param array $array * @param int|null $offset * @param int|null $length * @param mixed $replacement * * @return array * * @example * $array = ['a' => 1, 'b' => 2, 'c' => 3]; * * Arr::splice($array, 1, 1); * // => ['a' => 1, 'c' => 3] * * Arr::splice($array, 1, 2, ['d' => 4]); * // => ['a' => 1, 'd' => 4] */ public static function splice(&$array, $offset, $length, $replacement) { $result = $offset !== null && $length ? array_slice($array, $offset, $length, true) : []; $array = array_merge( array_slice($array, 0, $offset, true), static::wrap($replacement), $offset !== null ? array_slice($array, $offset + $length, null, true) : [], ); return $result; } /** * Renames keys in an array. It does not preserve key order. * * @param array $array * @param array $keys * * @return array * * @example * $array = ['a' => 1, 'b' => 2, 'c' => 3]; * * Arr::rename($array, ['b' => 'd']); * // => ['a' => 1, 'c' => 3, 'd' => 2] */ public static function updateKeys(&$array, $keys) { foreach ($keys as $oldKey => $newKey) { if (static::has($array, $oldKey)) { $value = static::get($array, $oldKey); static::del($array, $oldKey); if (is_callable($newKey)) { foreach ($newKey($value) ?: [] as $key => $value) { static::set($array, $key, $value); } } else { static::set($array, $newKey, $value); } } } return $array; } /** * Creates a callback function to match array values. * * @param array $predicate * * @return callable */ protected static function matches(array $predicate) { return function ($array) use ($predicate) { if (!static::accessible($array)) { return false; } foreach ($predicate as $key => $value) { if (!static::exists($array, $key)) { return false; } if ($array[$key] != $value) { return false; } } return true; }; } } src/Str.php 0000644 00000025074 15175266533 0006641 0 ustar 00 <?php namespace YOOtheme; /** * A static class which provides utilities for working with strings. */ abstract class Str { /** * @var string */ public static $encoding = 'UTF-8'; /** * Checks if string matches a given pattern. * * @param string $pattern * @param string $string * * @return bool * * @example * Str::is('foo/*', 'foo/bar/baz'); * // => true */ public static function is($pattern, $string) { static $cache; $string = (string) $string; $pattern = (string) $pattern; if ($pattern === $string) { return true; } if (empty($cache[$pattern])) { $regexp = addcslashes($pattern, '/\\.+^$()=!<>|#'); $regexp = strtr($regexp, ['*' => '.*', '?' => '.?']); $regexp = static::convertBraces($regexp); $cache[$pattern] = "#^{$regexp}$#s"; } return (bool) preg_match($cache[$pattern], $string); } /** * Checks if string contains a given substring. * * @param string $haystack * @param string|array $needles * * @return bool * * @example * Str::contains('taylor', 'ylo'); * // => true */ public static function contains($haystack, $needles) { foreach ((array) $needles as $needle) { if ($needle !== '' && mb_strpos($haystack, $needle, 0, static::$encoding) !== false) { return true; } } return false; } /** * Checks if string starts with a given substring. * * @param string $haystack * @param string|array $needles * * @return bool * * @example * Str::startsWith('jason', 'jas'); * // => true */ public static function startsWith($haystack, $needles) { foreach ((array) $needles as $needle) { if (str_starts_with((string) $haystack, (string) $needle)) { return true; } } return false; } /** * Checks if string ends with a given substring. * * @param string $haystack * @param string|array $needles * * @return bool * * @example * Str::endsWith('jason', 'on'); * // => true */ public static function endsWith($haystack, $needles) { foreach ((array) $needles as $needle) { if (str_ends_with((string) $haystack, (string) $needle)) { return true; } } return false; } /** * Returns the string length. * * @param string $string * * @return int * * @example * Str::length('foo bar baz'); * // => 11 */ public static function length($string) { return mb_strlen(strval($string), static::$encoding); } /** * Convert string to lower case. * * @param string $string * * @return string * * @example * Str::lower('fOo Bar bAz'); * // => foo bar baz */ public static function lower($string) { return mb_strtolower($string, static::$encoding); } /** * Converts the first character of string to lower case. * * @param string $string * * @return string * * @example * Str::lowerFirst('FOO BAR BAZ'); * // => fOO BAR BAZ */ public static function lowerFirst($string) { return static::lower(static::substr($string, 0, 1)) . static::substr($string, 1); } /** * Converts string to upper case. * * @param string $string * * @return string * * @example * Str::upper('fOo Bar bAz'); * // => FOO BAR BAZ */ public static function upper($string) { return mb_strtoupper($string, static::$encoding); } /** * Converts the first character of string to upper case. * * @param string $string * * @return string * * @example * Str::upperFirst('foo bar baz'); * // => Foo bar baz */ public static function upperFirst($string) { return static::upper(static::substr($string, 0, 1)) . static::substr($string, 1); } /** * Converts string to title case. * * @param string|string[] $string * * @return string * * @example * Str::titleCase('jefferson costella'); * // => Jefferson Costella */ public static function titleCase($string) { return mb_convert_case(join(' ', (array) $string), MB_CASE_TITLE, static::$encoding); } /** * Converts string to camel case (https://en.wikipedia.org/wiki/Camel_case). * * @param string|string[] $string * @param bool $upper * * @return string * * @example * Str::camelCase('Yootheme Framework'); * // => yoothemeFramework */ public static function camelCase($string, $upper = false) { $string = join(' ', (array) $string); $string = str_replace(['-', '_'], ' ', $string); $string = str_replace(' ', '', ucwords($string)); return $upper ? $string : lcfirst($string); } /** * Converts string to snake case (https://en.wikipedia.org/wiki/Snake_case). * * @param string|string[] $string * @param string $delimiter * * @return string * * @example * Str::snakeCase('Yootheme Framework'); * // => yootheme_framework */ public static function snakeCase($string, $delimiter = '_') { $string = join(' ', (array) $string); if (!ctype_lower($string)) { $string = preg_replace('/[^a-zA-Z0-9]/u', ' ', $string); $string = preg_replace('/\s+/u', '', ucwords($string)); $string = static::lower( preg_replace( '/([a-z])(?=[A-Z0-9])|([0-9]+)(?=[a-zA-Z])|([A-Z]+)(?=[A-Z])/u', "$0{$delimiter}", $string, ), ); } return $string; } /** * Returns part of a string. * * @param string $string * @param int $start * @param int|null $length * * @return string * * @example * Str::substr('Yootheme Framework', 3, 5); * // => theme */ public static function substr($string, $start, $length = null) { return mb_substr($string, $start, $length, static::$encoding); } /** * Limit the number of characters in a string. * * @param string $string * @param int $length * @param string $omission * @param bool $exact * * @return string * * @example * Str::limit('hi-diddly-ho there, neighborino', 24); * // => hi-diddly-ho there, n... */ public static function limit($string, $length = 100, $omission = '...', $exact = true) { $strLength = mb_strwidth($string, static::$encoding); $omitLength = $length - mb_strwidth($omission, static::$encoding); if ($omitLength <= 0) { return ''; } if ($strLength <= $length) { return $string; } $trimmed = rtrim( mb_strimwidth($string, 0, $omitLength, '', static::$encoding), " \n\r\t\v\x00,.!?:", // Remove trailing whitespace and punctuation ); if ($exact || mb_substr($string, mb_strwidth($trimmed), 1, static::$encoding) === ' ') { return $trimmed . $omission; } return preg_replace('/(.*)\s.*/s', '$1', ltrim($trimmed)) . $omission; } /** * Limit the number of words in a string. * * @param string $string * @param int $words * @param string $omission * * @return string * * @example * Str::words('Taylor Otwell', 1); * // => Taylor... */ public static function words($string, $words = 100, $omission = '...') { preg_match('/^\s*+(?:\S++\s*+){1,' . $words . '}/u', $string, $matches); if (!isset($matches[0]) || strlen($string) === strlen($matches[0])) { return $string; } return rtrim($matches[0]) . $omission; } /** * Generates a "random" alphanumeric string. * * @param int $length * * @throws \Exception * * @return string * * @example * Str::random(); * // => X2wvU09F1j4ZCzKD */ public static function random($length = 16) { $string = ''; while (($len = strlen($string)) < $length) { $bytes = random_bytes($size = $length - $len); $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size); } return $string; } /** * Expands glob braces to array. * * @param string $pattern * * @return array * * @example * Str::expandBraces('foo/{2,3}/bar'); * // => ['foo/2/bar', 'foo/3/bar'] */ public static function expandBraces($pattern) { $braces = []; $expanded = []; $callback = function ($matches) use (&$braces) { $index = '{' . count($braces) . '}'; $braces[$index] = $matches[0]; return $index; }; if ( preg_match($regex = '/{((?:[^{}]+|(?R))*)}/', $pattern, $matches, PREG_OFFSET_CAPTURE) ) { [$matches, [$replaces]] = $matches; foreach ( explode(',', preg_replace_callback($regex, $callback, $replaces)) as $replace ) { $expand = substr_replace( $pattern, strtr($replace, $braces), $matches[1], strlen($matches[0]), ); $expanded = array_merge($expanded, static::expandBraces($expand)); } } return $expanded ?: [$pattern]; } /** * Converts glob braces to a regex. * * @param string $pattern * * @return string * * @example * Str::convertBraces('foo/{2,3}/bar'); * // => foo/(2|3)/bar */ public static function convertBraces($pattern) { if (preg_match_all('/{((?:[^{}]+|(?R))*)}/', $pattern, $matches, PREG_OFFSET_CAPTURE)) { [$matches, $replaces] = $matches; foreach ($matches as $i => $m) { $replace = str_replace(',', '|', static::convertBraces($replaces[$i][0])); $pattern = substr_replace($pattern, "({$replace})", $m[1], strlen($m[0])); } } return $pattern; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.23 | Generation time: 0 |
proxy
|
phpinfo
|
Settings