File manager - Edit - /home/autoph/public_html/projects/Rating-AutoHub/public/css/src.zip
Back
PK Cv�Z���w w LoggerTrait.phpnu �[��� <?php namespace Psr\Log; /** * This is a simple Logger trait that classes unable to extend AbstractLogger * (because they extend another class, etc) can include. * * It simply delegates all log-level-specific methods to the `log` method to * reduce boilerplate code that a simple Logger that does the same thing with * messages regardless of the error level has to implement. */ trait LoggerTrait { /** * System is unusable. * * @param string|\Stringable $message * @param array $context * * @return void */ public function emergency(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::EMERGENCY, $message, $context); } /** * Action must be taken immediately. * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * * @param string|\Stringable $message * @param array $context * * @return void */ public function alert(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::ALERT, $message, $context); } /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string|\Stringable $message * @param array $context * * @return void */ public function critical(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::CRITICAL, $message, $context); } /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string|\Stringable $message * @param array $context * * @return void */ public function error(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::ERROR, $message, $context); } /** * Exceptional occurrences that are not errors. * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * * @param string|\Stringable $message * @param array $context * * @return void */ public function warning(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::WARNING, $message, $context); } /** * Normal but significant events. * * @param string|\Stringable $message * @param array $context * * @return void */ public function notice(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::NOTICE, $message, $context); } /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string|\Stringable $message * @param array $context * * @return void */ public function info(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::INFO, $message, $context); } /** * Detailed debug information. * * @param string|\Stringable $message * @param array $context * * @return void */ public function debug(string|\Stringable $message, array $context = []): void { $this->log(LogLevel::DEBUG, $message, $context); } /** * Logs with an arbitrary level. * * @param mixed $level * @param string|\Stringable $message * @param array $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ abstract public function log($level, string|\Stringable $message, array $context = []): void; } PK Cv�Z�K;� � NullLogger.phpnu �[��� <?php namespace Psr\Log; /** * This Logger can be used to avoid conditional log calls. * * Logging should always be optional, and if no logger is provided to your * library creating a NullLogger instance to have something to throw logs at * is a good way to avoid littering your code with `if ($this->logger) { }` * blocks. */ class NullLogger extends AbstractLogger { /** * Logs with an arbitrary level. * * @param mixed $level * @param string|\Stringable $message * @param array $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ public function log($level, string|\Stringable $message, array $context = []): void { // noop } } PK Cv�Z��~/ / LoggerAwareInterface.phpnu �[��� <?php namespace Psr\Log; /** * Describes a logger-aware instance. */ interface LoggerAwareInterface { /** * Sets a logger instance on the object. * * @param LoggerInterface $logger * * @return void */ public function setLogger(LoggerInterface $logger): void; } PK Cv�Z��P P LogLevel.phpnu �[��� <?php namespace Psr\Log; /** * Describes log levels. */ class LogLevel { const EMERGENCY = 'emergency'; const ALERT = 'alert'; const CRITICAL = 'critical'; const ERROR = 'error'; const WARNING = 'warning'; const NOTICE = 'notice'; const INFO = 'info'; const DEBUG = 'debug'; } PK Cv�Z �X1` ` InvalidArgumentException.phpnu �[��� <?php namespace Psr\Log; class InvalidArgumentException extends \InvalidArgumentException { } PK Cv�Z�AaHA A LoggerInterface.phpnu �[��� <?php namespace Psr\Log; /** * Describes a logger instance. * * The message MUST be a string or object implementing __toString(). * * The message MAY contain placeholders in the form: {foo} where foo * will be replaced by the context data in key "foo". * * The context array can contain arbitrary data. The only assumption that * can be made by implementors is that if an Exception instance is given * to produce a stack trace, it MUST be in a key named "exception". * * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md * for the full interface specification. */ interface LoggerInterface { /** * System is unusable. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function emergency(string|\Stringable $message, array $context = []): void; /** * Action must be taken immediately. * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function alert(string|\Stringable $message, array $context = []): void; /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function critical(string|\Stringable $message, array $context = []): void; /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function error(string|\Stringable $message, array $context = []): void; /** * Exceptional occurrences that are not errors. * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function warning(string|\Stringable $message, array $context = []): void; /** * Normal but significant events. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function notice(string|\Stringable $message, array $context = []): void; /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function info(string|\Stringable $message, array $context = []): void; /** * Detailed debug information. * * @param string|\Stringable $message * @param mixed[] $context * * @return void */ public function debug(string|\Stringable $message, array $context = []): void; /** * Logs with an arbitrary level. * * @param mixed $level * @param string|\Stringable $message * @param mixed[] $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ public function log($level, string|\Stringable $message, array $context = []): void; } PK Cv�Zۛ�� � AbstractLogger.phpnu �[��� <?php namespace Psr\Log; /** * This is a simple Logger implementation that other Loggers can inherit from. * * It simply delegates all log-level-specific methods to the `log` method to * reduce boilerplate code that a simple Logger that does the same thing with * messages regardless of the error level has to implement. */ abstract class AbstractLogger implements LoggerInterface { use LoggerTrait; } PK Cv�Z�>� � LoggerAwareTrait.phpnu �[��� <?php namespace Psr\Log; /** * Basic Implementation of LoggerAwareInterface. */ trait LoggerAwareTrait { /** * The logger instance. * * @var LoggerInterface|null */ protected ?LoggerInterface $logger = null; /** * Sets a logger. * * @param LoggerInterface $logger */ public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; } } PK �v�Z,��Y Y Iterators/Mapper.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Iterators; /** * Applies the callback to the elements of the inner iterator. */ class Mapper extends \IteratorIterator { /** @var callable */ private $callback; public function __construct(\Traversable $iterator, callable $callback) { parent::__construct($iterator); $this->callback = $callback; } public function current(): mixed { return ($this->callback)(parent::current(), parent::key()); } } PK �v�Z��2�� � Iterators/CachingIterator.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Iterators; use Nette; /** * Smarter caching iterator. * * @property-read bool $first * @property-read bool $last * @property-read bool $empty * @property-read bool $odd * @property-read bool $even * @property-read int $counter * @property-read mixed $nextKey * @property-read mixed $nextValue */ class CachingIterator extends \CachingIterator implements \Countable { use Nette\SmartObject; private int $counter = 0; public function __construct($iterator) { if (is_array($iterator) || $iterator instanceof \stdClass) { $iterator = new \ArrayIterator($iterator); } elseif ($iterator instanceof \IteratorAggregate) { do { $iterator = $iterator->getIterator(); } while ($iterator instanceof \IteratorAggregate); assert($iterator instanceof \Iterator); } elseif ($iterator instanceof \Iterator) { } elseif ($iterator instanceof \Traversable) { $iterator = new \IteratorIterator($iterator); } else { throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, is_object($iterator) ? $iterator::class : gettype($iterator))); } parent::__construct($iterator, 0); } /** * Is the current element the first one? */ public function isFirst(?int $gridWidth = null): bool { return $this->counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0); } /** * Is the current element the last one? */ public function isLast(?int $gridWidth = null): bool { return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0); } /** * Is the iterator empty? */ public function isEmpty(): bool { return $this->counter === 0; } /** * Is the counter odd? */ public function isOdd(): bool { return $this->counter % 2 === 1; } /** * Is the counter even? */ public function isEven(): bool { return $this->counter % 2 === 0; } /** * Returns the counter. */ public function getCounter(): int { return $this->counter; } /** * Returns the count of elements. */ public function count(): int { $inner = $this->getInnerIterator(); if ($inner instanceof \Countable) { return $inner->count(); } else { throw new Nette\NotSupportedException('Iterator is not countable.'); } } /** * Forwards to the next element. */ public function next(): void { parent::next(); if (parent::valid()) { $this->counter++; } } /** * Rewinds the Iterator. */ public function rewind(): void { parent::rewind(); $this->counter = parent::valid() ? 1 : 0; } /** * Returns the next key. */ public function getNextKey(): mixed { return $this->getInnerIterator()->key(); } /** * Returns the next element. */ public function getNextValue(): mixed { return $this->getInnerIterator()->current(); } } PK �v�Z��A� � Translator.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Localization; /** * Translator adapter. */ interface Translator { /** * Translates the given string. */ function translate(string|\Stringable $message, mixed ...$parameters): string|\Stringable; } interface_exists(ITranslator::class); PK �v�Z����� � exceptions.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette; /** * The exception that is thrown when the value of an argument is * outside the allowable range of values as defined by the invoked method. */ class ArgumentOutOfRangeException extends \InvalidArgumentException { } /** * The exception that is thrown when a method call is invalid for the object's * current state, method has been invoked at an illegal or inappropriate time. */ class InvalidStateException extends \RuntimeException { } /** * The exception that is thrown when a requested method or operation is not implemented. */ class NotImplementedException extends \LogicException { } /** * The exception that is thrown when an invoked method is not supported. For scenarios where * it is sometimes possible to perform the requested operation, see InvalidStateException. */ class NotSupportedException extends \LogicException { } /** * The exception that is thrown when a requested method or operation is deprecated. */ class DeprecatedException extends NotSupportedException { } /** * The exception that is thrown when accessing a class member (property or method) fails. */ class MemberAccessException extends \Error { } /** * The exception that is thrown when an I/O error occurs. */ class IOException extends \RuntimeException { } /** * The exception that is thrown when accessing a file that does not exist on disk. */ class FileNotFoundException extends IOException { } /** * The exception that is thrown when part of a file or directory cannot be found. */ class DirectoryNotFoundException extends IOException { } /** * The exception that is thrown when an argument does not match with the expected value. */ class InvalidArgumentException extends \InvalidArgumentException { } /** * The exception that is thrown when an illegal index was requested. */ class OutOfRangeException extends \OutOfRangeException { } /** * The exception that is thrown when a value (typically returned by function) does not match with the expected value. */ class UnexpectedValueException extends \UnexpectedValueException { } PK �v�Z�wX�� � SmartObject.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette; use Nette\Utils\ObjectHelpers; /** * Strict class for better experience. * - 'did you mean' hints * - access to undeclared members throws exceptions * - support for @property annotations * - support for calling event handlers stored in $onEvent via onEvent() */ trait SmartObject { /** * @return mixed * @throws MemberAccessException */ public function __call(string $name, array $args) { $class = static::class; if (ObjectHelpers::hasProperty($class, $name) === 'event') { // calling event handlers $handlers = $this->$name ?? null; if (is_iterable($handlers)) { foreach ($handlers as $handler) { $handler(...$args); } } elseif ($handlers !== null) { throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . gettype($handlers) . ' given.'); } return null; } ObjectHelpers::strictCall($class, $name); } /** * @throws MemberAccessException */ public static function __callStatic(string $name, array $args) { ObjectHelpers::strictStaticCall(static::class, $name); } /** * @return mixed * @throws MemberAccessException if the property is not defined. */ public function &__get(string $name) { $class = static::class; if ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property getter if (!($prop & 0b0001)) { throw new MemberAccessException("Cannot read a write-only property $class::\$$name."); } $m = ($prop & 0b0010 ? 'get' : 'is') . ucfirst($name); if ($prop & 0b10000) { $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call() $loc = isset($trace['file'], $trace['line']) ? " in $trace[file] on line $trace[line]" : ''; trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED); } if ($prop & 0b0100) { // return by reference return $this->$m(); } else { $val = $this->$m(); return $val; } } else { ObjectHelpers::strictGet($class, $name); } } /** * @throws MemberAccessException if the property is not defined or is read-only */ public function __set(string $name, mixed $value): void { $class = static::class; if (ObjectHelpers::hasProperty($class, $name)) { // unsetted property $this->$name = $value; } elseif ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property setter if (!($prop & 0b1000)) { throw new MemberAccessException("Cannot write to a read-only property $class::\$$name."); } $m = 'set' . ucfirst($name); if ($prop & 0b10000) { $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call() $loc = isset($trace['file'], $trace['line']) ? " in $trace[file] on line $trace[line]" : ''; trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED); } $this->$m($value); } else { ObjectHelpers::strictSet($class, $name); } } /** * @throws MemberAccessException */ public function __unset(string $name): void { $class = static::class; if (!ObjectHelpers::hasProperty($class, $name)) { throw new MemberAccessException("Cannot unset the property $class::\$$name."); } } public function __isset(string $name): bool { return isset(ObjectHelpers::getMagicProperties(static::class)[$name]); } } PK �v�Zr�k�� � compatibility.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; if (false) { /** @deprecated use Nette\HtmlStringable */ interface IHtmlString extends Nette\HtmlStringable { } } elseif (!interface_exists(IHtmlString::class)) { class_alias(Nette\HtmlStringable::class, IHtmlString::class); } namespace Nette\Localization; if (false) { /** @deprecated use Nette\Localization\Translator */ interface ITranslator extends Translator { } } elseif (!interface_exists(ITranslator::class)) { class_alias(Translator::class, ITranslator::class); } PK �v�Zl��y y StaticClass.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette; /** * Static class. */ trait StaticClass { /** * @return never * @throws \Error */ final public function __construct() { throw new \Error('Class ' . static::class . ' is static and cannot be instantiated.'); } /** * Call to undefined static method. * @throws MemberAccessException */ public static function __callStatic(string $name, array $args): mixed { Utils\ObjectHelpers::strictStaticCall(static::class, $name); } } PK �v�Zf��H H Utils/Floats.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Floating-point numbers comparison. */ class Floats { use Nette\StaticClass; private const Epsilon = 1e-10; public static function isZero(float $value): bool { return abs($value) < self::Epsilon; } public static function isInteger(float $value): bool { return abs(round($value) - $value) < self::Epsilon; } /** * Compare two floats. If $a < $b it returns -1, if they are equal it returns 0 and if $a > $b it returns 1 * @throws \LogicException if one of parameters is NAN */ public static function compare(float $a, float $b): int { if (is_nan($a) || is_nan($b)) { throw new \LogicException('Trying to compare NAN'); } elseif (!is_finite($a) && !is_finite($b) && $a === $b) { return 0; } $diff = abs($a - $b); if (($diff < self::Epsilon || ($diff / max(abs($a), abs($b)) < self::Epsilon))) { return 0; } return $a < $b ? -1 : 1; } /** * Returns true if $a = $b * @throws \LogicException if one of parameters is NAN */ public static function areEqual(float $a, float $b): bool { return self::compare($a, $b) === 0; } /** * Returns true if $a < $b * @throws \LogicException if one of parameters is NAN */ public static function isLessThan(float $a, float $b): bool { return self::compare($a, $b) < 0; } /** * Returns true if $a <= $b * @throws \LogicException if one of parameters is NAN */ public static function isLessThanOrEqualTo(float $a, float $b): bool { return self::compare($a, $b) <= 0; } /** * Returns true if $a > $b * @throws \LogicException if one of parameters is NAN */ public static function isGreaterThan(float $a, float $b): bool { return self::compare($a, $b) > 0; } /** * Returns true if $a >= $b * @throws \LogicException if one of parameters is NAN */ public static function isGreaterThanOrEqualTo(float $a, float $b): bool { return self::compare($a, $b) >= 0; } } PK �v�Z��U U Utils/Image.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Basic manipulation with images. Supported types are JPEG, PNG, GIF, WEBP, AVIF and BMP. * * <code> * $image = Image::fromFile('nette.jpg'); * $image->resize(150, 100); * $image->sharpen(); * $image->send(); * </code> * * @method Image affine(array $affine, array $clip = null) * @method array affineMatrixConcat(array $m1, array $m2) * @method array affineMatrixGet(int $type, mixed $options = null) * @method void alphaBlending(bool $on) * @method void antialias(bool $on) * @method void arc($x, $y, $w, $h, $start, $end, $color) * @method void char(int $font, $x, $y, string $char, $color) * @method void charUp(int $font, $x, $y, string $char, $color) * @method int colorAllocate($red, $green, $blue) * @method int colorAllocateAlpha($red, $green, $blue, $alpha) * @method int colorAt($x, $y) * @method int colorClosest($red, $green, $blue) * @method int colorClosestAlpha($red, $green, $blue, $alpha) * @method int colorClosestHWB($red, $green, $blue) * @method void colorDeallocate($color) * @method int colorExact($red, $green, $blue) * @method int colorExactAlpha($red, $green, $blue, $alpha) * @method void colorMatch(Image $image2) * @method int colorResolve($red, $green, $blue) * @method int colorResolveAlpha($red, $green, $blue, $alpha) * @method void colorSet($index, $red, $green, $blue) * @method array colorsForIndex($index) * @method int colorsTotal() * @method int colorTransparent($color = null) * @method void convolution(array $matrix, float $div, float $offset) * @method void copy(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH) * @method void copyMerge(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity) * @method void copyMergeGray(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity) * @method void copyResampled(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH) * @method void copyResized(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH) * @method Image cropAuto(int $mode = -1, float $threshold = .5, int $color = -1) * @method void ellipse($cx, $cy, $w, $h, $color) * @method void fill($x, $y, $color) * @method void filledArc($cx, $cy, $w, $h, $s, $e, $color, $style) * @method void filledEllipse($cx, $cy, $w, $h, $color) * @method void filledPolygon(array $points, $numPoints, $color) * @method void filledRectangle($x1, $y1, $x2, $y2, $color) * @method void fillToBorder($x, $y, $border, $color) * @method void filter($filtertype) * @method void flip(int $mode) * @method array ftText($size, $angle, $x, $y, $col, string $fontFile, string $text, array $extrainfo = null) * @method void gammaCorrect(float $inputgamma, float $outputgamma) * @method array getClip() * @method int interlace($interlace = null) * @method bool isTrueColor() * @method void layerEffect($effect) * @method void line($x1, $y1, $x2, $y2, $color) * @method void openPolygon(array $points, int $num_points, int $color) * @method void paletteCopy(Image $source) * @method void paletteToTrueColor() * @method void polygon(array $points, $numPoints, $color) * @method array psText(string $text, $font, $size, $color, $backgroundColor, $x, $y, $space = null, $tightness = null, float $angle = null, $antialiasSteps = null) * @method void rectangle($x1, $y1, $x2, $y2, $col) * @method mixed resolution(int $res_x = null, int $res_y = null) * @method Image rotate(float $angle, $backgroundColor) * @method void saveAlpha(bool $saveflag) * @method Image scale(int $newWidth, int $newHeight = -1, int $mode = IMG_BILINEAR_FIXED) * @method void setBrush(Image $brush) * @method void setClip(int $x1, int $y1, int $x2, int $y2) * @method void setInterpolation(int $method = IMG_BILINEAR_FIXED) * @method void setPixel($x, $y, $color) * @method void setStyle(array $style) * @method void setThickness($thickness) * @method void setTile(Image $tile) * @method void string($font, $x, $y, string $s, $col) * @method void stringUp($font, $x, $y, string $s, $col) * @method void trueColorToPalette(bool $dither, $ncolors) * @method array ttfText($size, $angle, $x, $y, $color, string $fontfile, string $text) * @property-read int $width * @property-read int $height * @property-read \GdImage $imageResource */ class Image { use Nette\SmartObject; /** Prevent from getting resized to a bigger size than the original */ public const ShrinkOnly = 0b0001; /** Resizes to a specified width and height without keeping aspect ratio */ public const Stretch = 0b0010; /** Resizes to fit into a specified width and height and preserves aspect ratio */ public const OrSmaller = 0b0000; /** Resizes while bounding the smaller dimension to the specified width or height and preserves aspect ratio */ public const OrBigger = 0b0100; /** Resizes to the smallest possible size to completely cover specified width and height and reserves aspect ratio */ public const Cover = 0b1000; /** @deprecated use Image::ShrinkOnly */ public const SHRINK_ONLY = self::ShrinkOnly; /** @deprecated use Image::Stretch */ public const STRETCH = self::Stretch; /** @deprecated use Image::OrSmaller */ public const FIT = self::OrSmaller; /** @deprecated use Image::OrBigger */ public const FILL = self::OrBigger; /** @deprecated use Image::Cover */ public const EXACT = self::Cover; /** @deprecated use Image::EmptyGIF */ public const EMPTY_GIF = self::EmptyGIF; /** image types */ public const JPEG = IMAGETYPE_JPEG, PNG = IMAGETYPE_PNG, GIF = IMAGETYPE_GIF, WEBP = IMAGETYPE_WEBP, AVIF = 19, // IMAGETYPE_AVIF, BMP = IMAGETYPE_BMP; public const EmptyGIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;"; private const Formats = [self::JPEG => 'jpeg', self::PNG => 'png', self::GIF => 'gif', self::WEBP => 'webp', self::AVIF => 'avif', self::BMP => 'bmp']; private \GdImage $image; /** * Returns RGB color (0..255) and transparency (0..127). */ public static function rgb(int $red, int $green, int $blue, int $transparency = 0): array { return [ 'red' => max(0, min(255, $red)), 'green' => max(0, min(255, $green)), 'blue' => max(0, min(255, $blue)), 'alpha' => max(0, min(127, $transparency)), ]; } /** * Reads an image from a file and returns its type in $type. * @throws Nette\NotSupportedException if gd extension is not loaded * @throws UnknownImageFileException if file not found or file type is not known */ public static function fromFile(string $file, ?int &$type = null): static { if (!extension_loaded('gd')) { throw new Nette\NotSupportedException('PHP extension GD is not loaded.'); } $type = self::detectTypeFromFile($file); if (!$type) { throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found."); } return self::invokeSafe('imagecreatefrom' . self::Formats[$type], $file, "Unable to open file '$file'.", __METHOD__); } /** * Reads an image from a string and returns its type in $type. * @throws Nette\NotSupportedException if gd extension is not loaded * @throws ImageException */ public static function fromString(string $s, ?int &$type = null): static { if (!extension_loaded('gd')) { throw new Nette\NotSupportedException('PHP extension GD is not loaded.'); } $type = self::detectTypeFromString($s); if (!$type) { throw new UnknownImageFileException('Unknown type of image.'); } return self::invokeSafe('imagecreatefromstring', $s, 'Unable to open image from string.', __METHOD__); } private static function invokeSafe(string $func, string $arg, string $message, string $callee): static { $errors = []; $res = Callback::invokeSafe($func, [$arg], function (string $message) use (&$errors): void { $errors[] = $message; }); if (!$res) { throw new ImageException($message . ' Errors: ' . implode(', ', $errors)); } elseif ($errors) { trigger_error($callee . '(): ' . implode(', ', $errors), E_USER_WARNING); } return new static($res); } /** * Creates a new true color image of the given dimensions. The default color is black. * @throws Nette\NotSupportedException if gd extension is not loaded */ public static function fromBlank(int $width, int $height, ?array $color = null): static { if (!extension_loaded('gd')) { throw new Nette\NotSupportedException('PHP extension GD is not loaded.'); } if ($width < 1 || $height < 1) { throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.'); } $image = imagecreatetruecolor($width, $height); if ($color) { $color += ['alpha' => 0]; $color = imagecolorresolvealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']); imagealphablending($image, false); imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color); imagealphablending($image, true); } return new static($image); } /** * Returns the type of image from file. */ public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int { [$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error return isset(self::Formats[$type]) ? $type : null; } /** * Returns the type of image from string. */ public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int { [$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error return isset(self::Formats[$type]) ? $type : null; } /** * Returns the file extension for the given `Image::XXX` constant. */ public static function typeToExtension(int $type): string { if (!isset(self::Formats[$type])) { throw new Nette\InvalidArgumentException("Unsupported image type '$type'."); } return self::Formats[$type]; } /** * Returns the `Image::XXX` constant for given file extension. */ public static function extensionToType(string $extension): int { $extensions = array_flip(self::Formats) + ['jpg' => self::JPEG]; $extension = strtolower($extension); if (!isset($extensions[$extension])) { throw new Nette\InvalidArgumentException("Unsupported file extension '$extension'."); } return $extensions[$extension]; } /** * Returns the mime type for the given `Image::XXX` constant. */ public static function typeToMimeType(int $type): string { return 'image/' . self::typeToExtension($type); } /** * Wraps GD image. */ public function __construct(\GdImage $image) { $this->setImageResource($image); imagesavealpha($image, true); } /** * Returns image width. */ public function getWidth(): int { return imagesx($this->image); } /** * Returns image height. */ public function getHeight(): int { return imagesy($this->image); } /** * Sets image resource. */ protected function setImageResource(\GdImage $image): static { $this->image = $image; return $this; } /** * Returns image GD resource. */ public function getImageResource(): \GdImage { return $this->image; } /** * Scales an image. Width and height accept pixels or percent. * @param self::OrSmaller|self::OrBigger|self::Stretch|self::Cover|self::ShrinkOnly $mode */ public function resize(int|string|null $width, int|string|null $height, int $mode = self::OrSmaller): static { if ($mode & self::Cover) { return $this->resize($width, $height, self::OrBigger)->crop('50%', '50%', $width, $height); } [$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $mode); if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize $newImage = static::fromBlank($newWidth, $newHeight, self::rgb(0, 0, 0, 127))->getImageResource(); imagecopyresampled( $newImage, $this->image, 0, 0, 0, 0, $newWidth, $newHeight, $this->getWidth(), $this->getHeight(), ); $this->image = $newImage; } if ($width < 0 || $height < 0) { imageflip($this->image, $width < 0 ? ($height < 0 ? IMG_FLIP_BOTH : IMG_FLIP_HORIZONTAL) : IMG_FLIP_VERTICAL); } return $this; } /** * Calculates dimensions of resized image. Width and height accept pixels or percent. * @param self::OrSmaller|self::OrBigger|self::Stretch|self::Cover|self::ShrinkOnly $mode */ public static function calculateSize( int $srcWidth, int $srcHeight, $newWidth, $newHeight, int $mode = self::OrSmaller, ): array { if ($newWidth === null) { } elseif (self::isPercent($newWidth)) { $newWidth = (int) round($srcWidth / 100 * abs($newWidth)); $percents = true; } else { $newWidth = abs($newWidth); } if ($newHeight === null) { } elseif (self::isPercent($newHeight)) { $newHeight = (int) round($srcHeight / 100 * abs($newHeight)); $mode |= empty($percents) ? 0 : self::Stretch; } else { $newHeight = abs($newHeight); } if ($mode & self::Stretch) { // non-proportional if (!$newWidth || !$newHeight) { throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.'); } if ($mode & self::ShrinkOnly) { $newWidth = min($srcWidth, $newWidth); $newHeight = min($srcHeight, $newHeight); } } else { // proportional if (!$newWidth && !$newHeight) { throw new Nette\InvalidArgumentException('At least width or height must be specified.'); } $scale = []; if ($newWidth > 0) { // fit width $scale[] = $newWidth / $srcWidth; } if ($newHeight > 0) { // fit height $scale[] = $newHeight / $srcHeight; } if ($mode & self::OrBigger) { $scale = [max($scale)]; } if ($mode & self::ShrinkOnly) { $scale[] = 1; } $scale = min($scale); $newWidth = (int) round($srcWidth * $scale); $newHeight = (int) round($srcHeight * $scale); } return [max($newWidth, 1), max($newHeight, 1)]; } /** * Crops image. Arguments accepts pixels or percent. */ public function crop(int|string $left, int|string $top, int|string $width, int|string $height): static { [$r['x'], $r['y'], $r['width'], $r['height']] = static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height); if (gd_info()['GD Version'] === 'bundled (2.1.0 compatible)') { $this->image = imagecrop($this->image, $r); imagesavealpha($this->image, true); } else { $newImage = static::fromBlank($r['width'], $r['height'], self::RGB(0, 0, 0, 127))->getImageResource(); imagecopy($newImage, $this->image, 0, 0, $r['x'], $r['y'], $r['width'], $r['height']); $this->image = $newImage; } return $this; } /** * Calculates dimensions of cutout in image. Arguments accepts pixels or percent. */ public static function calculateCutout( int $srcWidth, int $srcHeight, int|string $left, int|string $top, int|string $newWidth, int|string $newHeight, ): array { if (self::isPercent($newWidth)) { $newWidth = (int) round($srcWidth / 100 * $newWidth); } if (self::isPercent($newHeight)) { $newHeight = (int) round($srcHeight / 100 * $newHeight); } if (self::isPercent($left)) { $left = (int) round(($srcWidth - $newWidth) / 100 * $left); } if (self::isPercent($top)) { $top = (int) round(($srcHeight - $newHeight) / 100 * $top); } if ($left < 0) { $newWidth += $left; $left = 0; } if ($top < 0) { $newHeight += $top; $top = 0; } $newWidth = min($newWidth, $srcWidth - $left); $newHeight = min($newHeight, $srcHeight - $top); return [$left, $top, $newWidth, $newHeight]; } /** * Sharpens image a little bit. */ public function sharpen(): static { imageconvolution($this->image, [ // my magic numbers ;) [-1, -1, -1], [-1, 24, -1], [-1, -1, -1], ], 16, 0); return $this; } /** * Puts another image into this image. Left and top accepts pixels or percent. * @param int $opacity 0..100 */ public function place(self $image, int|string $left = 0, int|string $top = 0, int $opacity = 100): static { $opacity = max(0, min(100, $opacity)); if ($opacity === 0) { return $this; } $width = $image->getWidth(); $height = $image->getHeight(); if (self::isPercent($left)) { $left = (int) round(($this->getWidth() - $width) / 100 * $left); } if (self::isPercent($top)) { $top = (int) round(($this->getHeight() - $height) / 100 * $top); } $output = $input = $image->image; if ($opacity < 100) { $tbl = []; for ($i = 0; $i < 128; $i++) { $tbl[$i] = round(127 - (127 - $i) * $opacity / 100); } $output = imagecreatetruecolor($width, $height); imagealphablending($output, false); if (!$image->isTrueColor()) { $input = $output; imagefilledrectangle($output, 0, 0, $width, $height, imagecolorallocatealpha($output, 0, 0, 0, 127)); imagecopy($output, $image->image, 0, 0, 0, 0, $width, $height); } for ($x = 0; $x < $width; $x++) { for ($y = 0; $y < $height; $y++) { $c = \imagecolorat($input, $x, $y); $c = ($c & 0xFFFFFF) + ($tbl[$c >> 24] << 24); \imagesetpixel($output, $x, $y, $c); } } imagealphablending($output, true); } imagecopy( $this->image, $output, $left, $top, 0, 0, $width, $height, ); return $this; } /** * Saves image to the file. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). * @throws ImageException */ public function save(string $file, ?int $quality = null, ?int $type = null): void { $type ??= self::extensionToType(pathinfo($file, PATHINFO_EXTENSION)); $this->output($type, $quality, $file); } /** * Outputs image to string. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). */ public function toString(int $type = self::JPEG, ?int $quality = null): string { return Helpers::capture(function () use ($type, $quality): void { $this->output($type, $quality); }); } /** * Outputs image to string. */ public function __toString(): string { return $this->toString(); } /** * Outputs image to browser. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9). * @throws ImageException */ public function send(int $type = self::JPEG, ?int $quality = null): void { header('Content-Type: ' . self::typeToMimeType($type)); $this->output($type, $quality); } /** * Outputs image to browser or file. * @throws ImageException */ private function output(int $type, ?int $quality, ?string $file = null): void { switch ($type) { case self::JPEG: $quality = $quality === null ? 85 : max(0, min(100, $quality)); $success = @imagejpeg($this->image, $file, $quality); // @ is escalated to exception break; case self::PNG: $quality = $quality === null ? 9 : max(0, min(9, $quality)); $success = @imagepng($this->image, $file, $quality); // @ is escalated to exception break; case self::GIF: $success = @imagegif($this->image, $file); // @ is escalated to exception break; case self::WEBP: $quality = $quality === null ? 80 : max(0, min(100, $quality)); $success = @imagewebp($this->image, $file, $quality); // @ is escalated to exception break; case self::AVIF: $quality = $quality === null ? 30 : max(0, min(100, $quality)); $success = @imageavif($this->image, $file, $quality); // @ is escalated to exception break; case self::BMP: $success = @imagebmp($this->image, $file); // @ is escalated to exception break; default: throw new Nette\InvalidArgumentException("Unsupported image type '$type'."); } if (!$success) { throw new ImageException(Helpers::getLastError() ?: 'Unknown error'); } } /** * Call to undefined method. * @throws Nette\MemberAccessException */ public function __call(string $name, array $args): mixed { $function = 'image' . $name; if (!function_exists($function)) { ObjectHelpers::strictCall(static::class, $name); } foreach ($args as $key => $value) { if ($value instanceof self) { $args[$key] = $value->getImageResource(); } elseif (is_array($value) && isset($value['red'])) { // rgb $args[$key] = imagecolorallocatealpha( $this->image, $value['red'], $value['green'], $value['blue'], $value['alpha'], ) ?: imagecolorresolvealpha( $this->image, $value['red'], $value['green'], $value['blue'], $value['alpha'], ); } } $res = $function($this->image, ...$args); return $res instanceof \GdImage ? $this->setImageResource($res) : $res; } public function __clone() { ob_start(function () {}); imagepng($this->image, null, 0); $this->setImageResource(imagecreatefromstring(ob_get_clean())); } private static function isPercent(int|string &$num): bool { if (is_string($num) && str_ends_with($num, '%')) { $num = (float) substr($num, 0, -1); return true; } elseif (is_int($num) || $num === (string) (int) $num) { $num = (int) $num; return false; } throw new Nette\InvalidArgumentException("Expected dimension in int|string, '$num' given."); } /** * Prevents serialization. */ public function __sleep(): array { throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.'); } } PK �v�Z�O�!* !* Utils/Arrays.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use JetBrains\PhpStorm\Language; use Nette; use function is_array, is_int, is_object, count; /** * Array tools library. */ class Arrays { use Nette\StaticClass; /** * Returns item from array. If it does not exist, it throws an exception, unless a default value is set. * @template T * @param array<T> $array * @param array-key|array-key[] $key * @param ?T $default * @return ?T * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided */ public static function get(array $array, string|int|array $key, mixed $default = null): mixed { foreach (is_array($key) ? $key : [$key] as $k) { if (is_array($array) && array_key_exists($k, $array)) { $array = $array[$k]; } else { if (func_num_args() < 3) { throw new Nette\InvalidArgumentException("Missing item '$k'."); } return $default; } } return $array; } /** * Returns reference to array item. If the index does not exist, new one is created with value null. * @template T * @param array<T> $array * @param array-key|array-key[] $key * @return ?T * @throws Nette\InvalidArgumentException if traversed item is not an array */ public static function &getRef(array &$array, string|int|array $key): mixed { foreach (is_array($key) ? $key : [$key] as $k) { if (is_array($array) || $array === null) { $array = &$array[$k]; } else { throw new Nette\InvalidArgumentException('Traversed item is not an array.'); } } return $array; } /** * Recursively merges two fields. It is useful, for example, for merging tree structures. It behaves as * the + operator for array, ie. it adds a key/value pair from the second array to the first one and retains * the value from the first array in the case of a key collision. * @template T1 * @template T2 * @param array<T1> $array1 * @param array<T2> $array2 * @return array<T1|T2> */ public static function mergeTree(array $array1, array $array2): array { $res = $array1 + $array2; foreach (array_intersect_key($array1, $array2) as $k => $v) { if (is_array($v) && is_array($array2[$k])) { $res[$k] = self::mergeTree($v, $array2[$k]); } } return $res; } /** * Returns zero-indexed position of given array key. Returns null if key is not found. */ public static function getKeyOffset(array $array, string|int $key): ?int { return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), true)); } /** * @deprecated use getKeyOffset() */ public static function searchKey(array $array, $key): ?int { return self::getKeyOffset($array, $key); } /** * Tests an array for the presence of value. */ public static function contains(array $array, mixed $value): bool { return in_array($value, $array, true); } /** * Returns the first item from the array or null if array is empty. * @template T * @param array<T> $array * @return ?T */ public static function first(array $array): mixed { return count($array) ? reset($array) : null; } /** * Returns the last item from the array or null if array is empty. * @template T * @param array<T> $array * @return ?T */ public static function last(array $array): mixed { return count($array) ? end($array) : null; } /** * Inserts the contents of the $inserted array into the $array immediately after the $key. * If $key is null (or does not exist), it is inserted at the beginning. */ public static function insertBefore(array &$array, string|int|null $key, array $inserted): void { $offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key); $array = array_slice($array, 0, $offset, true) + $inserted + array_slice($array, $offset, count($array), true); } /** * Inserts the contents of the $inserted array into the $array before the $key. * If $key is null (or does not exist), it is inserted at the end. */ public static function insertAfter(array &$array, string|int|null $key, array $inserted): void { if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) { $offset = count($array) - 1; } $array = array_slice($array, 0, $offset + 1, true) + $inserted + array_slice($array, $offset + 1, count($array), true); } /** * Renames key in array. */ public static function renameKey(array &$array, string|int $oldKey, string|int $newKey): bool { $offset = self::getKeyOffset($array, $oldKey); if ($offset === null) { return false; } $val = &$array[$oldKey]; $keys = array_keys($array); $keys[$offset] = $newKey; $array = array_combine($keys, $array); $array[$newKey] = &$val; return true; } /** * Returns only those array items, which matches a regular expression $pattern. * @param string[] $array * @return string[] */ public static function grep( array $array, #[Language('RegExp')] string $pattern, bool|int $invert = false, ): array { $flags = $invert ? PREG_GREP_INVERT : 0; return Strings::pcre('preg_grep', [$pattern, $array, $flags]); } /** * Transforms multidimensional array to flat array. */ public static function flatten(array $array, bool $preserveKeys = false): array { $res = []; $cb = $preserveKeys ? function ($v, $k) use (&$res): void { $res[$k] = $v; } : function ($v) use (&$res): void { $res[] = $v; }; array_walk_recursive($array, $cb); return $res; } /** * Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list. */ public static function isList(mixed $value): bool { return is_array($value) && (PHP_VERSION_ID < 80100 ? !$value || array_keys($value) === range(0, count($value) - 1) : array_is_list($value) ); } /** * Reformats table to associative tree. Path looks like 'field|field[]field->field=field'. * @param string|string[] $path */ public static function associate(array $array, $path): array|\stdClass { $parts = is_array($path) ? $path : preg_split('#(\[\]|->|=|\|)#', $path, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); if (!$parts || $parts === ['->'] || $parts[0] === '=' || $parts[0] === '|') { throw new Nette\InvalidArgumentException("Invalid path '$path'."); } $res = $parts[0] === '->' ? new \stdClass : []; foreach ($array as $rowOrig) { $row = (array) $rowOrig; $x = &$res; for ($i = 0; $i < count($parts); $i++) { $part = $parts[$i]; if ($part === '[]') { $x = &$x[]; } elseif ($part === '=') { if (isset($parts[++$i])) { $x = $row[$parts[$i]]; $row = null; } } elseif ($part === '->') { if (isset($parts[++$i])) { if ($x === null) { $x = new \stdClass; } $x = &$x->{$row[$parts[$i]]}; } else { $row = is_object($rowOrig) ? $rowOrig : (object) $row; } } elseif ($part !== '|') { $x = &$x[(string) $row[$part]]; } } if ($x === null) { $x = $row; } } return $res; } /** * Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling. */ public static function normalize(array $array, mixed $filling = null): array { $res = []; foreach ($array as $k => $v) { $res[is_int($k) ? $v : $k] = is_int($k) ? $filling : $v; } return $res; } /** * Returns and removes the value of an item from an array. If it does not exist, it throws an exception, * or returns $default, if provided. * @template T * @param array<T> $array * @param ?T $default * @return ?T * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided */ public static function pick(array &$array, string|int $key, mixed $default = null): mixed { if (array_key_exists($key, $array)) { $value = $array[$key]; unset($array[$key]); return $value; } elseif (func_num_args() < 3) { throw new Nette\InvalidArgumentException("Missing item '$key'."); } else { return $default; } } /** * Tests whether at least one element in the array passes the test implemented by the * provided callback with signature `function ($value, $key, array $array): bool`. */ public static function some(iterable $array, callable $callback): bool { foreach ($array as $k => $v) { if ($callback($v, $k, $array)) { return true; } } return false; } /** * Tests whether all elements in the array pass the test implemented by the provided function, * which has the signature `function ($value, $key, array $array): bool`. */ public static function every(iterable $array, callable $callback): bool { foreach ($array as $k => $v) { if (!$callback($v, $k, $array)) { return false; } } return true; } /** * Calls $callback on all elements in the array and returns the array of return values. * The callback has the signature `function ($value, $key, array $array): bool`. */ public static function map(iterable $array, callable $callback): array { $res = []; foreach ($array as $k => $v) { $res[$k] = $callback($v, $k, $array); } return $res; } /** * Invokes all callbacks and returns array of results. * @param callable[] $callbacks */ public static function invoke(iterable $callbacks, ...$args): array { $res = []; foreach ($callbacks as $k => $cb) { $res[$k] = $cb(...$args); } return $res; } /** * Invokes method on every object in an array and returns array of results. * @param object[] $objects */ public static function invokeMethod(iterable $objects, string $method, ...$args): array { $res = []; foreach ($objects as $k => $obj) { $res[$k] = $obj->$method(...$args); } return $res; } /** * Copies the elements of the $array array to the $object object and then returns it. * @template T of object * @param T $object * @return T */ public static function toObject(iterable $array, object $object): object { foreach ($array as $k => $v) { $object->$k = $v; } return $object; } /** * Converts value to array key. */ public static function toKey(mixed $value): int|string { return key([$value => null]); } /** * Returns copy of the $array where every item is converted to string * and prefixed by $prefix and suffixed by $suffix. * @param string[] $array * @return string[] */ public static function wrap(array $array, string $prefix = '', string $suffix = ''): array { $res = []; foreach ($array as $k => $v) { $res[$k] = $prefix . $v . $suffix; } return $res; } } PK �v�Z8��� � Utils/Type.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * PHP type reflection. */ final class Type { /** @var array<int, string|self> */ private array $types; private bool $simple; private string $kind; // | & /** * Creates a Type object based on reflection. Resolves self, static and parent to the actual class name. * If the subject has no type, it returns null. */ public static function fromReflection( \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection, ): ?self { $type = $reflection instanceof \ReflectionFunctionAbstract ? $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null) : $reflection->getType(); return $type ? self::fromReflectionType($type, $reflection, true) : null; } private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject): self|string { if ($type instanceof \ReflectionNamedType) { $name = self::resolve($type->getName(), $of); return $asObject ? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name]) : $name; } elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { return new self( array_map(fn($t) => self::fromReflectionType($t, $of, false), $type->getTypes()), $type instanceof \ReflectionUnionType ? '|' : '&', ); } else { throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of)); } } /** * Creates the Type object according to the text notation. */ public static function fromString(string $type): self { if (!Validators::isTypeDeclaration($type)) { throw new Nette\InvalidArgumentException("Invalid type '$type'."); } if ($type[0] === '?') { return new self([substr($type, 1), 'null']); } $unions = []; foreach (explode('|', $type) as $part) { $part = explode('&', trim($part, '()')); $unions[] = count($part) === 1 ? $part[0] : new self($part, '&'); } return count($unions) === 1 && $unions[0] instanceof self ? $unions[0] : new self($unions); } /** * Resolves 'self', 'static' and 'parent' to the actual class name. */ public static function resolve( string $type, \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of, ): string { $lower = strtolower($type); if ($of instanceof \ReflectionFunction) { return $type; } elseif ($lower === 'self' || $lower === 'static') { return $of->getDeclaringClass()->name; } elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) { return $of->getDeclaringClass()->getParentClass()->name; } else { return $type; } } private function __construct(array $types, string $kind = '|') { $o = array_search('null', $types, true); if ($o !== false) { // null as last array_splice($types, $o, 1); $types[] = 'null'; } $this->types = $types; $this->simple = is_string($types[0]) && ($types[1] ?? 'null') === 'null'; $this->kind = count($types) > 1 ? $kind : ''; } public function __toString(): string { $multi = count($this->types) > 1; if ($this->simple) { return ($multi ? '?' : '') . $this->types[0]; } $res = []; foreach ($this->types as $type) { $res[] = $type instanceof self && $multi ? "($type)" : $type; } return implode($this->kind, $res); } /** * Returns the array of subtypes that make up the compound type as strings. * @return array<int, string|string[]> */ public function getNames(): array { return array_map(fn($t) => $t instanceof self ? $t->getNames() : $t, $this->types); } /** * Returns the array of subtypes that make up the compound type as Type objects: * @return self[] */ public function getTypes(): array { return array_map(fn($t) => $t instanceof self ? $t : new self([$t]), $this->types); } /** * Returns the type name for simple types, otherwise null. */ public function getSingleName(): ?string { return $this->simple ? $this->types[0] : null; } /** * Returns true whether it is a union type. */ public function isUnion(): bool { return $this->kind === '|'; } /** * Returns true whether it is an intersection type. */ public function isIntersection(): bool { return $this->kind === '&'; } /** * Returns true whether it is a simple type. Single nullable types are also considered to be simple types. */ public function isSimple(): bool { return $this->simple; } /** @deprecated use isSimple() */ public function isSingle(): bool { return $this->simple; } /** * Returns true whether the type is both a simple and a PHP built-in type. */ public function isBuiltin(): bool { return $this->simple && Validators::isBuiltinType($this->types[0]); } /** * Returns true whether the type is both a simple and a class name. */ public function isClass(): bool { return $this->simple && !Validators::isBuiltinType($this->types[0]); } /** * Determines if type is special class name self/parent/static. */ public function isClassKeyword(): bool { return $this->simple && Validators::isClassKeyword($this->types[0]); } /** * Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter. */ public function allows(string $subtype): bool { if ($this->types === ['mixed']) { return true; } $subtype = self::fromString($subtype); return $subtype->isUnion() ? Arrays::every($subtype->types, fn($t) => $this->allows2($t instanceof self ? $t->types : [$t])) : $this->allows2($subtype->types); } private function allows2(array $subtypes): bool { return $this->isUnion() ? Arrays::some($this->types, fn($t) => $this->allows3($t instanceof self ? $t->types : [$t], $subtypes)) : $this->allows3($this->types, $subtypes); } private function allows3(array $types, array $subtypes): bool { return Arrays::every( $types, fn($type) => Arrays::some( $subtypes, fn($subtype) => Validators::isBuiltinType($type) ? strcasecmp($type, $subtype) === 0 : is_a($subtype, $type, true) ) ); } } PK �v�Z�s�vW( W( Utils/Validators.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Validation utilities. */ class Validators { use Nette\StaticClass; private const BuiltinTypes = [ 'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1, 'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1, 'never' => 1, 'true' => 1, ]; /** @var array<string,?callable> */ protected static $validators = [ // PHP types 'array' => 'is_array', 'bool' => 'is_bool', 'boolean' => 'is_bool', 'float' => 'is_float', 'int' => 'is_int', 'integer' => 'is_int', 'null' => 'is_null', 'object' => 'is_object', 'resource' => 'is_resource', 'scalar' => 'is_scalar', 'string' => 'is_string', // pseudo-types 'callable' => [self::class, 'isCallable'], 'iterable' => 'is_iterable', 'list' => [Arrays::class, 'isList'], 'mixed' => [self::class, 'isMixed'], 'none' => [self::class, 'isNone'], 'number' => [self::class, 'isNumber'], 'numeric' => [self::class, 'isNumeric'], 'numericint' => [self::class, 'isNumericInt'], // string patterns 'alnum' => 'ctype_alnum', 'alpha' => 'ctype_alpha', 'digit' => 'ctype_digit', 'lower' => 'ctype_lower', 'pattern' => null, 'space' => 'ctype_space', 'unicode' => [self::class, 'isUnicode'], 'upper' => 'ctype_upper', 'xdigit' => 'ctype_xdigit', // syntax validation 'email' => [self::class, 'isEmail'], 'identifier' => [self::class, 'isPhpIdentifier'], 'uri' => [self::class, 'isUri'], 'url' => [self::class, 'isUrl'], // environment validation 'class' => 'class_exists', 'interface' => 'interface_exists', 'directory' => 'is_dir', 'file' => 'is_file', 'type' => [self::class, 'isType'], ]; /** @var array<string,callable> */ protected static $counters = [ 'string' => 'strlen', 'unicode' => [Strings::class, 'length'], 'array' => 'count', 'list' => 'count', 'alnum' => 'strlen', 'alpha' => 'strlen', 'digit' => 'strlen', 'lower' => 'strlen', 'space' => 'strlen', 'upper' => 'strlen', 'xdigit' => 'strlen', ]; /** * Verifies that the value is of expected types separated by pipe. * @throws AssertionException */ public static function assert(mixed $value, string $expected, string $label = 'variable'): void { if (!static::is($value, $expected)) { $expected = str_replace(['|', ':'], [' or ', ' in range '], $expected); $translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null']; $type = $translate[gettype($value)] ?? gettype($value); if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) { $type .= ' ' . var_export($value, true); } elseif (is_object($value)) { $type .= ' ' . $value::class; } throw new AssertionException("The $label expects to be $expected, $type given."); } } /** * Verifies that element $key in array is of expected types separated by pipe. * @param mixed[] $array * @throws AssertionException */ public static function assertField( array $array, $key, ?string $expected = null, string $label = "item '%' in array", ): void { if (!array_key_exists($key, $array)) { throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.'); } elseif ($expected) { static::assert($array[$key], $expected, str_replace('%', $key, $label)); } } /** * Verifies that the value is of expected types separated by pipe. */ public static function is(mixed $value, string $expected): bool { foreach (explode('|', $expected) as $item) { if (str_ends_with($item, '[]')) { if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) { return true; } continue; } elseif (str_starts_with($item, '?')) { $item = substr($item, 1); if ($value === null) { return true; } } [$type] = $item = explode(':', $item, 2); if (isset(static::$validators[$type])) { try { if (!static::$validators[$type]($value)) { continue; } } catch (\TypeError $e) { continue; } } elseif ($type === 'pattern') { if (Strings::match($value, '|^' . ($item[1] ?? '') . '$|D')) { return true; } continue; } elseif (!$value instanceof $type) { continue; } if (isset($item[1])) { $length = $value; if (isset(static::$counters[$type])) { $length = static::$counters[$type]($value); } $range = explode('..', $item[1]); if (!isset($range[1])) { $range[1] = $range[0]; } if (($range[0] !== '' && $length < $range[0]) || ($range[1] !== '' && $length > $range[1])) { continue; } } return true; } return false; } /** * Finds whether all values are of expected types separated by pipe. * @param mixed[] $values */ public static function everyIs(iterable $values, string $expected): bool { foreach ($values as $value) { if (!static::is($value, $expected)) { return false; } } return true; } /** * Checks if the value is an integer or a float. */ public static function isNumber(mixed $value): bool { return is_int($value) || is_float($value); } /** * Checks if the value is an integer or a integer written in a string. */ public static function isNumericInt(mixed $value): bool { return is_int($value) || (is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value)); } /** * Checks if the value is a number or a number written in a string. */ public static function isNumeric(mixed $value): bool { return is_float($value) || is_int($value) || (is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value)); } /** * Checks if the value is a syntactically correct callback. */ public static function isCallable(mixed $value): bool { return $value && is_callable($value, true); } /** * Checks if the value is a valid UTF-8 string. */ public static function isUnicode(mixed $value): bool { return is_string($value) && preg_match('##u', $value); } /** * Checks if the value is 0, '', false or null. */ public static function isNone(mixed $value): bool { return $value == null; // intentionally == } /** @internal */ public static function isMixed(): bool { return true; } /** * Checks if a variable is a zero-based integer indexed array. * @deprecated use Nette\Utils\Arrays::isList */ public static function isList(mixed $value): bool { return Arrays::isList($value); } /** * Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null). * Numbers, strings and DateTime objects can be compared. */ public static function isInRange(mixed $value, array $range): bool { if ($value === null || !(isset($range[0]) || isset($range[1]))) { return false; } $limit = $range[0] ?? $range[1]; if (is_string($limit)) { $value = (string) $value; } elseif ($limit instanceof \DateTimeInterface) { if (!$value instanceof \DateTimeInterface) { return false; } } elseif (is_numeric($value)) { $value *= 1; } else { return false; } return (!isset($range[0]) || ($value >= $range[0])) && (!isset($range[1]) || ($value <= $range[1])); } /** * Checks if the value is a valid email address. It does not verify that the domain actually exists, only the syntax is verified. */ public static function isEmail(string $value): bool { $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part $alpha = "a-z\x80-\xFF"; // superset of IDN return (bool) preg_match(<<<XX (^(?n) ("([ !#-[\\]-~]*|\\\\[ -~])+"|$atom+(\\.$atom+)*) # quoted or unquoted @ ([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034 [$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain $)Dix XX, $value); } /** * Checks if the value is a valid URL address. */ public static function isUrl(string $value): bool { $alpha = "a-z\x80-\xFF"; return (bool) preg_match(<<<XX (^(?n) https?://( (([-_0-9$alpha]+\\.)* # subdomain [0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain [$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain |\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4 |\\[[0-9a-f:]{3,39}\\] # IPv6 )(:\\d{1,5})? # port (/\\S*)? # path (\\?\\S*)? # query (\\#\\S*)? # fragment $)Dix XX, $value); } /** * Checks if the value is a valid URI address, that is, actually a string beginning with a syntactically valid schema. */ public static function isUri(string $value): bool { return (bool) preg_match('#^[a-z\d+\.-]+:\S+$#Di', $value); } /** * Checks whether the input is a class, interface or trait. * @deprecated */ public static function isType(string $type): bool { return class_exists($type) || interface_exists($type) || trait_exists($type); } /** * Checks whether the input is a valid PHP identifier. */ public static function isPhpIdentifier(string $value): bool { return preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $value) === 1; } /** * Determines if type is PHP built-in type. Otherwise, it is the class name. */ public static function isBuiltinType(string $type): bool { return isset(self::BuiltinTypes[strtolower($type)]); } /** * Determines if type is special class name self/parent/static. */ public static function isClassKeyword(string $name): bool { return (bool) preg_match('#^(self|parent|static)$#Di', $name); } /** * Checks whether the given type declaration is syntactically valid. */ public static function isTypeDeclaration(string $type): bool { return (bool) preg_match(<<<'XX' ~((?n) \?? (?<type> \\? (?<name> [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) | (?<intersection> (?&type) (& (?&type))+ ) | (?<upart> (?&type) | \( (?&intersection) \) ) (\| (?&upart))+ )$~xAD XX, $type); } } PK �v�Z�d� Utils/exceptions.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; /** * The exception that is thrown when an image error occurs. */ class ImageException extends \Exception { } /** * The exception that indicates invalid image file. */ class UnknownImageFileException extends ImageException { } /** * The exception that indicates error of JSON encoding/decoding. */ class JsonException extends \JsonException { } /** * The exception that indicates error of the last Regexp execution. */ class RegexpException extends \Exception { } /** * The exception that indicates assertion error. */ class AssertionException extends \Exception { } PK �v�Zں�� Utils/ArrayHash.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Provides objects to work as array. * @template T */ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate { /** * Transforms array to ArrayHash. * @param array<T> $array */ public static function from(array $array, bool $recursive = true): static { $obj = new static; foreach ($array as $key => $value) { $obj->$key = $recursive && is_array($value) ? static::from($value, true) : $value; } return $obj; } /** * Returns an iterator over all items. * @return \Iterator<int|string, T> */ public function &getIterator(): \Iterator { foreach ((array) $this as $key => $foo) { yield $key => $this->$key; } } /** * Returns items count. */ public function count(): int { return count((array) $this); } /** * Replaces or appends a item. * @param string|int $key * @param T $value */ public function offsetSet($key, $value): void { if (!is_scalar($key)) { // prevents null throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', gettype($key))); } $this->$key = $value; } /** * Returns a item. * @param string|int $key * @return T */ #[\ReturnTypeWillChange] public function offsetGet($key) { return $this->$key; } /** * Determines whether a item exists. * @param string|int $key */ public function offsetExists($key): bool { return isset($this->$key); } /** * Removes the element from this list. * @param string|int $key */ public function offsetUnset($key): void { unset($this->$key); } } PK �v�Z��� � Utils/DateTime.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * DateTime. */ class DateTime extends \DateTime implements \JsonSerializable { use Nette\SmartObject; /** minute in seconds */ public const MINUTE = 60; /** hour in seconds */ public const HOUR = 60 * self::MINUTE; /** day in seconds */ public const DAY = 24 * self::HOUR; /** week in seconds */ public const WEEK = 7 * self::DAY; /** average month in seconds */ public const MONTH = 2_629_800; /** average year in seconds */ public const YEAR = 31_557_600; /** * Creates a DateTime object from a string, UNIX timestamp, or other DateTimeInterface object. * @throws \Exception if the date and time are not valid. */ public static function from(string|int|\DateTimeInterface|null $time): static { if ($time instanceof \DateTimeInterface) { return new static($time->format('Y-m-d H:i:s.u'), $time->getTimezone()); } elseif (is_numeric($time)) { if ($time <= self::YEAR) { $time += time(); } return (new static('@' . $time))->setTimezone(new \DateTimeZone(date_default_timezone_get())); } else { // textual or null return new static((string) $time); } } /** * Creates DateTime object. * @throws Nette\InvalidArgumentException if the date and time are not valid. */ public static function fromParts( int $year, int $month, int $day, int $hour = 0, int $minute = 0, float $second = 0.0, ): static { $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second); if ( !checkdate($month, $day, $year) || $hour < 0 || $hour > 23 || $minute < 0 || $minute > 59 || $second < 0 || $second >= 60 ) { throw new Nette\InvalidArgumentException("Invalid date '$s'"); } return new static($s); } /** * Returns new DateTime object formatted according to the specified format. */ public static function createFromFormat( string $format, string $time, string|\DateTimeZone|null $timezone = null, ): static|false { if ($timezone === null) { $timezone = new \DateTimeZone(date_default_timezone_get()); } elseif (is_string($timezone)) { $timezone = new \DateTimeZone($timezone); } $date = parent::createFromFormat($format, $time, $timezone); return $date ? static::from($date) : false; } /** * Returns JSON representation in ISO 8601 (used by JavaScript). */ public function jsonSerialize(): string { return $this->format('c'); } /** * Returns the date and time in the format 'Y-m-d H:i:s'. */ public function __toString(): string { return $this->format('Y-m-d H:i:s'); } /** * Creates a copy with a modified time. */ public function modifyClone(string $modify = ''): static { $dolly = clone $this; return $modify ? $dolly->modify($modify) : $dolly; } } PK �v�Z�9 Utils/Helpers.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; class Helpers { /** * Executes a callback and returns the captured output as a string. */ public static function capture(callable $func): string { ob_start(function () {}); try { $func(); return ob_get_clean(); } catch (\Throwable $e) { ob_end_clean(); throw $e; } } /** * Returns the last occurred PHP error or an empty string if no error occurred. Unlike error_get_last(), * it is nit affected by the PHP directive html_errors and always returns text, not HTML. */ public static function getLastError(): string { $message = error_get_last()['message'] ?? ''; $message = ini_get('html_errors') ? Html::htmlToText($message) : $message; $message = preg_replace('#^\w+\(.*?\): #', '', $message); return $message; } /** * Converts false to null, does not change other values. */ public static function falseToNull(mixed $value): mixed { return $value === false ? null : $value; } /** * Returns value clamped to the inclusive range of min and max. */ public static function clamp(int|float $value, int|float $min, int|float $max): int|float { if ($min > $max) { throw new Nette\InvalidArgumentException("Minimum ($min) is not less than maximum ($max)."); } return min(max($value, $min), $max); } /** * Looks for a string from possibilities that is most similar to value, but not the same (for 8-bit encoding). * @param string[] $possibilities */ public static function getSuggestion(array $possibilities, string $value): ?string { $best = null; $min = (strlen($value) / 4 + 1) * 10 + .1; foreach (array_unique($possibilities) as $item) { if ($item !== $value && ($len = levenshtein($item, $value, 10, 11, 10)) < $min) { $min = $len; $best = $item; } } return $best; } /** * Compares two values in the same way that PHP does. Recognizes operators: >, >=, <, <=, =, ==, ===, !=, !==, <> */ public static function compare(mixed $left, string $operator, mixed $right): bool { return match ($operator) { '>' => $left > $right, '>=' => $left >= $right, '<' => $left < $right, '<=' => $left <= $right, '=', '==' => $left == $right, '===' => $left === $right, '!=', '<>' => $left != $right, '!==' => $left !== $right, default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"), }; } } PK �v�Zs�B�L L Utils/Html.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; use Nette\HtmlStringable; use function is_array, is_float, is_object, is_string; /** * HTML helper. * * @property string|null $accept * @property string|null $accesskey * @property string|null $action * @property string|null $align * @property string|null $allow * @property string|null $alt * @property bool|null $async * @property string|null $autocapitalize * @property string|null $autocomplete * @property bool|null $autofocus * @property bool|null $autoplay * @property string|null $charset * @property bool|null $checked * @property string|null $cite * @property string|null $class * @property int|null $cols * @property int|null $colspan * @property string|null $content * @property bool|null $contenteditable * @property bool|null $controls * @property string|null $coords * @property string|null $crossorigin * @property string|null $data * @property string|null $datetime * @property string|null $decoding * @property bool|null $default * @property bool|null $defer * @property string|null $dir * @property string|null $dirname * @property bool|null $disabled * @property bool|null $download * @property string|null $draggable * @property string|null $dropzone * @property string|null $enctype * @property string|null $for * @property string|null $form * @property string|null $formaction * @property string|null $formenctype * @property string|null $formmethod * @property bool|null $formnovalidate * @property string|null $formtarget * @property string|null $headers * @property int|null $height * @property bool|null $hidden * @property float|null $high * @property string|null $href * @property string|null $hreflang * @property string|null $id * @property string|null $integrity * @property string|null $inputmode * @property bool|null $ismap * @property string|null $itemprop * @property string|null $kind * @property string|null $label * @property string|null $lang * @property string|null $list * @property bool|null $loop * @property float|null $low * @property float|null $max * @property int|null $maxlength * @property int|null $minlength * @property string|null $media * @property string|null $method * @property float|null $min * @property bool|null $multiple * @property bool|null $muted * @property string|null $name * @property bool|null $novalidate * @property bool|null $open * @property float|null $optimum * @property string|null $pattern * @property string|null $ping * @property string|null $placeholder * @property string|null $poster * @property string|null $preload * @property string|null $radiogroup * @property bool|null $readonly * @property string|null $rel * @property bool|null $required * @property bool|null $reversed * @property int|null $rows * @property int|null $rowspan * @property string|null $sandbox * @property string|null $scope * @property bool|null $selected * @property string|null $shape * @property int|null $size * @property string|null $sizes * @property string|null $slot * @property int|null $span * @property string|null $spellcheck * @property string|null $src * @property string|null $srcdoc * @property string|null $srclang * @property string|null $srcset * @property int|null $start * @property float|null $step * @property string|null $style * @property int|null $tabindex * @property string|null $target * @property string|null $title * @property string|null $translate * @property string|null $type * @property string|null $usemap * @property string|null $value * @property int|null $width * @property string|null $wrap * * @method self accept(?string $val) * @method self accesskey(?string $val, bool $state = null) * @method self action(?string $val) * @method self align(?string $val) * @method self allow(?string $val, bool $state = null) * @method self alt(?string $val) * @method self async(?bool $val) * @method self autocapitalize(?string $val) * @method self autocomplete(?string $val) * @method self autofocus(?bool $val) * @method self autoplay(?bool $val) * @method self charset(?string $val) * @method self checked(?bool $val) * @method self cite(?string $val) * @method self class(?string $val, bool $state = null) * @method self cols(?int $val) * @method self colspan(?int $val) * @method self content(?string $val) * @method self contenteditable(?bool $val) * @method self controls(?bool $val) * @method self coords(?string $val) * @method self crossorigin(?string $val) * @method self datetime(?string $val) * @method self decoding(?string $val) * @method self default(?bool $val) * @method self defer(?bool $val) * @method self dir(?string $val) * @method self dirname(?string $val) * @method self disabled(?bool $val) * @method self download(?bool $val) * @method self draggable(?string $val) * @method self dropzone(?string $val) * @method self enctype(?string $val) * @method self for(?string $val) * @method self form(?string $val) * @method self formaction(?string $val) * @method self formenctype(?string $val) * @method self formmethod(?string $val) * @method self formnovalidate(?bool $val) * @method self formtarget(?string $val) * @method self headers(?string $val, bool $state = null) * @method self height(?int $val) * @method self hidden(?bool $val) * @method self high(?float $val) * @method self hreflang(?string $val) * @method self id(?string $val) * @method self integrity(?string $val) * @method self inputmode(?string $val) * @method self ismap(?bool $val) * @method self itemprop(?string $val) * @method self kind(?string $val) * @method self label(?string $val) * @method self lang(?string $val) * @method self list(?string $val) * @method self loop(?bool $val) * @method self low(?float $val) * @method self max(?float $val) * @method self maxlength(?int $val) * @method self minlength(?int $val) * @method self media(?string $val) * @method self method(?string $val) * @method self min(?float $val) * @method self multiple(?bool $val) * @method self muted(?bool $val) * @method self name(?string $val) * @method self novalidate(?bool $val) * @method self open(?bool $val) * @method self optimum(?float $val) * @method self pattern(?string $val) * @method self ping(?string $val, bool $state = null) * @method self placeholder(?string $val) * @method self poster(?string $val) * @method self preload(?string $val) * @method self radiogroup(?string $val) * @method self readonly(?bool $val) * @method self rel(?string $val) * @method self required(?bool $val) * @method self reversed(?bool $val) * @method self rows(?int $val) * @method self rowspan(?int $val) * @method self sandbox(?string $val, bool $state = null) * @method self scope(?string $val) * @method self selected(?bool $val) * @method self shape(?string $val) * @method self size(?int $val) * @method self sizes(?string $val) * @method self slot(?string $val) * @method self span(?int $val) * @method self spellcheck(?string $val) * @method self src(?string $val) * @method self srcdoc(?string $val) * @method self srclang(?string $val) * @method self srcset(?string $val) * @method self start(?int $val) * @method self step(?float $val) * @method self style(?string $property, string $val = null) * @method self tabindex(?int $val) * @method self target(?string $val) * @method self title(?string $val) * @method self translate(?string $val) * @method self type(?string $val) * @method self usemap(?string $val) * @method self value(?string $val) * @method self width(?int $val) * @method self wrap(?string $val) */ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringable { use Nette\SmartObject; /** @var array<string, mixed> element's attributes */ public $attrs = []; /** void elements */ public static $emptyElements = [ 'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1, 'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1, 'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1, ]; /** @var array<int, HtmlStringable|string> nodes */ protected $children = []; /** element's name */ private string $name = ''; private bool $isEmpty = false; /** * Constructs new HTML element. * @param array|string $attrs element's attributes or plain text content */ public static function el(?string $name = null, array|string|null $attrs = null): static { $el = new static; $parts = explode(' ', (string) $name, 2); $el->setName($parts[0]); if (is_array($attrs)) { $el->attrs = $attrs; } elseif ($attrs !== null) { $el->setText($attrs); } if (isset($parts[1])) { foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\2|\s))?#i') as $m) { $el->attrs[$m[1]] = $m[3] ?? true; } } return $el; } /** * Returns an object representing HTML text. */ public static function fromHtml(string $html): static { return (new static)->setHtml($html); } /** * Returns an object representing plain text. */ public static function fromText(string $text): static { return (new static)->setText($text); } /** * Converts to HTML. */ final public function toHtml(): string { return $this->render(); } /** * Converts to plain text. */ final public function toText(): string { return $this->getText(); } /** * Converts given HTML code to plain text. */ public static function htmlToText(string $html): string { return html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8'); } /** * Changes element's name. */ final public function setName(string $name, ?bool $isEmpty = null): static { $this->name = $name; $this->isEmpty = $isEmpty ?? isset(static::$emptyElements[$name]); return $this; } /** * Returns element's name. */ final public function getName(): string { return $this->name; } /** * Is element empty? */ final public function isEmpty(): bool { return $this->isEmpty; } /** * Sets multiple attributes. */ public function addAttributes(array $attrs): static { $this->attrs = array_merge($this->attrs, $attrs); return $this; } /** * Appends value to element's attribute. */ public function appendAttribute(string $name, mixed $value, mixed $option = true): static { if (is_array($value)) { $prev = isset($this->attrs[$name]) ? (array) $this->attrs[$name] : []; $this->attrs[$name] = $value + $prev; } elseif ((string) $value === '') { $tmp = &$this->attrs[$name]; // appending empty value? -> ignore, but ensure it exists } elseif (!isset($this->attrs[$name]) || is_array($this->attrs[$name])) { // needs array $this->attrs[$name][$value] = $option; } else { $this->attrs[$name] = [$this->attrs[$name] => true, $value => $option]; } return $this; } /** * Sets element's attribute. */ public function setAttribute(string $name, mixed $value): static { $this->attrs[$name] = $value; return $this; } /** * Returns element's attribute. */ public function getAttribute(string $name): mixed { return $this->attrs[$name] ?? null; } /** * Unsets element's attribute. */ public function removeAttribute(string $name): static { unset($this->attrs[$name]); return $this; } /** * Unsets element's attributes. */ public function removeAttributes(array $attributes): static { foreach ($attributes as $name) { unset($this->attrs[$name]); } return $this; } /** * Overloaded setter for element's attribute. */ final public function __set(string $name, mixed $value): void { $this->attrs[$name] = $value; } /** * Overloaded getter for element's attribute. */ final public function &__get(string $name): mixed { return $this->attrs[$name]; } /** * Overloaded tester for element's attribute. */ final public function __isset(string $name): bool { return isset($this->attrs[$name]); } /** * Overloaded unsetter for element's attribute. */ final public function __unset(string $name): void { unset($this->attrs[$name]); } /** * Overloaded setter for element's attribute. */ final public function __call(string $m, array $args): mixed { $p = substr($m, 0, 3); if ($p === 'get' || $p === 'set' || $p === 'add') { $m = substr($m, 3); $m[0] = $m[0] | "\x20"; if ($p === 'get') { return $this->attrs[$m] ?? null; } elseif ($p === 'add') { $args[] = true; } } if (count($args) === 0) { // invalid } elseif (count($args) === 1) { // set $this->attrs[$m] = $args[0]; } else { // add $this->appendAttribute($m, $args[0], $args[1]); } return $this; } /** * Special setter for element's attribute. */ final public function href(string $path, array $query = []): static { if ($query) { $query = http_build_query($query, '', '&'); if ($query !== '') { $path .= '?' . $query; } } $this->attrs['href'] = $path; return $this; } /** * Setter for data-* attributes. Booleans are converted to 'true' resp. 'false'. */ public function data(string $name, mixed $value = null): static { if (func_num_args() === 1) { $this->attrs['data'] = $name; } else { $this->attrs["data-$name"] = is_bool($value) ? json_encode($value) : $value; } return $this; } /** * Sets element's HTML content. */ final public function setHtml(mixed $html): static { $this->children = [(string) $html]; return $this; } /** * Returns element's HTML content. */ final public function getHtml(): string { return implode('', $this->children); } /** * Sets element's textual content. */ final public function setText(mixed $text): static { if (!$text instanceof HtmlStringable) { $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); } $this->children = [(string) $text]; return $this; } /** * Returns element's textual content. */ final public function getText(): string { return self::htmlToText($this->getHtml()); } /** * Adds new element's child. */ final public function addHtml(mixed $child): static { return $this->insert(null, $child); } /** * Appends plain-text string to element content. */ public function addText(mixed $text): static { if (!$text instanceof HtmlStringable) { $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); } return $this->insert(null, $text); } /** * Creates and adds a new Html child. */ final public function create(string $name, array|string|null $attrs = null): static { $this->insert(null, $child = static::el($name, $attrs)); return $child; } /** * Inserts child node. */ public function insert(?int $index, HtmlStringable|string $child, bool $replace = false): static { $child = $child instanceof self ? $child : (string) $child; if ($index === null) { // append $this->children[] = $child; } else { // insert or replace array_splice($this->children, $index, $replace ? 1 : 0, [$child]); } return $this; } /** * Inserts (replaces) child node (\ArrayAccess implementation). * @param int|null $index position or null for appending * @param Html|string $child Html node or raw HTML string */ final public function offsetSet($index, $child): void { $this->insert($index, $child, true); } /** * Returns child node (\ArrayAccess implementation). * @param int $index */ final public function offsetGet($index): HtmlStringable|string { return $this->children[$index]; } /** * Exists child node? (\ArrayAccess implementation). * @param int $index */ final public function offsetExists($index): bool { return isset($this->children[$index]); } /** * Removes child node (\ArrayAccess implementation). * @param int $index */ public function offsetUnset($index): void { if (isset($this->children[$index])) { array_splice($this->children, $index, 1); } } /** * Returns children count. */ final public function count(): int { return count($this->children); } /** * Removes all children. */ public function removeChildren(): void { $this->children = []; } /** * Iterates over elements. * @return \ArrayIterator<int, HtmlStringable|string> */ final public function getIterator(): \ArrayIterator { return new \ArrayIterator($this->children); } /** * Returns all children. */ final public function getChildren(): array { return $this->children; } /** * Renders element's start tag, content and end tag. */ final public function render(?int $indent = null): string { $s = $this->startTag(); if (!$this->isEmpty) { // add content if ($indent !== null) { $indent++; } foreach ($this->children as $child) { if ($child instanceof self) { $s .= $child->render($indent); } else { $s .= $child; } } // add end tag $s .= $this->endTag(); } if ($indent !== null) { return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2)); } return $s; } final public function __toString(): string { return $this->render(); } /** * Returns element's start tag. */ final public function startTag(): string { return $this->name ? '<' . $this->name . $this->attributes() . '>' : ''; } /** * Returns element's end tag. */ final public function endTag(): string { return $this->name && !$this->isEmpty ? '</' . $this->name . '>' : ''; } /** * Returns element's attributes. * @internal */ final public function attributes(): string { if (!is_array($this->attrs)) { return ''; } $s = ''; $attrs = $this->attrs; foreach ($attrs as $key => $value) { if ($value === null || $value === false) { continue; } elseif ($value === true) { $s .= ' ' . $key; continue; } elseif (is_array($value)) { if (strncmp($key, 'data-', 5) === 0) { $value = Json::encode($value); } else { $tmp = null; foreach ($value as $k => $v) { if ($v != null) { // intentionally ==, skip nulls & empty string // composite 'style' vs. 'others' $tmp[] = $v === true ? $k : (is_string($k) ? $k . ':' . $v : $v); } } if ($tmp === null) { continue; } $value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp); } } elseif (is_float($value)) { $value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); } else { $value = (string) $value; } $q = str_contains($value, '"') ? "'" : '"'; $s .= ' ' . $key . '=' . $q . str_replace( ['&', $q, '<'], ['&', $q === '"' ? '"' : ''', '<'], $value, ) . (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '') . $q; } $s = str_replace('@', '@', $s); return $s; } /** * Clones all children too. */ public function __clone() { foreach ($this->children as $key => $value) { if (is_object($value)) { $this->children[$key] = clone $value; } } } } PK �v�Z�z�d! d! Utils/Reflection.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * PHP reflection helpers. */ final class Reflection { use Nette\StaticClass; /** @deprecated use Nette\Utils\Validator::isBuiltinType() */ public static function isBuiltinType(string $type): bool { return Validators::isBuiltinType($type); } /** @deprecated use Nette\Utils\Validator::isClassKeyword() */ public static function isClassKeyword(string $name): bool { return Validators::isClassKeyword($name); } /** @deprecated use native ReflectionParameter::getDefaultValue() */ public static function getParameterDefaultValue(\ReflectionParameter $param): mixed { if ($param->isDefaultValueConstant()) { $const = $orig = $param->getDefaultValueConstantName(); $pair = explode('::', $const); if (isset($pair[1])) { $pair[0] = Type::resolve($pair[0], $param); try { $rcc = new \ReflectionClassConstant($pair[0], $pair[1]); } catch (\ReflectionException $e) { $name = self::toString($param); throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name.", 0, $e); } return $rcc->getValue(); } elseif (!defined($const)) { $const = substr((string) strrchr($const, '\\'), 1); if (!defined($const)) { $name = self::toString($param); throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name."); } } return constant($const); } return $param->getDefaultValue(); } /** * Returns a reflection of a class or trait that contains a declaration of given property. Property can also be declared in the trait. */ public static function getPropertyDeclaringClass(\ReflectionProperty $prop): \ReflectionClass { foreach ($prop->getDeclaringClass()->getTraits() as $trait) { if ($trait->hasProperty($prop->name) // doc-comment guessing as workaround for insufficient PHP reflection && $trait->getProperty($prop->name)->getDocComment() === $prop->getDocComment() ) { return self::getPropertyDeclaringClass($trait->getProperty($prop->name)); } } return $prop->getDeclaringClass(); } /** * Returns a reflection of a method that contains a declaration of $method. * Usually, each method is its own declaration, but the body of the method can also be in the trait and under a different name. */ public static function getMethodDeclaringMethod(\ReflectionMethod $method): \ReflectionMethod { // file & line guessing as workaround for insufficient PHP reflection $decl = $method->getDeclaringClass(); if ($decl->getFileName() === $method->getFileName() && $decl->getStartLine() <= $method->getStartLine() && $decl->getEndLine() >= $method->getEndLine() ) { return $method; } $hash = [$method->getFileName(), $method->getStartLine(), $method->getEndLine()]; if (($alias = $decl->getTraitAliases()[$method->name] ?? null) && ($m = new \ReflectionMethod($alias)) && $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] ) { return self::getMethodDeclaringMethod($m); } foreach ($decl->getTraits() as $trait) { if ($trait->hasMethod($method->name) && ($m = $trait->getMethod($method->name)) && $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()] ) { return self::getMethodDeclaringMethod($m); } } return $method; } /** * Finds out if reflection has access to PHPdoc comments. Comments may not be available due to the opcode cache. */ public static function areCommentsAvailable(): bool { static $res; return $res ?? $res = (bool) (new \ReflectionMethod(__METHOD__))->getDocComment(); } public static function toString(\Reflector $ref): string { if ($ref instanceof \ReflectionClass) { return $ref->name; } elseif ($ref instanceof \ReflectionMethod) { return $ref->getDeclaringClass()->name . '::' . $ref->name . '()'; } elseif ($ref instanceof \ReflectionFunction) { return $ref->name . '()'; } elseif ($ref instanceof \ReflectionProperty) { return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name; } elseif ($ref instanceof \ReflectionParameter) { return '$' . $ref->name . ' in ' . self::toString($ref->getDeclaringFunction()); } else { throw new Nette\InvalidArgumentException; } } /** * Expands the name of the class to full name in the given context of given class. * Thus, it returns how the PHP parser would understand $name if it were written in the body of the class $context. * @throws Nette\InvalidArgumentException */ public static function expandClassName(string $name, \ReflectionClass $context): string { $lower = strtolower($name); if (empty($name)) { throw new Nette\InvalidArgumentException('Class name must not be empty.'); } elseif (Validators::isBuiltinType($lower)) { return $lower; } elseif ($lower === 'self' || $lower === 'static') { return $context->name; } elseif ($lower === 'parent') { return $context->getParentClass() ? $context->getParentClass()->name : 'parent'; } elseif ($name[0] === '\\') { // fully qualified name return ltrim($name, '\\'); } $uses = self::getUseStatements($context); $parts = explode('\\', $name, 2); if (isset($uses[$parts[0]])) { $parts[0] = $uses[$parts[0]]; return implode('\\', $parts); } elseif ($context->inNamespace()) { return $context->getNamespaceName() . '\\' . $name; } else { return $name; } } /** @return array of [alias => class] */ public static function getUseStatements(\ReflectionClass $class): array { if ($class->isAnonymous()) { throw new Nette\NotImplementedException('Anonymous classes are not supported.'); } static $cache = []; if (!isset($cache[$name = $class->name])) { if ($class->isInternal()) { $cache[$name] = []; } else { $code = file_get_contents($class->getFileName()); $cache = self::parseUseStatements($code, $name) + $cache; } } return $cache[$name]; } /** * Parses PHP code to [class => [alias => class, ...]] */ private static function parseUseStatements(string $code, ?string $forClass = null): array { try { $tokens = \PhpToken::tokenize($code, TOKEN_PARSE); } catch (\ParseError $e) { trigger_error($e->getMessage(), E_USER_NOTICE); $tokens = []; } $namespace = $class = $classLevel = $level = null; $res = $uses = []; $nameTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED]; while ($token = current($tokens)) { next($tokens); switch ($token->id) { case T_NAMESPACE: $namespace = ltrim(self::fetch($tokens, $nameTokens) . '\\', '\\'); $uses = []; break; case T_CLASS: case T_INTERFACE: case T_TRAIT: case PHP_VERSION_ID < 80100 ? T_CLASS : T_ENUM: if ($name = self::fetch($tokens, T_STRING)) { $class = $namespace . $name; $classLevel = $level + 1; $res[$class] = $uses; if ($class === $forClass) { return $res; } } break; case T_USE: while (!$class && ($name = self::fetch($tokens, $nameTokens))) { $name = ltrim($name, '\\'); if (self::fetch($tokens, '{')) { while ($suffix = self::fetch($tokens, $nameTokens)) { if (self::fetch($tokens, T_AS)) { $uses[self::fetch($tokens, T_STRING)] = $name . $suffix; } else { $tmp = explode('\\', $suffix); $uses[end($tmp)] = $name . $suffix; } if (!self::fetch($tokens, ',')) { break; } } } elseif (self::fetch($tokens, T_AS)) { $uses[self::fetch($tokens, T_STRING)] = $name; } else { $tmp = explode('\\', $name); $uses[end($tmp)] = $name; } if (!self::fetch($tokens, ',')) { break; } } break; case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: case ord('{'): $level++; break; case ord('}'): if ($level === $classLevel) { $class = $classLevel = null; } $level--; } } return $res; } private static function fetch(array &$tokens, string|int|array $take): ?string { $res = null; while ($token = current($tokens)) { if ($token->is($take)) { $res .= $token->text; } elseif (!$token->is([T_DOC_COMMENT, T_WHITESPACE, T_COMMENT])) { break; } next($tokens); } return $res; } } PK �v�Z�4+�� � Utils/Json.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * JSON encoder and decoder. */ final class Json { use Nette\StaticClass; /** @deprecated use Json::decode(..., forceArrays: true) */ public const FORCE_ARRAY = JSON_OBJECT_AS_ARRAY; /** @deprecated use Json::encode(..., pretty: true) */ public const PRETTY = JSON_PRETTY_PRINT; /** @deprecated use Json::encode(..., asciiSafe: true) */ public const ESCAPE_UNICODE = 1 << 19; /** * Converts value to JSON format. Use $pretty for easier reading and clarity, $asciiSafe for ASCII output * and $htmlSafe for HTML escaping, $forceObjects enforces the encoding of non-associateve arrays as objects. * @throws JsonException */ public static function encode( mixed $value, bool|int $pretty = false, bool $asciiSafe = false, bool $htmlSafe = false, bool $forceObjects = false, ): string { if (is_int($pretty)) { // back compatibility $flags = ($pretty & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE) | ($pretty & ~self::ESCAPE_UNICODE); } else { $flags = ($asciiSafe ? 0 : JSON_UNESCAPED_UNICODE) | ($pretty ? JSON_PRETTY_PRINT : 0) | ($forceObjects ? JSON_FORCE_OBJECT : 0) | ($htmlSafe ? JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG : 0); } $flags |= JSON_UNESCAPED_SLASHES | (defined('JSON_PRESERVE_ZERO_FRACTION') ? JSON_PRESERVE_ZERO_FRACTION : 0); // since PHP 5.6.6 & PECL JSON-C 1.3.7 $json = json_encode($value, $flags); if ($error = json_last_error()) { throw new JsonException(json_last_error_msg(), $error); } return $json; } /** * Parses JSON to PHP value. The $forceArrays enforces the decoding of objects as arrays. * @throws JsonException */ public static function decode(string $json, bool|int $forceArrays = false): mixed { $flags = is_int($forceArrays) // back compatibility ? $forceArrays : ($forceArrays ? JSON_OBJECT_AS_ARRAY : 0); $flags |= JSON_BIGINT_AS_STRING; $value = json_decode($json, flags: $flags); if ($error = json_last_error()) { throw new JsonException(json_last_error_msg(), $error); } return $value; } } PK �v�Z��0;, , Utils/Paginator.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Paginating math. * * @property int $page * @property-read int $firstPage * @property-read int|null $lastPage * @property-read int $firstItemOnPage * @property-read int $lastItemOnPage * @property int $base * @property-read bool $first * @property-read bool $last * @property-read int|null $pageCount * @property int $itemsPerPage * @property int|null $itemCount * @property-read int $offset * @property-read int|null $countdownOffset * @property-read int $length */ class Paginator { use Nette\SmartObject; private int $base = 1; private int $itemsPerPage = 1; private int $page = 1; private ?int $itemCount = null; /** * Sets current page number. */ public function setPage(int $page): static { $this->page = $page; return $this; } /** * Returns current page number. */ public function getPage(): int { return $this->base + $this->getPageIndex(); } /** * Returns first page number. */ public function getFirstPage(): int { return $this->base; } /** * Returns last page number. */ public function getLastPage(): ?int { return $this->itemCount === null ? null : $this->base + max(0, $this->getPageCount() - 1); } /** * Returns the sequence number of the first element on the page */ public function getFirstItemOnPage(): int { return $this->itemCount !== 0 ? $this->offset + 1 : 0; } /** * Returns the sequence number of the last element on the page */ public function getLastItemOnPage(): int { return $this->offset + $this->length; } /** * Sets first page (base) number. */ public function setBase(int $base): static { $this->base = $base; return $this; } /** * Returns first page (base) number. */ public function getBase(): int { return $this->base; } /** * Returns zero-based page number. */ protected function getPageIndex(): int { $index = max(0, $this->page - $this->base); return $this->itemCount === null ? $index : min($index, max(0, $this->getPageCount() - 1)); } /** * Is the current page the first one? */ public function isFirst(): bool { return $this->getPageIndex() === 0; } /** * Is the current page the last one? */ public function isLast(): bool { return $this->itemCount === null ? false : $this->getPageIndex() >= $this->getPageCount() - 1; } /** * Returns the total number of pages. */ public function getPageCount(): ?int { return $this->itemCount === null ? null : (int) ceil($this->itemCount / $this->itemsPerPage); } /** * Sets the number of items to display on a single page. */ public function setItemsPerPage(int $itemsPerPage): static { $this->itemsPerPage = max(1, $itemsPerPage); return $this; } /** * Returns the number of items to display on a single page. */ public function getItemsPerPage(): int { return $this->itemsPerPage; } /** * Sets the total number of items. */ public function setItemCount(?int $itemCount = null): static { $this->itemCount = $itemCount === null ? null : max(0, $itemCount); return $this; } /** * Returns the total number of items. */ public function getItemCount(): ?int { return $this->itemCount; } /** * Returns the absolute index of the first item on current page. */ public function getOffset(): int { return $this->getPageIndex() * $this->itemsPerPage; } /** * Returns the absolute index of the first item on current page in countdown paging. */ public function getCountdownOffset(): ?int { return $this->itemCount === null ? null : max(0, $this->itemCount - ($this->getPageIndex() + 1) * $this->itemsPerPage); } /** * Returns the number of items on current page. */ public function getLength(): int { return $this->itemCount === null ? $this->itemsPerPage : min($this->itemsPerPage, $this->itemCount - $this->getPageIndex() * $this->itemsPerPage); } } PK �v�Z�f-�A3 A3 Utils/Finder.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Finder allows searching through directory trees using iterator. * * Finder::findFiles('*.php') * ->size('> 10kB') * ->from('.') * ->exclude('temp'); * * @implements \IteratorAggregate<string, FileInfo> */ class Finder implements \IteratorAggregate { use Nette\SmartObject; /** @var array<array{string, string}> */ private array $find = []; /** @var string[] */ private array $in = []; /** @var \Closure[] */ private array $filters = []; /** @var \Closure[] */ private array $descentFilters = []; /** @var array<string|self> */ private array $appends = []; private bool $childFirst = false; /** @var ?callable */ private $sort; private int $maxDepth = -1; private bool $ignoreUnreadableDirs = true; /** * Begins search for files and directories matching mask. */ public static function find(string|array $masks): static { $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic return (new static)->addMask($masks, 'dir')->addMask($masks, 'file'); } /** * Begins search for files matching mask. */ public static function findFiles(string|array $masks): static { $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic return (new static)->addMask($masks, 'file'); } /** * Begins search for directories matching mask. */ public static function findDirectories(string|array $masks): static { $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic return (new static)->addMask($masks, 'dir'); } /** * Finds files matching the specified masks. */ public function files(string|array $masks): static { return $this->addMask((array) $masks, 'file'); } /** * Finds directories matching the specified masks. */ public function directories(string|array $masks): static { return $this->addMask((array) $masks, 'dir'); } private function addMask(array $masks, string $mode): static { foreach ($masks as $mask) { $mask = FileSystem::unixSlashes($mask); if ($mode === 'dir') { $mask = rtrim($mask, '/'); } if ($mask === '' || ($mode === 'file' && str_ends_with($mask, '/'))) { throw new Nette\InvalidArgumentException("Invalid mask '$mask'"); } if (str_starts_with($mask, '**/')) { $mask = substr($mask, 3); } $this->find[] = [$mask, $mode]; } return $this; } /** * Searches in the given directories. Wildcards are allowed. */ public function in(string|array $paths): static { $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic $this->addLocation($paths, ''); return $this; } /** * Searches recursively from the given directories. Wildcards are allowed. */ public function from(string|array $paths): static { $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic $this->addLocation($paths, '/**'); return $this; } private function addLocation(array $paths, string $ext): void { foreach ($paths as $path) { if ($path === '') { throw new Nette\InvalidArgumentException("Invalid directory '$path'"); } $path = rtrim(FileSystem::unixSlashes($path), '/'); $this->in[] = $path . $ext; } } /** * Lists directory's contents before the directory itself. By default, this is disabled. */ public function childFirst(bool $state = true): static { $this->childFirst = $state; return $this; } /** * Ignores unreadable directories. By default, this is enabled. */ public function ignoreUnreadableDirs(bool $state = true): static { $this->ignoreUnreadableDirs = $state; return $this; } /** * Set a compare function for sorting directory entries. The function will be called to sort entries from the same directory. * @param callable(FileInfo, FileInfo): int $callback */ public function sortBy(callable $callback): static { $this->sort = $callback; return $this; } /** * Sorts files in each directory naturally by name. */ public function sortByName(): static { $this->sort = fn(FileInfo $a, FileInfo $b): int => strnatcmp($a->getBasename(), $b->getBasename()); return $this; } /** * Adds the specified paths or appends a new finder that returns. */ public function append(string|array|null $paths = null): static { if ($paths === null) { return $this->appends[] = new static; } $this->appends = array_merge($this->appends, (array) $paths); return $this; } /********************* filtering ****************d*g**/ /** * Skips entries that matches the given masks relative to the ones defined with the in() or from() methods. */ public function exclude(string|array $masks): static { $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic foreach ($masks as $mask) { $mask = FileSystem::unixSlashes($mask); if (!preg_match('~^/?(\*\*/)?(.+)(/\*\*|/\*|/|)$~D', $mask, $m)) { throw new Nette\InvalidArgumentException("Invalid mask '$mask'"); } $end = $m[3]; $re = $this->buildPattern($m[2]); $filter = fn(FileInfo $file): bool => ($end && !$file->isDir()) || !preg_match($re, FileSystem::unixSlashes($file->getRelativePathname())); $this->descentFilter($filter); if ($end !== '/*') { $this->filter($filter); } } return $this; } /** * Yields only entries which satisfy the given filter. * @param callable(FileInfo): bool $callback */ public function filter(callable $callback): static { $this->filters[] = \Closure::fromCallable($callback); return $this; } /** * It descends only to directories that match the specified filter. * @param callable(FileInfo): bool $callback */ public function descentFilter(callable $callback): static { $this->descentFilters[] = \Closure::fromCallable($callback); return $this; } /** * Sets the maximum depth of entries. */ public function limitDepth(?int $depth): static { $this->maxDepth = $depth ?? -1; return $this; } /** * Restricts the search by size. $operator accepts "[operator] [size] [unit]" example: >=10kB */ public function size(string $operator, ?int $size = null): static { if (func_num_args() === 1) { // in $operator is predicate if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?((?:\d*\.)?\d+)\s*(K|M|G|)B?$#Di', $operator, $matches)) { throw new Nette\InvalidArgumentException('Invalid size predicate format.'); } [, $operator, $size, $unit] = $matches; $units = ['' => 1, 'k' => 1e3, 'm' => 1e6, 'g' => 1e9]; $size *= $units[strtolower($unit)]; $operator = $operator ?: '='; } return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getSize(), $operator, $size)); } /** * Restricts the search by modified time. $operator accepts "[operator] [date]" example: >1978-01-23 */ public function date(string $operator, string|int|\DateTimeInterface|null $date = null): static { if (func_num_args() === 1) { // in $operator is predicate if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?(.+)$#Di', $operator, $matches)) { throw new Nette\InvalidArgumentException('Invalid date predicate format.'); } [, $operator, $date] = $matches; $operator = $operator ?: '='; } $date = DateTime::from($date)->format('U'); return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getMTime(), $operator, $date)); } /********************* iterator generator ****************d*g**/ /** * Returns an array with all found files and directories. */ public function collect(): array { return iterator_to_array($this->getIterator()); } /** @return \Generator<string, FileInfo> */ public function getIterator(): \Generator { $plan = $this->buildPlan(); foreach ($plan as $dir => $searches) { yield from $this->traverseDir($dir, $searches); } foreach ($this->appends as $item) { if ($item instanceof self) { yield from $item->getIterator(); } else { $item = FileSystem::platformSlashes($item); yield $item => new FileInfo($item); } } } /** * @param array<\stdClass{pattern: string, mode: string, recursive: bool}> $searches * @param string[] $subdirs * @return \Generator<string, FileInfo> */ private function traverseDir(string $dir, array $searches, array $subdirs = []): \Generator { if ($this->maxDepth >= 0 && count($subdirs) > $this->maxDepth) { return; } elseif (!is_dir($dir)) { throw new Nette\InvalidStateException("Directory '$dir' not found."); } try { $pathNames = new \FilesystemIterator($dir, \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::UNIX_PATHS); } catch (\UnexpectedValueException $e) { if ($this->ignoreUnreadableDirs) { return; } else { throw new Nette\InvalidStateException($e->getMessage()); } } $files = $this->convertToFiles($pathNames, implode('/', $subdirs), FileSystem::isAbsolute($dir)); if ($this->sort) { $files = iterator_to_array($files); usort($files, $this->sort); } foreach ($files as $file) { $pathName = $file->getPathname(); $cache = $subSearch = []; if ($file->isDir()) { foreach ($searches as $search) { if ($search->recursive && $this->proveFilters($this->descentFilters, $file, $cache)) { $subSearch[] = $search; } } } if ($this->childFirst && $subSearch) { yield from $this->traverseDir($pathName, $subSearch, array_merge($subdirs, [$file->getBasename()])); } $relativePathname = FileSystem::unixSlashes($file->getRelativePathname()); foreach ($searches as $search) { if ( $file->getType() === $search->mode && preg_match($search->pattern, $relativePathname) && $this->proveFilters($this->filters, $file, $cache) ) { yield $pathName => $file; break; } } if (!$this->childFirst && $subSearch) { yield from $this->traverseDir($pathName, $subSearch, array_merge($subdirs, [$file->getBasename()])); } } } private function convertToFiles(iterable $pathNames, string $relativePath, bool $absolute): \Generator { foreach ($pathNames as $pathName) { if (!$absolute) { $pathName = preg_replace('~\.?/~A', '', $pathName); } $pathName = FileSystem::platformSlashes($pathName); yield new FileInfo($pathName, $relativePath); } } private function proveFilters(array $filters, FileInfo $file, array &$cache): bool { foreach ($filters as $filter) { $res = &$cache[spl_object_id($filter)]; $res ??= $filter($file); if (!$res) { return false; } } return true; } /** @return array<string, array<\stdClass{pattern: string, mode: string, recursive: bool}>> */ private function buildPlan(): array { $plan = $dirCache = []; foreach ($this->find as [$mask, $mode]) { $splits = []; if (FileSystem::isAbsolute($mask)) { if ($this->in) { throw new Nette\InvalidStateException("You cannot combine the absolute path in the mask '$mask' and the directory to search '{$this->in[0]}'."); } $splits[] = self::splitRecursivePart($mask); } else { foreach ($this->in ?: ['.'] as $in) { $in = strtr($in, ['[' => '[[]', ']' => '[]]']); // in path, do not treat [ and ] as a pattern by glob() $splits[] = self::splitRecursivePart($in . '/' . $mask); } } foreach ($splits as [$base, $rest, $recursive]) { $base = $base === '' ? '.' : $base; $dirs = $dirCache[$base] ??= strpbrk($base, '*?[') ? glob($base, GLOB_NOSORT | GLOB_ONLYDIR | GLOB_NOESCAPE) : [strtr($base, ['[[]' => '[', '[]]' => ']'])]; // unescape [ and ] $search = (object) ['pattern' => $this->buildPattern($rest), 'mode' => $mode, 'recursive' => $recursive]; foreach ($dirs as $dir) { $plan[$dir][] = $search; } } } return $plan; } /** * Since glob() does not know ** wildcard, we divide the path into a part for glob and a part for manual traversal. */ private static function splitRecursivePart(string $path): array { $a = strrpos($path, '/'); $parts = preg_split('~(?<=^|/)\*\*($|/)~', substr($path, 0, $a + 1), 2); return isset($parts[1]) ? [$parts[0], $parts[1] . substr($path, $a + 1), true] : [$parts[0], substr($path, $a + 1), false]; } /** * Converts wildcards to regular expression. */ private function buildPattern(string $mask): string { if ($mask === '*') { return '##'; } elseif (str_starts_with($mask, './')) { $anchor = '^'; $mask = substr($mask, 2); } else { $anchor = '(?:^|/)'; } $pattern = strtr( preg_quote($mask, '#'), [ '\*\*/' => '(.+/)?', '\*' => '[^/]*', '\?' => '[^/]', '\[\!' => '[^', '\[' => '[', '\]' => ']', '\-' => '-', ], ); return '#' . $anchor . $pattern . '$#D' . (defined('PHP_WINDOWS_VERSION_BUILD') ? 'i' : ''); } } PK �v�ZmT�% % Utils/ArrayList.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Provides the base class for a generic list (items can be accessed by index). * @template T */ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate { use Nette\SmartObject; private array $list = []; /** * Transforms array to ArrayList. * @param array<T> $array */ public static function from(array $array): static { if (!Arrays::isList($array)) { throw new Nette\InvalidArgumentException('Array is not valid list.'); } $obj = new static; $obj->list = $array; return $obj; } /** * Returns an iterator over all items. * @return \Iterator<int, T> */ public function &getIterator(): \Iterator { foreach ($this->list as &$item) { yield $item; } } /** * Returns items count. */ public function count(): int { return count($this->list); } /** * Replaces or appends a item. * @param int|null $index * @param T $value * @throws Nette\OutOfRangeException */ public function offsetSet($index, $value): void { if ($index === null) { $this->list[] = $value; } elseif (!is_int($index) || $index < 0 || $index >= count($this->list)) { throw new Nette\OutOfRangeException('Offset invalid or out of range'); } else { $this->list[$index] = $value; } } /** * Returns a item. * @param int $index * @return T * @throws Nette\OutOfRangeException */ public function offsetGet($index): mixed { if (!is_int($index) || $index < 0 || $index >= count($this->list)) { throw new Nette\OutOfRangeException('Offset invalid or out of range'); } return $this->list[$index]; } /** * Determines whether a item exists. * @param int $index */ public function offsetExists($index): bool { return is_int($index) && $index >= 0 && $index < count($this->list); } /** * Removes the element at the specified position in this list. * @param int $index * @throws Nette\OutOfRangeException */ public function offsetUnset($index): void { if (!is_int($index) || $index < 0 || $index >= count($this->list)) { throw new Nette\OutOfRangeException('Offset invalid or out of range'); } array_splice($this->list, $index, 1); } /** * Prepends a item. * @param T $value */ public function prepend(mixed $value): void { $first = array_slice($this->list, 0, 1); $this->offsetSet(0, $value); array_splice($this->list, 1, 0, $first); } } PK �v�ZO�I�d d Utils/ObjectHelpers.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; use Nette\MemberAccessException; /** * Nette\SmartObject helpers. * @internal */ final class ObjectHelpers { use Nette\StaticClass; /** * @return never * @throws MemberAccessException */ public static function strictGet(string $class, string $name): void { $rc = new \ReflectionClass($class); $hint = self::getSuggestion(array_merge( array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()), self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'), ), $name); throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.')); } /** * @return never * @throws MemberAccessException */ public static function strictSet(string $class, string $name): void { $rc = new \ReflectionClass($class); $hint = self::getSuggestion(array_merge( array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()), self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'), ), $name); throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.')); } /** * @return never * @throws MemberAccessException */ public static function strictCall(string $class, string $method, array $additionalMethods = []): void { $trace = debug_backtrace(0, 3); // suppose this method is called from __call() $context = ($trace[1]['function'] ?? null) === '__call' ? ($trace[2]['class'] ?? null) : null; if ($context && is_a($class, $context, true) && method_exists($context, $method)) { // called parent::$method() $class = get_parent_class($context); } if (method_exists($class, $method)) { // insufficient visibility $rm = new \ReflectionMethod($class, $method); $visibility = $rm->isPrivate() ? 'private ' : ($rm->isProtected() ? 'protected ' : ''); throw new MemberAccessException("Call to {$visibility}method $class::$method() from " . ($context ? "scope $context." : 'global scope.')); } else { $hint = self::getSuggestion(array_merge( get_class_methods($class), self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:static[ \t]+)?(?:\S+[ \t]+)??(\w+)\(~m'), $additionalMethods, ), $method); throw new MemberAccessException("Call to undefined method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.')); } } /** * @return never * @throws MemberAccessException */ public static function strictStaticCall(string $class, string $method): void { $trace = debug_backtrace(0, 3); // suppose this method is called from __callStatic() $context = ($trace[1]['function'] ?? null) === '__callStatic' ? ($trace[2]['class'] ?? null) : null; if ($context && is_a($class, $context, true) && method_exists($context, $method)) { // called parent::$method() $class = get_parent_class($context); } if (method_exists($class, $method)) { // insufficient visibility $rm = new \ReflectionMethod($class, $method); $visibility = $rm->isPrivate() ? 'private ' : ($rm->isProtected() ? 'protected ' : ''); throw new MemberAccessException("Call to {$visibility}method $class::$method() from " . ($context ? "scope $context." : 'global scope.')); } else { $hint = self::getSuggestion( array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), fn($m) => $m->isStatic()), $method, ); throw new MemberAccessException("Call to undefined static method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.')); } } /** * Returns array of magic properties defined by annotation @property. * @return array of [name => bit mask] * @internal */ public static function getMagicProperties(string $class): array { static $cache; $props = &$cache[$class]; if ($props !== null) { return $props; } $rc = new \ReflectionClass($class); preg_match_all( '~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx', (string) $rc->getDocComment(), $matches, PREG_SET_ORDER, ); $props = []; foreach ($matches as [, $type, $name]) { $uname = ucfirst($name); $write = $type !== '-read' && $rc->hasMethod($nm = 'set' . $uname) && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic(); $read = $type !== '-write' && ($rc->hasMethod($nm = 'get' . $uname) || $rc->hasMethod($nm = 'is' . $uname)) && ($rm = $rc->getMethod($nm))->name === $nm && !$rm->isPrivate() && !$rm->isStatic(); if ($read || $write) { $props[$name] = $read << 0 | ($nm[0] === 'g') << 1 | $rm->returnsReference() << 2 | $write << 3 | ($type === '-deprecated') << 4; } } foreach ($rc->getTraits() as $trait) { $props += self::getMagicProperties($trait->name); } if ($parent = get_parent_class($class)) { $props += self::getMagicProperties($parent); } return $props; } /** * Finds the best suggestion (for 8-bit encoding). * @param (\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionClass|\ReflectionProperty|string)[] $possibilities * @internal */ public static function getSuggestion(array $possibilities, string $value): ?string { $norm = preg_replace($re = '#^(get|set|has|is|add)(?=[A-Z])#', '+', $value); $best = null; $min = (strlen($value) / 4 + 1) * 10 + .1; foreach (array_unique($possibilities, SORT_REGULAR) as $item) { $item = $item instanceof \Reflector ? $item->name : $item; if ($item !== $value && ( ($len = levenshtein($item, $value, 10, 11, 10)) < $min || ($len = levenshtein(preg_replace($re, '*', $item), $norm, 10, 11, 10)) < $min )) { $min = $len; $best = $item; } } return $best; } private static function parseFullDoc(\ReflectionClass $rc, string $pattern): array { do { $doc[] = $rc->getDocComment(); $traits = $rc->getTraits(); while ($trait = array_pop($traits)) { $doc[] = $trait->getDocComment(); $traits += $trait->getTraits(); } } while ($rc = $rc->getParentClass()); return preg_match_all($pattern, implode('', $doc), $m) ? $m[1] : []; } /** * Checks if the public non-static property exists. * Returns 'event' if the property exists and has event like name * @internal */ public static function hasProperty(string $class, string $name): bool|string { static $cache; $prop = &$cache[$class][$name]; if ($prop === null) { $prop = false; try { $rp = new \ReflectionProperty($class, $name); if ($rp->isPublic() && !$rp->isStatic()) { $prop = $name >= 'onA' && $name < 'on_' ? 'event' : true; } } catch (\ReflectionException $e) { } } return $prop; } } PK �v�Z�,v��U �U Utils/Strings.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use JetBrains\PhpStorm\Language; use Nette; use function is_array, is_object, strlen; /** * String tools library. */ class Strings { use Nette\StaticClass; public const TrimCharacters = " \t\n\r\0\x0B\u{A0}"; /** @deprecated use Strings::TrimCharacters */ public const TRIM_CHARACTERS = self::TrimCharacters; /** * @deprecated use Nette\Utils\Validator::isUnicode() */ public static function checkEncoding(string $s): bool { return $s === self::fixEncoding($s); } /** * Removes all invalid UTF-8 characters from a string. */ public static function fixEncoding(string $s): string { // removes xD800-xDFFF, x110000 and higher return htmlspecialchars_decode(htmlspecialchars($s, ENT_NOQUOTES | ENT_IGNORE, 'UTF-8'), ENT_NOQUOTES); } /** * Returns a specific character in UTF-8 from code point (number in range 0x0000..D7FF or 0xE000..10FFFF). * @throws Nette\InvalidArgumentException if code point is not in valid range */ public static function chr(int $code): string { if ($code < 0 || ($code >= 0xD800 && $code <= 0xDFFF) || $code > 0x10FFFF) { throw new Nette\InvalidArgumentException('Code point must be in range 0x0 to 0xD7FF or 0xE000 to 0x10FFFF.'); } elseif (!extension_loaded('iconv')) { throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); } return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code)); } /** * Returns a code point of specific character in UTF-8 (number in range 0x0000..D7FF or 0xE000..10FFFF). */ public static function ord(string $c): int { if (!extension_loaded('iconv')) { throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); } $tmp = iconv('UTF-8', 'UTF-32BE//IGNORE', $c); if (!$tmp) { throw new Nette\InvalidArgumentException('Invalid UTF-8 character "' . ($c === '' ? '' : '\x' . strtoupper(bin2hex($c))) . '".'); } return unpack('N', $tmp)[1]; } /** * @deprecated use str_starts_with() */ public static function startsWith(string $haystack, string $needle): bool { return str_starts_with($haystack, $needle); } /** * @deprecated use str_ends_with() */ public static function endsWith(string $haystack, string $needle): bool { return str_ends_with($haystack, $needle); } /** * @deprecated use str_contains() */ public static function contains(string $haystack, string $needle): bool { return str_contains($haystack, $needle); } /** * Returns a part of UTF-8 string specified by starting position and length. If start is negative, * the returned string will start at the start'th character from the end of string. */ public static function substring(string $s, int $start, ?int $length = null): string { if (function_exists('mb_substr')) { return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster } elseif (!extension_loaded('iconv')) { throw new Nette\NotSupportedException(__METHOD__ . '() requires extension ICONV or MBSTRING, neither is loaded.'); } elseif ($length === null) { $length = self::length($s); } elseif ($start < 0 && $length < 0) { $start += self::length($s); // unifies iconv_substr behavior with mb_substr } return iconv_substr($s, $start, $length, 'UTF-8'); } /** * Removes control characters, normalizes line breaks to `\n`, removes leading and trailing blank lines, * trims end spaces on lines, normalizes UTF-8 to the normal form of NFC. */ public static function normalize(string $s): string { // convert to compressed normal form (NFC) if (class_exists('Normalizer', false) && ($n = \Normalizer::normalize($s, \Normalizer::FORM_C)) !== false) { $s = $n; } $s = self::unixNewLines($s); // remove control characters; leave \t + \n $s = self::pcre('preg_replace', ['#[\x00-\x08\x0B-\x1F\x7F-\x9F]+#u', '', $s]); // right trim $s = self::pcre('preg_replace', ['#[\t ]+$#m', '', $s]); // leading and trailing blank lines $s = trim($s, "\n"); return $s; } /** @deprecated use Strings::unixNewLines() */ public static function normalizeNewLines(string $s): string { return self::unixNewLines($s); } /** * Converts line endings to \n used on Unix-like systems. * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator. */ public static function unixNewLines(string $s): string { return preg_replace("~\r\n?|\u{2028}|\u{2029}~", "\n", $s); } /** * Converts line endings to platform-specific, i.e. \r\n on Windows and \n elsewhere. * Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator. */ public static function platformNewLines(string $s): string { return preg_replace("~\r\n?|\n|\u{2028}|\u{2029}~", PHP_EOL, $s); } /** * Converts UTF-8 string to ASCII, ie removes diacritics etc. */ public static function toAscii(string $s): string { $iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null; static $transliterator = null; if ($transliterator === null) { if (class_exists('Transliterator', false)) { $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII'); } else { trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE); $transliterator = false; } } // remove control characters and check UTF-8 validity $s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]); // transliteration (by Transliterator and iconv) is not optimal, replace some characters directly $s = strtr($s, ["\u{201E}" => '"', "\u{201C}" => '"', "\u{201D}" => '"', "\u{201A}" => "'", "\u{2018}" => "'", "\u{2019}" => "'", "\u{B0}" => '^', "\u{42F}" => 'Ya', "\u{44F}" => 'ya', "\u{42E}" => 'Yu', "\u{44E}" => 'yu', "\u{c4}" => 'Ae', "\u{d6}" => 'Oe', "\u{dc}" => 'Ue', "\u{1e9e}" => 'Ss', "\u{e4}" => 'ae', "\u{f6}" => 'oe', "\u{fc}" => 'ue', "\u{df}" => 'ss']); // „ “ ” ‚ ‘ ’ ° Я я Ю ю Ä Ö Ü ẞ ä ö ü ß if ($iconv !== 'libiconv') { $s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔ } if ($transliterator) { $s = $transliterator->transliterate($s); // use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ if ($iconv === 'glibc') { $s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); $s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters } elseif ($iconv === 'libiconv') { $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); } else { // null or 'unknown' (#216) $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars } } elseif ($iconv === 'glibc' || $iconv === 'libiconv') { // temporarily hide these characters to distinguish them from the garbage that iconv creates $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06"); if ($iconv === 'glibc') { // glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved $s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); $s = strtr( $s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7", 'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.', ); $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); } else { $s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); } // remove garbage that iconv creates during transliteration (eg Ý -> Y') $s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s); // restore temporarily hidden characters $s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?'); } else { $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars } return $s; } /** * Modifies the UTF-8 string to the form used in the URL, ie removes diacritics and replaces all characters * except letters of the English alphabet and numbers with a hyphens. */ public static function webalize(string $s, ?string $charlist = null, bool $lower = true): string { $s = self::toAscii($s); if ($lower) { $s = strtolower($s); } $s = self::pcre('preg_replace', ['#[^a-z0-9' . ($charlist !== null ? preg_quote($charlist, '#') : '') . ']+#i', '-', $s]); $s = trim($s, '-'); return $s; } /** * Truncates a UTF-8 string to given maximal length, while trying not to split whole words. Only if the string is truncated, * an ellipsis (or something else set with third argument) is appended to the string. */ public static function truncate(string $s, int $maxLen, string $append = "\u{2026}"): string { if (self::length($s) > $maxLen) { $maxLen -= self::length($append); if ($maxLen < 1) { return $append; } elseif ($matches = self::match($s, '#^.{1,' . $maxLen . '}(?=[\s\x00-/:-@\[-`{-~])#us')) { return $matches[0] . $append; } else { return self::substring($s, 0, $maxLen) . $append; } } return $s; } /** * Indents a multiline text from the left. Second argument sets how many indentation chars should be used, * while the indent itself is the third argument (*tab* by default). */ public static function indent(string $s, int $level = 1, string $chars = "\t"): string { if ($level > 0) { $s = self::replace($s, '#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level)); } return $s; } /** * Converts all characters of UTF-8 string to lower case. */ public static function lower(string $s): string { return mb_strtolower($s, 'UTF-8'); } /** * Converts the first character of a UTF-8 string to lower case and leaves the other characters unchanged. */ public static function firstLower(string $s): string { return self::lower(self::substring($s, 0, 1)) . self::substring($s, 1); } /** * Converts all characters of a UTF-8 string to upper case. */ public static function upper(string $s): string { return mb_strtoupper($s, 'UTF-8'); } /** * Converts the first character of a UTF-8 string to upper case and leaves the other characters unchanged. */ public static function firstUpper(string $s): string { return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1); } /** * Converts the first character of every word of a UTF-8 string to upper case and the others to lower case. */ public static function capitalize(string $s): string { return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8'); } /** * Compares two UTF-8 strings or their parts, without taking character case into account. If length is null, whole strings are compared, * if it is negative, the corresponding number of characters from the end of the strings is compared, * otherwise the appropriate number of characters from the beginning is compared. */ public static function compare(string $left, string $right, ?int $length = null): bool { if (class_exists('Normalizer', false)) { $left = \Normalizer::normalize($left, \Normalizer::FORM_D); // form NFD is faster $right = \Normalizer::normalize($right, \Normalizer::FORM_D); // form NFD is faster } if ($length < 0) { $left = self::substring($left, $length, -$length); $right = self::substring($right, $length, -$length); } elseif ($length !== null) { $left = self::substring($left, 0, $length); $right = self::substring($right, 0, $length); } return self::lower($left) === self::lower($right); } /** * Finds the common prefix of strings or returns empty string if the prefix was not found. * @param string[] $strings */ public static function findPrefix(array $strings): string { $first = array_shift($strings); for ($i = 0; $i < strlen($first); $i++) { foreach ($strings as $s) { if (!isset($s[$i]) || $first[$i] !== $s[$i]) { while ($i && $first[$i - 1] >= "\x80" && $first[$i] >= "\x80" && $first[$i] < "\xC0") { $i--; } return substr($first, 0, $i); } } } return $first; } /** * Returns number of characters (not bytes) in UTF-8 string. * That is the number of Unicode code points which may differ from the number of graphemes. */ public static function length(string $s): int { return function_exists('mb_strlen') ? mb_strlen($s, 'UTF-8') : strlen(utf8_decode($s)); } /** * Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string. */ public static function trim(string $s, string $charlist = self::TrimCharacters): string { $charlist = preg_quote($charlist, '#'); return self::replace($s, '#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', ''); } /** * Pads a UTF-8 string to given length by prepending the $pad string to the beginning. */ public static function padLeft(string $s, int $length, string $pad = ' '): string { $length = max(0, $length - self::length($s)); $padLen = self::length($pad); return str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen) . $s; } /** * Pads UTF-8 string to given length by appending the $pad string to the end. */ public static function padRight(string $s, int $length, string $pad = ' '): string { $length = max(0, $length - self::length($s)); $padLen = self::length($pad); return $s . str_repeat($pad, (int) ($length / $padLen)) . self::substring($pad, 0, $length % $padLen); } /** * Reverses UTF-8 string. */ public static function reverse(string $s): string { if (!extension_loaded('iconv')) { throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.'); } return iconv('UTF-32LE', 'UTF-8', strrev(iconv('UTF-8', 'UTF-32BE', $s))); } /** * Returns part of $haystack before $nth occurence of $needle or returns null if the needle was not found. * Negative value means searching from the end. */ public static function before(string $haystack, string $needle, int $nth = 1): ?string { $pos = self::pos($haystack, $needle, $nth); return $pos === null ? null : substr($haystack, 0, $pos); } /** * Returns part of $haystack after $nth occurence of $needle or returns null if the needle was not found. * Negative value means searching from the end. */ public static function after(string $haystack, string $needle, int $nth = 1): ?string { $pos = self::pos($haystack, $needle, $nth); return $pos === null ? null : substr($haystack, $pos + strlen($needle)); } /** * Returns position in characters of $nth occurence of $needle in $haystack or null if the $needle was not found. * Negative value of `$nth` means searching from the end. */ public static function indexOf(string $haystack, string $needle, int $nth = 1): ?int { $pos = self::pos($haystack, $needle, $nth); return $pos === null ? null : self::length(substr($haystack, 0, $pos)); } /** * Returns position in characters of $nth occurence of $needle in $haystack or null if the needle was not found. */ private static function pos(string $haystack, string $needle, int $nth = 1): ?int { if (!$nth) { return null; } elseif ($nth > 0) { if ($needle === '') { return 0; } $pos = 0; while (($pos = strpos($haystack, $needle, $pos)) !== false && --$nth) { $pos++; } } else { $len = strlen($haystack); if ($needle === '') { return $len; } elseif ($len === 0) { return null; } $pos = $len - 1; while (($pos = strrpos($haystack, $needle, $pos - $len)) !== false && ++$nth) { $pos--; } } return Helpers::falseToNull($pos); } /** * Divides the string into arrays according to the regular expression. Expressions in parentheses will be captured and returned as well. */ public static function split( string $subject, #[Language('RegExp')] string $pattern, bool|int $captureOffset = false, bool $skipEmpty = false, int $limit = -1, bool $utf8 = false, ): array { $flags = is_int($captureOffset) // back compatibility ? $captureOffset : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); $pattern .= $utf8 ? 'u' : ''; $m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]); return $utf8 && $captureOffset ? self::bytesToChars($subject, [$m])[0] : $m; } /** * Searches the string for the part matching the regular expression and returns * an array with the found expression and individual subexpressions, or `null`. */ public static function match( string $subject, #[Language('RegExp')] string $pattern, bool|int $captureOffset = false, int $offset = 0, bool $unmatchedAsNull = false, bool $utf8 = false, ): ?array { $flags = is_int($captureOffset) // back compatibility ? $captureOffset : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); if ($utf8) { $offset = strlen(self::substring($subject, 0, $offset)); $pattern .= 'u'; } if ($offset > strlen($subject)) { return null; } elseif (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) { return null; } elseif ($utf8 && $captureOffset) { return self::bytesToChars($subject, [$m])[0]; } else { return $m; } } /** * Searches the string for all occurrences matching the regular expression and * returns an array of arrays containing the found expression and each subexpression. */ public static function matchAll( string $subject, #[Language('RegExp')] string $pattern, bool|int $captureOffset = false, int $offset = 0, bool $unmatchedAsNull = false, bool $patternOrder = false, bool $utf8 = false, ): array { $flags = is_int($captureOffset) // back compatibility ? $captureOffset : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0); if ($utf8) { $offset = strlen(self::substring($subject, 0, $offset)); $pattern .= 'u'; } if ($offset > strlen($subject)) { return []; } self::pcre('preg_match_all', [ $pattern, $subject, &$m, ($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER), $offset, ]); return $utf8 && $captureOffset ? self::bytesToChars($subject, $m) : $m; } /** * Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`. */ public static function replace( string $subject, #[Language('RegExp')] string|array $pattern, string|callable $replacement = '', int $limit = -1, bool $captureOffset = false, bool $unmatchedAsNull = false, bool $utf8 = false, ): string { if (is_object($replacement) || is_array($replacement)) { if (!is_callable($replacement, false, $textual)) { throw new Nette\InvalidStateException("Callback '$textual' is not callable."); } $flags = ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); if ($utf8) { $pattern .= 'u'; if ($captureOffset) { $replacement = fn($m) => $replacement(self::bytesToChars($subject, [$m])[0]); } } return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]); } elseif (is_array($pattern) && is_string(key($pattern))) { $replacement = array_values($pattern); $pattern = array_keys($pattern); } if ($utf8) { $pattern = array_map(fn($item) => $item . 'u', (array) $pattern); } return self::pcre('preg_replace', [$pattern, $replacement, $subject, $limit]); } private static function bytesToChars(string $s, array $groups): array { $lastBytes = $lastChars = 0; foreach ($groups as &$matches) { foreach ($matches as &$match) { if ($match[1] > $lastBytes) { $lastChars += self::length(substr($s, $lastBytes, $match[1] - $lastBytes)); } elseif ($match[1] < $lastBytes) { $lastChars -= self::length(substr($s, $match[1], $lastBytes - $match[1])); } $lastBytes = $match[1]; $match[1] = $lastChars; } } return $groups; } /** @internal */ public static function pcre(string $func, array $args) { $res = Callback::invokeSafe($func, $args, function (string $message) use ($args): void { // compile-time error, not detectable by preg_last_error throw new RegexpException($message . ' in pattern: ' . implode(' or ', (array) $args[0])); }); if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars && ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true)) ) { throw new RegexpException(preg_last_error_msg() . ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code); } return $res; } } PK �v�Z�_� Utils/FileInfo.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Represents the file or directory returned by the Finder. * @internal do not create instances directly */ final class FileInfo extends \SplFileInfo { private string $relativePath; public function __construct(string $file, string $relativePath = '') { parent::__construct($file); $this->setInfoClass(static::class); $this->relativePath = $relativePath; } /** * Returns the relative directory path. */ public function getRelativePath(): string { return $this->relativePath; } /** * Returns the relative path including file name. */ public function getRelativePathname(): string { return ($this->relativePath === '' ? '' : $this->relativePath . DIRECTORY_SEPARATOR) . $this->getBasename(); } /** * Returns the contents of the file. * @throws Nette\IOException */ public function read(): string { return FileSystem::read($this->getPathname()); } /** * Writes the contents to the file. * @throws Nette\IOException */ public function write(string $content): void { FileSystem::write($this->getPathname(), $content); } } PK �v�Z�%�X$ X$ Utils/FileSystem.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * File system tool. */ final class FileSystem { use Nette\StaticClass; /** * Creates a directory if it does not exist, including parent directories. * @throws Nette\IOException on error occurred */ public static function createDir(string $dir, int $mode = 0777): void { if (!is_dir($dir) && !@mkdir($dir, $mode, true) && !is_dir($dir)) { // @ - dir may already exist throw new Nette\IOException(sprintf( "Unable to create directory '%s' with mode %s. %s", self::normalizePath($dir), decoct($mode), Helpers::getLastError(), )); } } /** * Copies a file or an entire directory. Overwrites existing files and directories by default. * @throws Nette\IOException on error occurred * @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists */ public static function copy(string $origin, string $target, bool $overwrite = true): void { if (stream_is_local($origin) && !file_exists($origin)) { throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin))); } elseif (!$overwrite && file_exists($target)) { throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target))); } elseif (is_dir($origin)) { static::createDir($target); foreach (new \FilesystemIterator($target) as $item) { static::delete($item->getPathname()); } foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($origin, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item) { if ($item->isDir()) { static::createDir($target . '/' . $iterator->getSubPathName()); } else { static::copy($item->getPathname(), $target . '/' . $iterator->getSubPathName()); } } } else { static::createDir(dirname($target)); if (@stream_copy_to_stream(static::open($origin, 'rb'), static::open($target, 'wb')) === false) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to copy file '%s' to '%s'. %s", self::normalizePath($origin), self::normalizePath($target), Helpers::getLastError(), )); } } } /** * Opens file and returns resource. * @return resource * @throws Nette\IOException on error occurred */ public static function open(string $path, string $mode) { $f = @fopen($path, $mode); // @ is escalated to exception if (!$f) { throw new Nette\IOException(sprintf( "Unable to open file '%s'. %s", self::normalizePath($path), Helpers::getLastError(), )); } return $f; } /** * Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first. * @throws Nette\IOException on error occurred */ public static function delete(string $path): void { if (is_file($path) || is_link($path)) { $func = DIRECTORY_SEPARATOR === '\\' && is_dir($path) ? 'rmdir' : 'unlink'; if (!@$func($path)) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to delete '%s'. %s", self::normalizePath($path), Helpers::getLastError(), )); } } elseif (is_dir($path)) { foreach (new \FilesystemIterator($path) as $item) { static::delete($item->getPathname()); } if (!@rmdir($path)) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to delete directory '%s'. %s", self::normalizePath($path), Helpers::getLastError(), )); } } } /** * Renames or moves a file or a directory. Overwrites existing files and directories by default. * @throws Nette\IOException on error occurred * @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists */ public static function rename(string $origin, string $target, bool $overwrite = true): void { if (!$overwrite && file_exists($target)) { throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target))); } elseif (!file_exists($origin)) { throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin))); } else { static::createDir(dirname($target)); if (realpath($origin) !== realpath($target)) { static::delete($target); } if (!@rename($origin, $target)) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to rename file or directory '%s' to '%s'. %s", self::normalizePath($origin), self::normalizePath($target), Helpers::getLastError(), )); } } } /** * Reads the content of a file. * @throws Nette\IOException on error occurred */ public static function read(string $file): string { $content = @file_get_contents($file); // @ is escalated to exception if ($content === false) { throw new Nette\IOException(sprintf( "Unable to read file '%s'. %s", self::normalizePath($file), Helpers::getLastError(), )); } return $content; } /** * Reads the file content line by line. Because it reads continuously as we iterate over the lines, * it is possible to read files larger than the available memory. * @return \Generator<int, string> * @throws Nette\IOException on error occurred */ public static function readLines(string $file, bool $stripNewLines = true): \Generator { return (function ($f) use ($file, $stripNewLines) { $counter = 0; do { $line = Callback::invokeSafe('fgets', [$f], fn($error) => throw new Nette\IOException(sprintf( "Unable to read file '%s'. %s", self::normalizePath($file), $error, ))); if ($line === false) { fclose($f); break; } if ($stripNewLines) { $line = rtrim($line, "\r\n"); } yield $counter++ => $line; } while (true); })(static::open($file, 'r')); } /** * Writes the string to a file. * @throws Nette\IOException on error occurred */ public static function write(string $file, string $content, ?int $mode = 0666): void { static::createDir(dirname($file)); if (@file_put_contents($file, $content) === false) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to write file '%s'. %s", self::normalizePath($file), Helpers::getLastError(), )); } if ($mode !== null && !@chmod($file, $mode)) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to chmod file '%s' to mode %s. %s", self::normalizePath($file), decoct($mode), Helpers::getLastError(), )); } } /** * Sets file permissions to `$fileMode` or directory permissions to `$dirMode`. * Recursively traverses and sets permissions on the entire contents of the directory as well. * @throws Nette\IOException on error occurred */ public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void { if (is_file($path)) { if (!@chmod($path, $fileMode)) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to chmod file '%s' to mode %s. %s", self::normalizePath($path), decoct($fileMode), Helpers::getLastError(), )); } } elseif (is_dir($path)) { foreach (new \FilesystemIterator($path) as $item) { static::makeWritable($item->getPathname(), $dirMode, $fileMode); } if (!@chmod($path, $dirMode)) { // @ is escalated to exception throw new Nette\IOException(sprintf( "Unable to chmod directory '%s' to mode %s. %s", self::normalizePath($path), decoct($dirMode), Helpers::getLastError(), )); } } else { throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($path))); } } /** * Determines if the path is absolute. */ public static function isAbsolute(string $path): bool { return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path); } /** * Normalizes `..` and `.` and directory separators in path. */ public static function normalizePath(string $path): string { $parts = $path === '' ? [] : preg_split('~[/\\\\]+~', $path); $res = []; foreach ($parts as $part) { if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') { array_pop($res); } elseif ($part !== '.') { $res[] = $part; } } return $res === [''] ? DIRECTORY_SEPARATOR : implode(DIRECTORY_SEPARATOR, $res); } /** * Joins all segments of the path and normalizes the result. */ public static function joinPaths(string ...$paths): string { return self::normalizePath(implode('/', $paths)); } /** * Converts backslashes to slashes. */ public static function unixSlashes(string $path): string { return strtr($path, '\\', '/'); } /** * Converts slashes to platform-specific directory separators. */ public static function platformSlashes(string $path): string { return DIRECTORY_SEPARATOR === '/' ? strtr($path, '\\', '/') : str_replace(':\\\\', '://', strtr($path, '/', '\\')); // protocol:// } } PK �v�Z��[ K K Utils/Random.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; /** * Secure random string generator. */ final class Random { use Nette\StaticClass; /** * Generates a random string of given length from characters specified in second argument. * Supports intervals, such as `0-9` or `A-Z`. */ public static function generate(int $length = 10, string $charlist = '0-9a-z'): string { $charlist = preg_replace_callback( '#.-.#', fn(array $m): string => implode('', range($m[0][0], $m[0][2])), $charlist, ); $charlist = count_chars($charlist, mode: 3); $chLen = strlen($charlist); if ($length < 1) { throw new Nette\InvalidArgumentException('Length must be greater than zero.'); } elseif ($chLen < 2) { throw new Nette\InvalidArgumentException('Character list must contain at least two chars.'); } $res = ''; for ($i = 0; $i < $length; $i++) { $res .= $charlist[random_int(0, $chLen - 1)]; } return $res; } } PK �v�Ze��� � Utils/Callback.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette\Utils; use Nette; use function is_array, is_object, is_string; /** * PHP callable tools. */ final class Callback { use Nette\StaticClass; /** * Invokes internal PHP function with own error handler. */ public static function invokeSafe(string $function, array $args, callable $onError): mixed { $prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool { if ($file === __FILE__) { $msg = ini_get('html_errors') ? Html::htmlToText($message) : $message; $msg = preg_replace("#^$function\\(.*?\\): #", '', $msg); if ($onError($msg, $severity) !== false) { return null; } } return $prev ? $prev(...func_get_args()) : false; }); try { return $function(...$args); } finally { restore_error_handler(); } } /** * Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies * that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists. * @return callable * @throws Nette\InvalidArgumentException */ public static function check(mixed $callable, bool $syntax = false) { if (!is_callable($callable, $syntax)) { throw new Nette\InvalidArgumentException( $syntax ? 'Given value is not a callable type.' : sprintf("Callback '%s' is not callable.", self::toString($callable)), ); } return $callable; } /** * Converts PHP callback to textual form. Class or method may not exists. */ public static function toString(mixed $callable): string { if ($callable instanceof \Closure) { $inner = self::unwrap($callable); return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}'); } else { is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual); return $textual; } } /** * Returns reflection for method or function used in PHP callback. * @param callable $callable type check is escalated to ReflectionException * @throws \ReflectionException if callback is not valid */ public static function toReflection($callable): \ReflectionMethod|\ReflectionFunction { if ($callable instanceof \Closure) { $callable = self::unwrap($callable); } if (is_string($callable) && str_contains($callable, '::')) { return new \ReflectionMethod($callable); } elseif (is_array($callable)) { return new \ReflectionMethod($callable[0], $callable[1]); } elseif (is_object($callable) && !$callable instanceof \Closure) { return new \ReflectionMethod($callable, '__invoke'); } else { return new \ReflectionFunction($callable); } } /** * Checks whether PHP callback is function or static method. */ public static function isStatic(callable $callable): bool { return is_string(is_array($callable) ? $callable[0] : $callable); } /** * Unwraps closure created by Closure::fromCallable(). */ public static function unwrap(\Closure $closure): callable|array { $r = new \ReflectionFunction($closure); if (str_ends_with($r->name, '}')) { return $closure; } elseif ($obj = $r->getClosureThis()) { return [$obj, $r->name]; } elseif ($class = $r->getClosureScopeClass()) { return [$class->name, $r->name]; } else { return $r->name; } } } PK �v�Z6��O O HtmlStringable.phpnu �[��� <?php /** * This file is part of the Nette Framework (https://nette.org) * Copyright (c) 2004 David Grudl (https://davidgrudl.com) */ declare(strict_types=1); namespace Nette; interface HtmlStringable { /** * Returns string in HTML format */ function __toString(): string; } interface_exists(Utils\IHtmlString::class); PK Cv�Z���w w LoggerTrait.phpnu �[��� PK Cv�Z�K;� � � NullLogger.phpnu �[��� PK Cv�Z��~/ / � LoggerAwareInterface.phpnu �[��� PK Cv�Z��P P M LogLevel.phpnu �[��� PK Cv�Z �X1` ` � InvalidArgumentException.phpnu �[��� PK Cv�Z�AaHA A � LoggerInterface.phpnu �[��� PK Cv�Zۛ�� � # AbstractLogger.phpnu �[��� PK Cv�Z�>� � �$ LoggerAwareTrait.phpnu �[��� PK �v�Z,��Y Y �&