first commit

This commit is contained in:
2026-02-08 21:16:11 +01:00
commit e17b7026fd
8881 changed files with 1160453 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
use _JchOptimizeVendor\Interop\Container\ContainerInterface;
// phpcs:disable WebimpressCodingStandard.PHP.CorrectClassNameCase
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
use _JchOptimizeVendor\Laminas\Cache\Storage\AdapterPluginManager;
use _JchOptimizeVendor\Laminas\ServiceManager\Factory\InvokableFactory;
final class AdapterPluginManagerDelegatorFactory
{
public function __invoke(ContainerInterface $container, string $name, callable $callback): AdapterPluginManager
{
$pluginManager = $callback();
\assert($pluginManager instanceof AdapterPluginManager);
$pluginManager->configure(['factories' => [Filesystem::class => InvokableFactory::class], 'aliases' => ['filesystem' => Filesystem::class, 'Filesystem' => Filesystem::class]]);
return $pluginManager;
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
use _JchOptimizeVendor\Laminas\Cache\Storage\AdapterPluginManager;
final class ConfigProvider
{
/**
* @return array<string,mixed>
*/
public function __invoke(): array
{
return ['dependencies' => $this->getServiceDependencies()];
}
/**
* @return array<string,mixed>
*/
public function getServiceDependencies(): array
{
return ['delegators' => [AdapterPluginManager::class => [AdapterPluginManagerDelegatorFactory::class]]];
}
}

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem\Exception;
use _JchOptimizeVendor\Laminas\Cache\Exception\RuntimeException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
final class MetadataException extends RuntimeException
{
public const METADATA_ATIME = Filesystem::METADATA_ATIME;
public const METADATA_CTIME = Filesystem::METADATA_CTIME;
public const METADATA_MTIME = Filesystem::METADATA_MTIME;
public const METADATA_FILESIZE = Filesystem::METADATA_FILESIZE;
/** @var \ErrorException */
private $error;
/**
* @psalm-param MetadataException::METADATA_* $metadata
*/
public function __construct(string $metadata, \ErrorException $error)
{
parent::__construct(\sprintf('Could not detected metadata "%s"', $metadata), 0, $error);
$this->error = $error;
}
public function getErrorSeverity(): int
{
return $this->error->getSeverity();
}
public function getErrorMessage(): string
{
return $this->error->getMessage();
}
public function getErrorFile(): string
{
return $this->error->getFile();
}
public function getErrorLine(): int
{
return $this->error->getLine();
}
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem\Exception;
use _JchOptimizeVendor\Laminas\Cache\Exception\RuntimeException;
final class UnlinkException extends RuntimeException
{
/** @var \ErrorException */
private $error;
public function __construct(string $path, \ErrorException $error)
{
parent::__construct(\sprintf('Error unlinking file \'%s\'; file still exists', $path), 0, $error);
$this->error = $error;
}
public function getErrorSeverity(): int
{
return $this->error->getSeverity();
}
public function getErrorMessage(): string
{
return $this->error->getMessage();
}
public function getErrorFile(): string
{
return $this->error->getFile();
}
public function getErrorLine(): int
{
return $this->error->getLine();
}
}

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
use _JchOptimizeVendor\Laminas\Cache\Exception\RuntimeException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem\Exception\MetadataException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem\Exception\UnlinkException;
interface FilesystemInteractionInterface
{
/**
* @throws UnlinkException if the file could not be deleted and still exists
*/
public function delete(string $file): bool;
/**
* @throws RuntimeException
*/
public function write(string $file, string $contents, ?int $umask, ?int $permissions, bool $lock, bool $block, ?bool &$wouldBlock): bool;
/**
* @throws RuntimeException
*/
public function read(string $file, bool $lock, bool $block, ?bool &$wouldBlock): string;
public function exists(string $file): bool;
/**
* @throws MetadataException
*/
public function lastModifiedTime(string $file): int;
/**
* @throws MetadataException
*/
public function lastAccessedTime(string $file): int;
/**
* @throws MetadataException
*/
public function createdTime(string $file): int;
/**
* @throws MetadataException
*/
public function filesize(string $file): int;
public function clearStatCache(): void;
/**
* @throws RuntimeException
*/
public function availableBytes(string $directory): int;
/**
* @throws RuntimeException
*/
public function totalBytes(string $directory): int;
public function touch(string $file): bool;
/**
* @return int the previous set umask
*/
public function umask(int $umask): int;
public function createDirectory(string $directory, int $permissions, bool $recursive, ?int $umask = null): void;
}

View File

@@ -0,0 +1,302 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
use _JchOptimizeVendor\Laminas\Cache\Exception\RuntimeException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem\Exception\MetadataException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem\Exception\UnlinkException;
use _JchOptimizeVendor\Laminas\Stdlib\ErrorHandler;
use function file_get_contents;
use function file_put_contents;
/**
* @internal
*/
final class LocalFilesystemInteraction implements FilesystemInteractionInterface
{
public function delete(string $file): bool
{
ErrorHandler::start();
$res = @\unlink($file);
$err = ErrorHandler::stop();
if (!$res && \file_exists($file)) {
\assert(null !== $err);
throw new UnlinkException($file, $err);
}
return \true;
}
public function write(string $file, string $contents, ?int $umask, ?int $permissions, bool $lock, bool $block, ?bool &$wouldBlock): bool
{
$nonBlocking = $lock && $block;
$wouldBlock = null;
if (null !== $umask && null !== $permissions) {
$permissions &= ~$umask;
}
ErrorHandler::start();
// if locking and non blocking is enabled -> file_put_contents can't used
if ($lock && $nonBlocking) {
$umask = null !== $umask ? \umask($umask) : null;
$fp = \fopen($file, 'cb');
if ($umask) {
\umask($umask);
}
if (!$fp) {
$err = ErrorHandler::stop();
throw new RuntimeException("Error opening file '{$file}'", 0, $err);
}
if (null !== $permissions && !\chmod($file, $permissions)) {
\fclose($fp);
$oct = \decoct($permissions);
$err = ErrorHandler::stop();
throw new RuntimeException("chmod('{$file}', 0{$oct}) failed", 0, $err);
}
$wouldblockFromFileLock = null;
if (!\flock($fp, \LOCK_EX | \LOCK_NB, $wouldblockFromFileLock)) {
\fclose($fp);
$err = ErrorHandler::stop();
if ($wouldblockFromFileLock) {
$wouldBlock = \true;
return \false;
}
throw new RuntimeException("Error locking file '{$file}'", 0, $err);
}
if (\false === \fwrite($fp, $contents)) {
\flock($fp, \LOCK_UN);
\fclose($fp);
$err = ErrorHandler::stop();
throw new RuntimeException("Error writing file '{$file}'", 0, $err);
}
if (!\ftruncate($fp, \strlen($contents))) {
\flock($fp, \LOCK_UN);
\fclose($fp);
$err = ErrorHandler::stop();
throw new RuntimeException("Error truncating file '{$file}'", 0, $err);
}
\flock($fp, \LOCK_UN);
\fclose($fp);
// else -> file_put_contents can be used
} else {
$flags = 0;
if ($lock) {
$flags |= \LOCK_EX;
}
$umask = null !== $umask ? \umask($umask) : null;
$rs = \file_put_contents($file, $contents, $flags);
if ($umask) {
\umask($umask);
}
if (\false === $rs) {
$err = ErrorHandler::stop();
throw new RuntimeException("Error writing file '{$file}'", 0, $err);
}
if (null !== $permissions && !\chmod($file, $permissions)) {
$oct = \decoct($permissions);
$err = ErrorHandler::stop();
throw new RuntimeException("chmod('{$file}', 0{$oct}) failed", 0, $err);
}
}
ErrorHandler::stop();
return \true;
}
public function read(string $file, bool $lock, bool $block, ?bool &$wouldBlock): string
{
$wouldBlock = null;
ErrorHandler::start();
// if file locking enabled -> file_get_contents can't be used
if ($lock) {
$fp = \fopen($file, 'rb');
if (\false === $fp) {
$err = ErrorHandler::stop();
throw new RuntimeException("Error opening file '{$file}'", 0, $err);
}
if ($block) {
$wouldblockFromFileLock = null;
$locked = \flock($fp, \LOCK_SH | \LOCK_NB, $wouldblockFromFileLock);
if ($wouldblockFromFileLock) {
\fclose($fp);
ErrorHandler::stop();
$wouldBlock = \true;
return '';
}
} else {
$locked = \flock($fp, \LOCK_SH);
}
if (!$locked) {
\fclose($fp);
$err = ErrorHandler::stop();
throw new RuntimeException("Error locking file '{$file}'", 0, $err);
}
$res = \stream_get_contents($fp);
if (\false === $res) {
\flock($fp, \LOCK_UN);
\fclose($fp);
$err = ErrorHandler::stop();
throw new RuntimeException('Error getting stream contents', 0, $err);
}
\flock($fp, \LOCK_UN);
\fclose($fp);
// if file locking disabled -> file_get_contents can be used
} else {
$res = \file_get_contents($file, \false);
if (\false === $res) {
$err = ErrorHandler::stop();
throw new RuntimeException("Error getting file contents for file '{$file}'", 0, $err);
}
}
ErrorHandler::stop();
return $res;
}
public function exists(string $file): bool
{
return \file_exists($file);
}
public function lastModifiedTime(string $file): int
{
ErrorHandler::start();
$mtime = \filemtime($file);
$error = ErrorHandler::stop();
if (\false === $mtime) {
\assert(null !== $error);
throw new MetadataException(MetadataException::METADATA_MTIME, $error);
}
return $mtime;
}
public function lastAccessedTime(string $file): int
{
ErrorHandler::start();
$atime = \fileatime($file);
$error = ErrorHandler::stop();
if (\false === $atime) {
\assert(null !== $error);
throw new MetadataException(MetadataException::METADATA_ATIME, $error);
}
return $atime;
}
public function createdTime(string $file): int
{
ErrorHandler::start();
$ctime = \filectime($file);
$error = ErrorHandler::stop();
if (\false === $ctime) {
\assert(null !== $error);
throw new MetadataException(MetadataException::METADATA_CTIME, $error);
}
return $ctime;
}
public function filesize(string $file): int
{
ErrorHandler::start();
$filesize = \filesize($file);
$error = ErrorHandler::stop();
if (\false === $filesize) {
\assert(null !== $error);
throw new MetadataException(MetadataException::METADATA_FILESIZE, $error);
}
return $filesize;
}
public function clearStatCache(): void
{
\clearstatcache();
}
public function availableBytes(string $directory): int
{
ErrorHandler::start();
$bytes = \disk_free_space($directory);
$error = ErrorHandler::stop();
if (\false === $bytes) {
throw new RuntimeException('Could not detect disk free space', 0, $error);
}
return (int) $bytes;
}
public function totalBytes(string $directory): int
{
ErrorHandler::start();
$bytes = \disk_total_space($directory);
$error = ErrorHandler::stop();
if (\false === $bytes) {
throw new RuntimeException('Could not detect disk total space', 0, $error);
}
return (int) $bytes;
}
public function touch(string $file): bool
{
ErrorHandler::start();
$touch = \touch($file);
$error = ErrorHandler::stop();
if (!$touch) {
throw new RuntimeException("Error touching file '{$file}'", 0, $error);
}
return \true;
}
public function umask(int $umask): int
{
return \umask($umask);
}
public function createDirectory(string $directory, int $permissions, bool $recursive = \false, ?int $umask = null): void
{
$umaskToRestore = null;
if ($umask) {
$umaskToRestore = \umask($umask);
}
$created = \mkdir($directory, $permissions, $recursive);
$error = ErrorHandler::stop();
if ($umaskToRestore) {
\umask($umaskToRestore);
}
if (\false === $created && !\is_dir($directory)) {
throw new RuntimeException(\sprintf('Could not create directory "%s"', $directory), 0, $error);
}
ErrorHandler::start();
if (!\chmod($directory, $permissions)) {
$oct = \decoct($permissions);
$error = ErrorHandler::stop();
throw new RuntimeException("chmod('{$directory}', 0{$oct}) failed", 0, $error);
}
ErrorHandler::stop();
}
}

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter\Filesystem;
final class Module
{
public function getConfig(): array
{
$config = (new ConfigProvider())();
$dependencies = $config['dependencies'] ?? [];
\assert(\is_array($dependencies));
$config['service_manager'] = $dependencies;
unset($config['dependencies']);
return $config;
}
}

View File

@@ -0,0 +1,152 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter;
use _JchOptimizeVendor\Laminas\Cache\Storage\IteratorInterface;
use GlobIterator;
use ReturnTypeWillChange;
final class FilesystemIterator implements IteratorInterface
{
/**
* The Filesystem storage instance.
*
* @var Filesystem
*/
private $storage;
/**
* The iterator mode.
*
* @var int
*/
private $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* The GlobIterator instance.
*
* @var \GlobIterator
*/
private $globIterator;
/**
* The namespace sprefix.
*
* @var string
*/
private $prefix;
/**
* String length of namespace prefix.
*
* @var int
*/
private $prefixLength;
/**
* Constructor.
*
* @param string $path
* @param string $prefix
*/
public function __construct(Filesystem $storage, $path, $prefix)
{
$this->storage = $storage;
$this->globIterator = new \GlobIterator($path, \GlobIterator::KEY_AS_FILENAME);
$this->prefix = $prefix;
$this->prefixLength = \strlen($prefix);
}
/**
* Get storage instance.
*
* @return Filesystem
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode.
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode.
*
* @param int $mode
*
* @return FilesystemIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
// Iterator
/**
* Get current key, value or metadata.
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
if (IteratorInterface::CURRENT_AS_SELF === $this->mode) {
return $this;
}
$key = $this->key();
if (IteratorInterface::CURRENT_AS_VALUE === $this->mode) {
return $this->storage->getItem($key);
}
if (IteratorInterface::CURRENT_AS_METADATA === $this->mode) {
return $this->storage->getMetadata($key);
}
return $key;
}
/**
* Get current key.
*/
public function key(): string
{
$filename = $this->globIterator->key();
// return without namespace prefix and file suffix
return \substr($filename, $this->prefixLength, -4);
}
/**
* Move forward to next element.
*/
public function next(): void
{
$this->globIterator->next();
}
/**
* Checks if current position is valid.
*/
public function valid(): bool
{
return $this->globIterator->valid();
}
/**
* Rewind the Iterator to the first element.
*/
public function rewind(): void
{
$this->globIterator->rewind();
}
}

View File

@@ -0,0 +1,432 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter;
use _JchOptimizeVendor\Laminas\Cache\Exception;
final class FilesystemOptions extends AdapterOptions
{
public const KEY_PATTERN = '/^[a-z0-9_\\+\\-]*$/Di';
/**
* Overwrite default key pattern.
*
* @var string
*/
protected $keyPattern = self::KEY_PATTERN;
/**
* Directory to store cache files.
*
* @var string The cache directory
*/
private $cacheDir;
/**
* Call clearstatcache enabled?
*
* @var bool
*/
private $clearStatCache = \true;
/**
* How much sub-directaries should be created?
*
* @var int
*/
private $dirLevel = 1;
/**
* Permission creating new directories.
*
* @var false|int
*/
private $dirPermission = 0700;
/**
* Lock files on writing.
*
* @var bool
*/
private $fileLocking = \true;
/**
* Permission creating new files.
*
* @var false|int
*/
private $filePermission = 0600;
/**
* Namespace separator.
*
* @var string
*/
private $namespaceSeparator = '-';
/**
* Don't get 'fileatime' as 'atime' on metadata.
*
* @var bool
*/
private $noAtime = \true;
/**
* Don't get 'filectime' as 'ctime' on metadata.
*
* @var bool
*/
private $noCtime = \true;
/**
* Umask to create files and directories.
*
* @var false|int
*/
private $umask = \false;
/**
* Suffix for cache files.
*
* @var string
*/
private $suffix = 'dat';
/**
* Suffix for tag files.
*
* @var string
*/
private $tagSuffix = 'tag';
/**
* @param null|array|\Traversable $options
*/
public function __construct($options = null)
{
// disable file/directory permissions by default on windows systems
if (0 === \stripos(\PHP_OS, 'WIN')) {
$this->filePermission = \false;
$this->dirPermission = \false;
}
// $this->setCacheDir(null);
parent::__construct($options);
}
/**
* @throws Exception\InvalidArgumentException
*/
public function setCacheDir(?string $cacheDir): self
{
$cacheDir = $cacheDir ?? \sys_get_temp_dir();
$cacheDir = $this->normalizeCacheDirectory($cacheDir);
if ($this->cacheDir === $cacheDir) {
return $this;
}
$this->triggerOptionEvent('cache_dir', $cacheDir);
$this->cacheDir = $cacheDir;
return $this;
}
public function getCacheDir(): string
{
return $this->cacheDir;
}
public function setClearStatCache(bool $clearStatCache): self
{
if ($this->clearStatCache === $clearStatCache) {
return $this;
}
$this->triggerOptionEvent('clear_stat_cache', $clearStatCache);
$this->clearStatCache = $clearStatCache;
return $this;
}
public function getClearStatCache(): bool
{
return $this->clearStatCache;
}
/**
* @throws Exception\InvalidArgumentException
*/
public function setDirLevel(int $dirLevel): self
{
if ($dirLevel < 0 || $dirLevel > 16) {
throw new Exception\InvalidArgumentException("Directory level '{$dirLevel}' must be between 0 and 16");
}
if ($this->dirLevel === $dirLevel) {
return $this;
}
$this->triggerOptionEvent('dir_level', $dirLevel);
$this->dirLevel = $dirLevel;
return $this;
}
public function getDirLevel(): int
{
return $this->dirLevel;
}
/**
* Set permission to create directories on unix systems.
*
* @see http://php.net/manual/function.chmod.php
* @see FilesystemOptions::setUmask
* @see FilesystemOptions::setFilePermission
*
* @param false|int|string $dirPermission FALSE to disable explicit permission or an octal number
*/
public function setDirPermission($dirPermission): self
{
if (\false !== $dirPermission) {
if (\is_string($dirPermission)) {
$dirPermission = \octdec($dirPermission);
} else {
$dirPermission = (int) $dirPermission;
}
// validate
if (($dirPermission & 0700) !== 0700) {
throw new Exception\InvalidArgumentException('Invalid directory permission: need permission to execute, read and write by owner');
}
}
if ($this->dirPermission === $dirPermission) {
return $this;
}
$this->triggerOptionEvent('dir_permission', $dirPermission);
$this->dirPermission = $dirPermission;
return $this;
}
/**
* Get permission to create directories on unix systems.
*
* @return false|int
*/
public function getDirPermission()
{
return $this->dirPermission;
}
public function setFileLocking(bool $fileLocking): self
{
$this->triggerOptionEvent('file_locking', $fileLocking);
$this->fileLocking = $fileLocking;
return $this;
}
public function getFileLocking(): bool
{
return $this->fileLocking;
}
/**
* Set permission to create files on unix systems.
*
* @see http://php.net/manual/function.chmod.php
* @see FilesystemOptions::setUmask
* @see FilesystemOptions::setDirPermission
*
* @param false|int|string $filePermission FALSE to disable explicit permission or an octal number
*/
public function setFilePermission($filePermission): self
{
if (\false !== $filePermission) {
if (\is_string($filePermission)) {
$filePermission = \octdec($filePermission);
} else {
$filePermission = (int) $filePermission;
}
// validate
if (($filePermission & 0600) !== 0600) {
throw new Exception\InvalidArgumentException('Invalid file permission: need permission to read and write by owner');
}
if ($filePermission & 0111) {
throw new Exception\InvalidArgumentException("Invalid file permission: Cache files shouldn't be executable");
}
}
if ($this->filePermission === $filePermission) {
return $this;
}
$this->triggerOptionEvent('file_permission', $filePermission);
$this->filePermission = $filePermission;
return $this;
}
/**
* Get permission to create files on unix systems.
*
* @return false|int
*/
public function getFilePermission()
{
return $this->filePermission;
}
/**
* @param string $namespace
*/
public function setNamespace($namespace): self
{
if (\strlen($namespace) >= 250) {
throw new Exception\InvalidArgumentException('Provided namespace is too long.');
}
parent::setNamespace($namespace);
return $this;
}
public function setNamespaceSeparator(string $namespaceSeparator): self
{
$this->triggerOptionEvent('namespace_separator', $namespaceSeparator);
$this->namespaceSeparator = $namespaceSeparator;
return $this;
}
public function getNamespaceSeparator(): string
{
return $this->namespaceSeparator;
}
public function setNoAtime(bool $noAtime): self
{
if ($this->noAtime === $noAtime) {
return $this;
}
$this->triggerOptionEvent('no_atime', $noAtime);
$this->noAtime = $noAtime;
return $this;
}
public function getNoAtime(): bool
{
return $this->noAtime;
}
public function setNoCtime(bool $noCtime): self
{
if ($this->noCtime === $noCtime) {
return $this;
}
$this->triggerOptionEvent('no_ctime', $noCtime);
$this->noCtime = $noCtime;
return $this;
}
public function getNoCtime(): bool
{
return $this->noCtime;
}
/**
* Set the umask to create files and directories on unix systems.
*
* Note: On multithreaded webservers it's better to explicit set file and dir permission.
*
* @see http://php.net/manual/function.umask.php
* @see http://en.wikipedia.org/wiki/Umask
* @see FilesystemOptions::setFilePermission
* @see FilesystemOptions::setDirPermission
*
* @param false|int|string $umask false to disable umask or an octal number
*/
public function setUmask($umask): self
{
if (\false !== $umask) {
if (\is_string($umask)) {
$umask = \octdec($umask);
} else {
$umask = (int) $umask;
}
// validate
if ($umask & 0700) {
throw new Exception\InvalidArgumentException('Invalid umask: need permission to execute, read and write by owner');
}
// normalize
$umask &= ~02;
}
if ($this->umask === $umask) {
return $this;
}
$this->triggerOptionEvent('umask', $umask);
$this->umask = $umask;
return $this;
}
/**
* Get the umask to create files and directories on unix systems.
*
* @return false|int
*/
public function getUmask()
{
return $this->umask;
}
/**
* Get the suffix for cache files.
*/
public function getSuffix(): string
{
return $this->suffix;
}
/**
* Set the suffix for cache files.
*/
public function setSuffix(string $suffix): self
{
$this->suffix = $suffix;
return $this;
}
/**
* Get the suffix for tag files.
*/
public function getTagSuffix(): string
{
return $this->tagSuffix;
}
/**
* Set the suffix for cache files.
*/
public function setTagSuffix(string $tagSuffix): self
{
$this->tagSuffix = $tagSuffix;
return $this;
}
/**
* @throws Exception\InvalidArgumentException
*/
private function normalizeCacheDirectory(string $cacheDir): string
{
if (!\is_dir($cacheDir)) {
throw new Exception\InvalidArgumentException("Cache directory '{$cacheDir}' not found or not a directory");
}
if (!\is_writable($cacheDir)) {
throw new Exception\InvalidArgumentException("Cache directory '{$cacheDir}' not writable");
}
if (!\is_readable($cacheDir)) {
throw new Exception\InvalidArgumentException("Cache directory '{$cacheDir}' not readable");
}
return \rtrim(\realpath($cacheDir), \DIRECTORY_SEPARATOR);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src"/>
<directory name="test"/>
<ignoreFiles>
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
<issueHandlers>
<InternalMethod>
<errorLevel type="suppress">
<referencedMethod name="_JchOptimizeVendor\Laminas\Cache\Command\DeprecatedStorageFactoryConfigurationCheckCommand::__construct"/>
</errorLevel>
</InternalMethod>
</issueHandlers>
<plugins>
<pluginClass class="_JchOptimizeVendor\Psalm\PhpUnitPlugin\Plugin"/>
</plugins>
</psalm>

View File

@@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"local>laminas/.github:renovate-config"
]
}

View File

@@ -0,0 +1,53 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache;
use _JchOptimizeVendor\Laminas\Cache\Command\DeprecatedStorageFactoryConfigurationCheckCommand;
use _JchOptimizeVendor\Laminas\Cache\Command\DeprecatedStorageFactoryConfigurationCheckCommandFactory;
use _JchOptimizeVendor\Laminas\Cache\Service\StorageAdapterFactory;
use _JchOptimizeVendor\Laminas\Cache\Service\StorageAdapterFactoryFactory;
use _JchOptimizeVendor\Laminas\Cache\Service\StorageAdapterFactoryInterface;
use _JchOptimizeVendor\Laminas\Cache\Service\StoragePluginFactory;
use _JchOptimizeVendor\Laminas\Cache\Service\StoragePluginFactoryFactory;
use _JchOptimizeVendor\Laminas\Cache\Service\StoragePluginFactoryInterface;
use Symfony\Component\Console\Command\Command;
class ConfigProvider
{
/**
* Return default configuration for laminas-cache.
*
* @return array
*/
public function __invoke()
{
return ['dependencies' => $this->getDependencyConfig(), 'laminas-cli' => $this->getCliConfig()];
}
/**
* Return default service mappings for laminas-cache.
*
* @return array
*/
public function getDependencyConfig()
{
$dependencies = ['abstract_factories' => [Service\StorageCacheAbstractServiceFactory::class], 'factories' => [Storage\AdapterPluginManager::class => Service\StorageAdapterPluginManagerFactory::class, Storage\PluginManager::class => Service\StoragePluginManagerFactory::class, StoragePluginFactory::class => StoragePluginFactoryFactory::class, StoragePluginFactoryInterface::class => StoragePluginFactoryFactory::class, StorageAdapterFactory::class => StorageAdapterFactoryFactory::class, StorageAdapterFactoryInterface::class => StorageAdapterFactoryFactory::class]];
if (\class_exists(Command::class)) {
$dependencies['factories'] += [DeprecatedStorageFactoryConfigurationCheckCommand::class => DeprecatedStorageFactoryConfigurationCheckCommandFactory::class];
}
return $dependencies;
}
/**
* @return array<string,mixed>
*/
public function getCliConfig(): array
{
if (!\class_exists(Command::class)) {
return [];
}
return ['commands' => [DeprecatedStorageFactoryConfigurationCheckCommand::NAME => DeprecatedStorageFactoryConfigurationCheckCommand::class]];
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
interface ExceptionInterface extends \Throwable
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class ExtensionNotLoadedException extends RuntimeException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class LogicException extends \LogicException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class MissingDependencyException extends RuntimeException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class MissingKeyException extends RuntimeException
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class OutOfSpaceException extends \OverflowException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Exception;
class UnsupportedMethodCallException extends \BadMethodCallException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,18 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache;
class Module
{
/**
* Return default laminas-cache configuration for laminas-mvc context.
*
* @return array
*/
public function getConfig()
{
$provider = new ConfigProvider();
return ['service_manager' => $provider->getDependencyConfig(), 'laminas-cli' => $provider->getCliConfig()];
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
abstract class AbstractPattern implements PatternInterface
{
/** @var null|PatternOptions */
protected $options;
public function __construct(?PatternOptions $options = null)
{
$this->options = $options;
}
/**
* @return AbstractPattern
*/
public function setOptions(PatternOptions $options)
{
$this->options = $options;
return $this;
}
/**
* @return PatternOptions
*/
public function getOptions()
{
if (null === $this->options) {
$this->setOptions(new PatternOptions());
}
return $this->options;
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
abstract class AbstractStorageCapablePattern extends AbstractPattern implements StorageCapableInterface
{
/** @var StorageInterface */
protected $storage;
public function __construct(StorageInterface $storage, ?PatternOptions $options = null)
{
parent::__construct($options);
$this->storage = $storage;
}
public function getStorage(): StorageInterface
{
return $this->storage;
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
use _JchOptimizeVendor\Laminas\Cache\Exception;
use _JchOptimizeVendor\Laminas\Stdlib\ErrorHandler;
class CallbackCache extends AbstractStorageCapablePattern
{
/**
* function call handler.
*
* @param string $function Function name to call
* @param array $args Function arguments
*
* @return mixed
*
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function __call($function, array $args)
{
return $this->call($function, $args);
}
/**
* Call the specified callback or get the result from cache.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
*
* @return mixed Result
*
* @throws Exception\RuntimeException if invalid cached data
* @throws \Exception
*/
public function call($callback, array $args = [])
{
$options = $this->getOptions();
$storage = $this->getStorage();
$success = null;
$key = $this->generateCallbackKey($callback, $args);
$result = $storage->getItem($key, $success);
if ($success) {
if (!\array_key_exists(0, $result)) {
throw new Exception\RuntimeException("Invalid cached data for key '{$key}'");
}
echo $result[1] ?? '';
return $result[0];
}
$cacheOutput = $options->getCacheOutput();
if ($cacheOutput) {
\ob_start();
\ob_implicit_flush(0);
}
// TODO: do not cache on errors using [set|restore]_error_handler
try {
$ret = $callback(...$args);
} catch (\Exception $e) {
if ($cacheOutput) {
\ob_end_flush();
}
throw $e;
}
if ($cacheOutput) {
$data = [$ret, \ob_get_flush()];
} else {
$data = [$ret];
}
$storage->setItem($key, $data);
return $ret;
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
*
* @return string
*
* @throws Exception\RuntimeException
* @throws Exception\InvalidArgumentException
*/
public function generateKey($callback, array $args = [])
{
return $this->generateCallbackKey($callback, $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
*
* @return string
*
* @throws Exception\RuntimeException if callback not serializable
* @throws Exception\InvalidArgumentException if invalid callback
*/
protected function generateCallbackKey($callback, array $args)
{
if (!\is_callable($callback, \false, $callbackKey)) {
throw new Exception\InvalidArgumentException('Invalid callback');
}
// functions, methods and classnames are case-insensitive
$callbackKey = \strtolower($callbackKey);
// generate a unique key of object callbacks
if (\is_object($callback)) {
// Closures & __invoke
$object = $callback;
} elseif (isset($callback[0])) {
// array($object, 'method')
$object = $callback[0];
}
if (isset($object)) {
ErrorHandler::start();
try {
$serializedObject = \serialize($object);
} catch (\Exception $e) {
ErrorHandler::stop();
throw new Exception\RuntimeException("Can't serialize callback: see previous exception", 0, $e);
}
$error = ErrorHandler::stop();
if (!$serializedObject) {
throw new Exception\RuntimeException(\sprintf('Cannot serialize callback%s', $error ? ': '.$error->getMessage() : ''), 0, $error);
}
$callbackKey .= $serializedObject;
}
return \md5($callbackKey).$this->generateArgumentsKey($args);
}
/**
* Generate a unique key of the argument part.
*
* @return string
*
* @throws Exception\RuntimeException
*/
protected function generateArgumentsKey(array $args)
{
if (!$args) {
return '';
}
ErrorHandler::start();
try {
$serializedArgs = \serialize(\array_values($args));
} catch (\Exception $e) {
ErrorHandler::stop();
throw new Exception\RuntimeException("Can't serialize arguments: see previous exception", 0, $e);
}
$error = ErrorHandler::stop();
if (!$serializedArgs) {
throw new Exception\RuntimeException(\sprintf('Cannot serialize arguments%s', $error ? ': '.$error->getMessage() : ''), 0, $error);
}
return \md5($serializedArgs);
}
}

View File

@@ -0,0 +1,270 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
use _JchOptimizeVendor\Laminas\Cache\Exception;
class ObjectCache extends CallbackCache
{
/**
* Class method call handler.
*
* @param string $method Method name to call
* @param array $args Method arguments
*
* @return mixed
*
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function __call($method, array $args)
{
return $this->call($method, $args);
}
/**
* Writing data to properties.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it calls __set
* and removes cached data of previous __get and __isset calls.
*
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*
* @phpcs:disable Squiz.Commenting.FunctionComment.InvalidReturnVoid
*
* @param string $name
* @param mixed $value
*/
public function __set($name, $value)
{
return $this->call('__set', [$name, $value]);
}
/**
* Reading data from properties.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it calls __get.
*
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*
* @param string $name
*
* @return mixed
*/
public function __get($name)
{
return $this->call('__get', [$name]);
}
/**
* Checking existing properties.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it calls __get.
*
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*
* @param string $name
*
* @return bool
*/
public function __isset($name)
{
return $this->call('__isset', [$name]);
}
/**
* Unseting a property.
*
* NOTE:
* Magic properties will be cached too if the option cacheMagicProperties
* is enabled and the property doesn't exist in real. If so it removes
* previous cached __isset and __get calls.
*
* @see http://php.net/manual/language.oop5.overloading.php#language.oop5.overloading.members
*
* @phpcs:disable Squiz.Commenting.FunctionComment.InvalidReturnVoid
*
* @param string $name
*/
public function __unset($name)
{
return $this->call('__unset', [$name]);
}
/**
* Handle casting to string.
*
* @see http://php.net/manual/language.oop5.magic.php#language.oop5.magic.tostring
*
* @return string
*/
public function __toString()
{
return $this->call('__toString');
}
/**
* Handle invoke calls.
*
* @see http://php.net/manual/language.oop5.magic.php#language.oop5.magic.invoke
*
* @return mixed
*/
public function __invoke()
{
return $this->call('__invoke', \func_get_args());
}
/**
* @return ObjectCache
*/
public function setOptions(PatternOptions $options)
{
parent::setOptions($options);
if (!$options->getObject()) {
throw new Exception\InvalidArgumentException("Missing option 'object'");
}
return $this;
}
/**
* Call and cache a class method.
*
* @param string $method Method name to call
* @param array $args Method arguments
*
* @return mixed
*
* @throws Exception\RuntimeException
* @throws \Exception
*/
public function call($method, array $args = [])
{
$options = $this->getOptions();
$object = $options->getObject();
$method = \strtolower($method);
// handle magic methods
switch ($method) {
case '__set':
$property = \array_shift($args);
$value = \array_shift($args);
$object->{$property} = $value;
if (!$options->getObjectCacheMagicProperties() || \property_exists($object, $property)) {
// no caching if property isn't magic
// or caching magic properties is disabled
return;
}
// remove cached __get and __isset
$removeKeys = null;
if (\method_exists($object, '__get')) {
$removeKeys[] = $this->generateKey('__get', [$property]);
}
if (\method_exists($object, '__isset')) {
$removeKeys[] = $this->generateKey('__isset', [$property]);
}
if ($removeKeys) {
$storage = $this->getStorage();
$storage->removeItems($removeKeys);
}
return;
case '__get':
$property = \array_shift($args);
if (!$options->getObjectCacheMagicProperties() || \property_exists($object, $property)) {
// no caching if property isn't magic
// or caching magic properties is disabled
return $object->{$property};
}
\array_unshift($args, $property);
return parent::call([$object, '__get'], $args);
case '__isset':
$property = \array_shift($args);
if (!$options->getObjectCacheMagicProperties() || \property_exists($object, $property)) {
// no caching if property isn't magic
// or caching magic properties is disabled
return isset($object->{$property});
}
return parent::call([$object, '__isset'], [$property]);
case '__unset':
$property = \array_shift($args);
unset($object->{$property});
if (!$options->getObjectCacheMagicProperties() || \property_exists($object, $property)) {
// no caching if property isn't magic
// or caching magic properties is disabled
return;
}
// remove previous cached __get and __isset calls
$removeKeys = null;
if (\method_exists($object, '__get')) {
$removeKeys[] = $this->generateKey('__get', [$property]);
}
if (\method_exists($object, '__isset')) {
$removeKeys[] = $this->generateKey('__isset', [$property]);
}
if ($removeKeys) {
$storage = $this->getStorage();
$storage->removeItems($removeKeys);
}
return;
}
$cache = $options->getCacheByDefault();
if ($cache) {
$cache = !\in_array($method, $options->getObjectNonCacheMethods());
} else {
$cache = \in_array($method, $options->getObjectCacheMethods());
}
if (!$cache) {
return $object->{$method}(...$args);
}
return parent::call([$object, $method], $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param string $method The method
* @param array $args Callback arguments
*
* @return string
*
* @throws Exception\RuntimeException
*/
public function generateKey($method, array $args = [])
{
return $this->generateCallbackKey([$this->getOptions()->getObject(), $method], $args);
}
/**
* Generate a unique key in base of a key representing the callback part
* and a key representing the arguments part.
*
* @param callable $callback A valid callback
* @param array $args Callback arguments
*
* @return string
*
* @throws Exception\RuntimeException
*/
protected function generateCallbackKey($callback, array $args = [])
{
$callbackKey = \md5($this->getOptions()->getObjectKey().'::'.\strtolower($callback[1]));
$argumentKey = $this->generateArgumentsKey($args);
return $callbackKey.$argumentKey;
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
use _JchOptimizeVendor\Laminas\Cache\Exception;
class OutputCache extends AbstractStorageCapablePattern
{
/**
* The key stack.
*
* @var array
*/
protected $keyStack = [];
/**
* if there is a cached item with the given key display it's data and return true
* else start buffering output until end() is called or the script ends.
*
* @param string $key Key
*
* @return bool
*
* @throws Exception\MissingKeyException if key is missing
*/
public function start($key)
{
if (($key = (string) $key) === '') {
throw new Exception\MissingKeyException('Missing key to read/write output from cache');
}
$success = null;
$storage = $this->getStorage();
$data = $storage->getItem($key, $success);
if ($success) {
echo $data;
return \true;
}
\ob_start();
\ob_implicit_flush(0);
$this->keyStack[] = $key;
return \false;
}
/**
* Stops buffering output, write buffered data to cache using the given key on start()
* and displays the buffer.
*
* @return bool TRUE on success, FALSE on failure writing to cache
*
* @throws Exception\RuntimeException if output cache not started or buffering not active
*/
public function end()
{
$key = \array_pop($this->keyStack);
if (null === $key) {
throw new Exception\RuntimeException('Output cache not started');
}
$output = \ob_get_flush();
if (\false === $output) {
throw new Exception\RuntimeException('Output buffering not active');
}
$storage = $this->getStorage();
return $storage->setItem($key, $output);
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
interface PatternInterface
{
/**
* Get all pattern options.
*
* @return PatternOptions
*/
public function getOptions();
}

View File

@@ -0,0 +1,598 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
use _JchOptimizeVendor\Laminas\Cache\Exception;
use _JchOptimizeVendor\Laminas\Stdlib\AbstractOptions;
class PatternOptions extends AbstractOptions
{
/**
* Used by:
* - ObjectCache.
*
* @var bool
*/
protected $cacheByDefault = \true;
/**
* Used by:
* - CallbackCache
* - ObjectCache.
*
* @var bool
*/
protected $cacheOutput = \true;
/**
* Used by:
* - CaptureCache.
*
* @var false|int
*/
protected $umask = \false;
/**
* Used by:
* - CaptureCache.
*
* @var false|int
*/
protected $dirPermission = 0700;
/**
* Used by:
* - CaptureCache.
*
* @var false|int
*/
protected $filePermission = 0600;
/**
* Used by:
* - CaptureCache.
*
* @var bool
*/
protected $fileLocking = \true;
/**
* Used by:
* - CaptureCache.
*
* @var string
*/
protected $indexFilename = 'index.html';
/**
* Used by:
* - ObjectCache.
*
* @var null|object
*/
protected $object;
/**
* Used by:
* - ObjectCache.
*
* @var bool
*/
protected $objectCacheMagicProperties = \false;
/**
* Used by:
* - ObjectCache.
*
* @var array
*/
protected $objectCacheMethods = [];
/**
* Used by:
* - ObjectCache.
*
* @var null|string
*/
protected $objectKey;
/**
* Used by:
* - ObjectCache.
*
* @var array
*/
protected $objectNonCacheMethods = ['__tostring'];
/**
* Used by:
* - CaptureCache.
*
* @var null|string
*/
protected $publicDir;
/**
* Constructor.
*
* @param null|array|\Traversable $options
*
* @throws Exception\InvalidArgumentException
*/
public function __construct($options = null)
{
// disable file/directory permissions by default on windows systems
if (0 === \stripos(\PHP_OS, 'WIN')) {
$this->filePermission = \false;
$this->dirPermission = \false;
}
parent::__construct($options);
}
/**
* Set flag indicating whether or not to cache by default.
*
* Used by:
* - ObjectCache
*
* @param bool $cacheByDefault
*
* @return PatternOptions Provides a fluent interface
*/
public function setCacheByDefault($cacheByDefault)
{
$this->cacheByDefault = $cacheByDefault;
return $this;
}
/**
* Do we cache by default?
*
* Used by:
* - ObjectCache
*
* @return bool
*/
public function getCacheByDefault()
{
return $this->cacheByDefault;
}
/**
* Set whether or not to cache output.
*
* Used by:
* - CallbackCache
* - ObjectCache
*
* @param bool $cacheOutput
*
* @return PatternOptions Provides a fluent interface
*/
public function setCacheOutput($cacheOutput)
{
$this->cacheOutput = (bool) $cacheOutput;
return $this;
}
/**
* Will we cache output?
*
* Used by:
* - CallbackCache
* - ObjectCache
*
* @return bool
*/
public function getCacheOutput()
{
return $this->cacheOutput;
}
/**
* Set directory permission.
*
* @param false|float|int|string $dirPermission
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setDirPermission($dirPermission)
{
if (\false !== $dirPermission) {
if (\is_string($dirPermission)) {
$dirPermission = \octdec($dirPermission);
} else {
$dirPermission = (int) $dirPermission;
}
/*
* Code is untested, applying strict type check might lead to unexpected errors.
*
* @phpcs:disable SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
*/
if (($dirPermission & 0700) != 0700) {
throw new Exception\InvalidArgumentException('Invalid directory permission: need permission to execute, read and write by owner');
}
}
$this->dirPermission = $dirPermission;
return $this;
}
/**
* Gets directory permission.
*
* @return false|int
*/
public function getDirPermission()
{
return $this->dirPermission;
}
/**
* Set umask.
*
* Used by:
* - CaptureCache
*
* @param false|float|int|string $umask
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setUmask($umask)
{
if (\false !== $umask) {
if (\is_string($umask)) {
$umask = \octdec($umask);
} else {
$umask = (int) $umask;
}
// validate
if ($umask & 0700) {
throw new Exception\InvalidArgumentException('Invalid umask: need permission to execute, read and write by owner');
}
// normalize
$umask &= ~02;
}
$this->umask = $umask;
return $this;
}
/**
* Get umask.
*
* Used by:
* - CaptureCache
*
* @return false|int
*/
public function getUmask()
{
return $this->umask;
}
/**
* Set whether or not file locking should be used.
*
* Used by:
* - CaptureCache
*
* @param bool $fileLocking
*
* @return PatternOptions Provides a fluent interface
*/
public function setFileLocking($fileLocking)
{
$this->fileLocking = (bool) $fileLocking;
return $this;
}
/**
* Is file locking enabled?
*
* Used by:
* - CaptureCache
*
* @return bool
*/
public function getFileLocking()
{
return $this->fileLocking;
}
/**
* Set file permission.
*
* @param false|float|int|string $filePermission
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setFilePermission($filePermission)
{
if (\false !== $filePermission) {
if (\is_string($filePermission)) {
$filePermission = \octdec($filePermission);
} else {
$filePermission = (int) $filePermission;
}
/*
* Code is untested, applying strict type check might lead to unexpected errors.
*
* @phpcs:disable SlevomatCodingStandard.Operators.DisallowEqualOperators.DisallowedNotEqualOperator
*/
if (($filePermission & 0600) != 0600) {
throw new Exception\InvalidArgumentException('Invalid file permission: need permission to read and write by owner');
}
if ($filePermission & 0111) {
throw new Exception\InvalidArgumentException("Invalid file permission: Files shouldn't be executable");
}
}
$this->filePermission = $filePermission;
return $this;
}
/**
* Gets file permission.
*
* @return false|int
*/
public function getFilePermission()
{
return $this->filePermission;
}
/**
* Set value for index filename.
*
* @param string $indexFilename
*
* @return PatternOptions Provides a fluent interface
*/
public function setIndexFilename($indexFilename)
{
$this->indexFilename = (string) $indexFilename;
return $this;
}
/**
* Get value for index filename.
*
* @return string
*/
public function getIndexFilename()
{
return $this->indexFilename;
}
/**
* Set object to cache.
*
* @param mixed $object
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setObject($object)
{
if (!\is_object($object)) {
throw new Exception\InvalidArgumentException(\sprintf('%s expects an object; received "%s"', __METHOD__, \gettype($object)));
}
$this->object = $object;
return $this;
}
/**
* Get object to cache.
*
* @return null|object
*/
public function getObject()
{
return $this->object;
}
/**
* Set flag indicating whether or not to cache magic properties.
*
* Used by:
* - ObjectCache
*
* @param bool $objectCacheMagicProperties
*
* @return PatternOptions Provides a fluent interface
*/
public function setObjectCacheMagicProperties($objectCacheMagicProperties)
{
$this->objectCacheMagicProperties = (bool) $objectCacheMagicProperties;
return $this;
}
/**
* Should we cache magic properties?
*
* Used by:
* - ObjectCache
*
* @return bool
*/
public function getObjectCacheMagicProperties()
{
return $this->objectCacheMagicProperties;
}
/**
* Set list of object methods for which to cache return values.
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setObjectCacheMethods(array $objectCacheMethods)
{
$this->objectCacheMethods = $this->normalizeObjectMethods($objectCacheMethods);
return $this;
}
/**
* Get list of object methods for which to cache return values.
*
* @return array
*/
public function getObjectCacheMethods()
{
return $this->objectCacheMethods;
}
/**
* Set the object key part.
*
* Used to generate a callback key in order to speed up key generation.
*
* Used by:
* - ObjectCache
*
* @param null|string $objectKey The object key or NULL to use the objects class name
*
* @return PatternOptions Provides a fluent interface
*/
public function setObjectKey($objectKey)
{
if (null !== $objectKey) {
$this->objectKey = (string) $objectKey;
} else {
$this->objectKey = null;
}
return $this;
}
/**
* Get object key.
*
* Used by:
* - ObjectCache
*
* @return string
*/
public function getObjectKey()
{
if (null === $this->objectKey) {
return \get_class($this->getObject());
}
return $this->objectKey;
}
/**
* Set list of object methods for which NOT to cache return values.
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setObjectNonCacheMethods(array $objectNonCacheMethods)
{
$this->objectNonCacheMethods = $this->normalizeObjectMethods($objectNonCacheMethods);
return $this;
}
/**
* Get list of object methods for which NOT to cache return values.
*
* @return array
*/
public function getObjectNonCacheMethods()
{
return $this->objectNonCacheMethods;
}
/**
* Set location of public directory.
*
* Used by:
* - CaptureCache
*
* @param string $publicDir
*
* @return PatternOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setPublicDir($publicDir)
{
$publicDir = (string) $publicDir;
if (!\is_dir($publicDir)) {
throw new Exception\InvalidArgumentException("Public directory '{$publicDir}' not found or not a directory");
}
if (!\is_writable($publicDir)) {
throw new Exception\InvalidArgumentException("Public directory '{$publicDir}' not writable");
}
if (!\is_readable($publicDir)) {
throw new Exception\InvalidArgumentException("Public directory '{$publicDir}' not readable");
}
$this->publicDir = \rtrim(\realpath($publicDir), \DIRECTORY_SEPARATOR);
return $this;
}
/**
* Get location of public directory.
*
* Used by:
* - CaptureCache
*
* @return null|string
*/
public function getPublicDir()
{
return $this->publicDir;
}
/**
* Recursively apply strtolower on all values of an array, and return as a
* list of unique values.
*
* @return array
*/
protected function recursiveStrtolower(array $array)
{
return \array_values(\array_unique(\array_map('strtolower', $array)));
}
/**
* Normalize object methods.
*
* Recursively casts values to lowercase, then determines if any are in a
* list of methods not handled, raising an exception if so.
*
* @return array
*
* @throws Exception\InvalidArgumentException
*/
protected function normalizeObjectMethods(array $methods)
{
$methods = $this->recursiveStrtolower($methods);
$intersect = \array_intersect(['__set', '__get', '__unset', '__isset'], $methods);
if (!empty($intersect)) {
throw new Exception\InvalidArgumentException("Magic properties are handled by option 'cache_magic_properties'");
}
return $methods;
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Pattern;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
interface StorageCapableInterface extends PatternInterface
{
public function getStorage(): ?StorageInterface;
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
/**
* @internal
*/
final class DeprecatedSchemaDetector implements DeprecatedSchemaDetectorInterface
{
public function isDeprecatedStorageFactorySchema(array $configuration): bool
{
if (!\is_string($configuration['adapter'])) {
return \true;
}
if (!isset($configuration['plugins'])) {
return \false;
}
if (!\is_array($configuration['plugins'])) {
return \true;
}
foreach ($configuration['plugins'] as $index => $plugin) {
if (\is_string($index) || !\is_array($plugin)) {
return \true;
}
if (!isset($plugin['name'])) {
return \true;
}
}
return \false;
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
/**
* @internal
*/
interface DeprecatedSchemaDetectorInterface
{
/**
* @param array<string,mixed> $configuration
*/
public function isDeprecatedStorageFactorySchema(array $configuration): bool;
}

View File

@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Exception;
use _JchOptimizeVendor\Laminas\Cache\Storage\PluginAwareInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
use _JchOptimizeVendor\Laminas\ServiceManager\PluginManagerInterface;
use _JchOptimizeVendor\Webmozart\Assert\Assert;
use function assert;
/**
* @psalm-import-type PluginArrayConfigurationWithPriorityType from StorageAdapterFactoryInterface
*/
final class StorageAdapterFactory implements StorageAdapterFactoryInterface
{
public const DEFAULT_PLUGIN_PRIORITY = 1;
/** @var PluginManagerInterface */
private $adapters;
/** @var StoragePluginFactoryInterface */
private $pluginFactory;
public function __construct(PluginManagerInterface $adapters, StoragePluginFactoryInterface $pluginFactory)
{
$this->adapters = $adapters;
$this->pluginFactory = $pluginFactory;
}
public function createFromArrayConfiguration(array $configuration): StorageInterface
{
$adapterName = $configuration['adapter'] ?? $configuration['name'] ?? null;
Assert::stringNotEmpty($adapterName, 'Configuration must contain a "adapter" key.');
$adapterOptions = $configuration['options'] ?? [];
$plugins = $configuration['plugins'] ?? [];
return $this->create($adapterName, $adapterOptions, $plugins);
}
public function create(string $storage, array $options = [], array $plugins = []): StorageInterface
{
$adapter = $this->adapters->build($storage, $options);
\assert($adapter instanceof StorageInterface);
if ([] === $plugins) {
return $adapter;
}
if (!$adapter instanceof PluginAwareInterface) {
throw new Exception\RuntimeException(\sprintf("The adapter '%s' doesn't implement '%s' and therefore can't handle plugins", \get_class($adapter), PluginAwareInterface::class));
}
foreach ($plugins as $pluginConfiguration) {
$plugin = $this->pluginFactory->createFromArrayConfiguration($pluginConfiguration);
$pluginPriority = $pluginConfiguration['priority'] ?? self::DEFAULT_PLUGIN_PRIORITY;
if (!$adapter->hasPlugin($plugin)) {
$adapter->addPlugin($plugin, $pluginPriority);
}
}
return $adapter;
}
public function assertValidConfigurationStructure(array $configuration): void
{
try {
Assert::isNonEmptyMap($configuration, 'Configuration must be a non-empty array.');
$adapter = $configuration['adapter'] ?? $configuration['name'] ?? null;
if (!\is_string($adapter)) {
throw new \InvalidArgumentException('Configuration must contain a "adapter" key.');
}
Assert::stringNotEmpty($adapter, 'Storage "adapter" has to be a non-empty string.');
Assert::nullOrIsMap($configuration['options'] ?? null, 'Storage "options" must be an array with string keys.');
if (isset($configuration['plugins'])) {
Assert::isList($configuration['plugins'], 'Storage "plugins" must be a list of plugin configurations.');
$this->assertValidPluginConfigurationStructure($adapter, $configuration['plugins']);
}
} catch (\InvalidArgumentException $exception) {
throw new Exception\InvalidArgumentException($exception->getMessage(), 0, $exception);
}
}
/**
* @psalm-param non-empty-string $adapter
* @psalm-param list<mixed> $plugins
*
* @psalm-assert list<PluginArrayConfigurationWithPriorityType> $plugins
*/
private function assertValidPluginConfigurationStructure(string $adapter, array $plugins): void
{
Assert::allIsArray($plugins, 'All plugin configurations are expected to be an array.');
foreach ($plugins as $pluginConfiguration) {
try {
$this->pluginFactory->assertValidConfigurationStructure($pluginConfiguration);
if (isset($pluginConfiguration['priority'])) {
Assert::integer($pluginConfiguration['priority'], 'Plugin priority has to be integer.');
}
} catch (Exception\InvalidArgumentException|\InvalidArgumentException $exception) {
throw new Exception\InvalidArgumentException(\sprintf('Plugin configuration for adapter "%s" is invalid: %s', $adapter, $exception->getMessage()), 0, $exception);
}
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Storage\AdapterPluginManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
final class StorageAdapterFactoryFactory
{
public function __invoke(ContainerInterface $container): StorageAdapterFactory
{
return new StorageAdapterFactory($container->get(AdapterPluginManager::class), $container->get(StoragePluginFactoryInterface::class));
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Exception\InvalidArgumentException;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
/**
* @psalm-type InternalOptionalPriorityConfigurationType = array{priority?:int}
* NOTE: have to re-declare this here until https://github.com/vimeo/psalm/issues/5148 is fixed.
* @psalm-type InternalPluginArrayConfigurationType = array{name:non-empty-string,options?:array<string,mixed>}
* @psalm-type PluginArrayConfigurationWithPriorityType =
* InternalPluginArrayConfigurationType&InternalOptionalPriorityConfigurationType
* @psalm-type StorageAdapterArrayConfigurationType = array{
* name:non-empty-string,
* options?:array<string,mixed>,
* plugins?: list<PluginArrayConfigurationWithPriorityType>
* }|array{
* adapter:non-empty-string,
* options?:array<string,mixed>,
* plugins?: list<PluginArrayConfigurationWithPriorityType>
* }
*/
interface StorageAdapterFactoryInterface
{
/**
* @psalm-param StorageAdapterArrayConfigurationType $configuration
*/
public function createFromArrayConfiguration(array $configuration): StorageInterface;
/**
* @psalm-param non-empty-string $storage
*
* @param array<string,mixed> $options
*
* @psalm-param list<PluginArrayConfigurationWithPriorityType> $plugins
*/
public function create(string $storage, array $options = [], array $plugins = []): StorageInterface;
/**
* @param array<mixed> $configuration
*
* @psalm-assert StorageAdapterArrayConfigurationType $configuration
*
* @throws InvalidArgumentException if the provided configuration is invalid
*/
public function assertValidConfigurationStructure(array $configuration): void;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Storage\AdapterPluginManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
final class StorageAdapterPluginManagerFactory
{
public function __invoke(ContainerInterface $container): AdapterPluginManager
{
return new AdapterPluginManager($container);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Interop\Container\ContainerInterface;
use _JchOptimizeVendor\Laminas\ServiceManager\Factory\AbstractFactoryInterface;
use _JchOptimizeVendor\Webmozart\Assert\Assert;
/**
* Storage cache factory for multiple caches.
*/
class StorageCacheAbstractServiceFactory implements AbstractFactoryInterface
{
public const CACHES_CONFIGURATION_KEY = 'caches';
/** @var null|array<string,mixed> */
protected $config;
/**
* Configuration key for cache objects.
*
* @var string
*/
protected $configKey = self::CACHES_CONFIGURATION_KEY;
/**
* Create an object.
*
* @param string $requestedName
*
* @return object
*/
public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null)
{
$config = $this->getConfig($container);
$factory = $container->get(StorageAdapterFactoryInterface::class);
\assert($factory instanceof StorageAdapterFactoryInterface);
$configForRequestedName = $config[$requestedName] ?? [];
Assert::isMap($configForRequestedName);
$factory->assertValidConfigurationStructure($configForRequestedName);
return $factory->createFromArrayConfiguration($configForRequestedName);
}
/**
* @param string $requestedName
*
* @return bool
*/
public function canCreate(ContainerInterface $container, $requestedName)
{
$config = $this->getConfig($container);
if (empty($config)) {
return \false;
}
return isset($config[$requestedName]) && \is_array($config[$requestedName]);
}
/**
* Retrieve cache configuration, if any.
*
* @return array
*/
protected function getConfig(ContainerInterface $container)
{
if (null !== $this->config) {
return $this->config;
}
if (!$container->has('config')) {
$this->config = [];
return $this->config;
}
$config = $container->get('config');
Assert::isArrayAccessible($config);
if (!isset($config[$this->configKey])) {
$this->config = [];
return $this->config;
}
$cacheConfigurations = $config[$this->configKey];
Assert::isMap($cacheConfigurations);
return $this->config = $cacheConfigurations;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
use _JchOptimizeVendor\Webmozart\Assert\Assert;
final class StorageCacheFactory
{
public const CACHE_CONFIGURATION_KEY = 'cache';
public function __invoke(ContainerInterface $container): StorageInterface
{
$config = $container->get('config');
Assert::isArrayAccessible($config);
$cacheConfig = $config['cache'] ?? [];
Assert::isMap($cacheConfig);
$factory = $container->get(StorageAdapterFactoryInterface::class);
\assert($factory instanceof StorageAdapterFactoryInterface);
$factory->assertValidConfigurationStructure($cacheConfig);
return $factory->createFromArrayConfiguration($cacheConfig);
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Exception\InvalidArgumentException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Plugin\PluginInterface;
use _JchOptimizeVendor\Laminas\ServiceManager\PluginManagerInterface;
use _JchOptimizeVendor\Webmozart\Assert\Assert;
use InvalidArgumentException as PhpInvalidArgumentException;
final class StoragePluginFactory implements StoragePluginFactoryInterface
{
/** @var PluginManagerInterface */
private $plugins;
public function __construct(PluginManagerInterface $plugins)
{
$this->plugins = $plugins;
}
public function createFromArrayConfiguration(array $configuration): PluginInterface
{
$name = $configuration['name'];
$options = $configuration['options'] ?? [];
return $this->create($name, $options);
}
public function create(string $plugin, array $options = []): PluginInterface
{
$instance = $this->plugins->build($plugin, $options);
\assert($instance instanceof PluginInterface);
return $instance;
}
public function assertValidConfigurationStructure(array $configuration): void
{
try {
Assert::isNonEmptyMap($configuration, 'Configuration must be a non-empty array.');
Assert::keyExists($configuration, 'name', 'Configuration must contain a "name" key.');
Assert::stringNotEmpty($configuration['name'], 'Plugin "name" has to be a non-empty string.');
Assert::nullOrIsMap($configuration['options'] ?? null, 'Plugin "options" must be an array with string keys.');
} catch (PhpInvalidArgumentException $exception) {
throw new InvalidArgumentException($exception->getMessage(), 0, $exception);
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Storage\PluginManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
final class StoragePluginFactoryFactory
{
public function __invoke(ContainerInterface $container): StoragePluginFactory
{
return new StoragePluginFactory($container->get(PluginManager::class));
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Exception\InvalidArgumentException;
use _JchOptimizeVendor\Laminas\Cache\Storage\Plugin\PluginInterface;
/**
* @psalm-type PluginArrayConfigurationType = array{name:non-empty-string,options?:array<string,mixed>}
*/
interface StoragePluginFactoryInterface
{
/**
* @psalm-param PluginArrayConfigurationType $configuration
*/
public function createFromArrayConfiguration(array $configuration): PluginInterface;
/**
* @psalm-param non-empty-string $plugin
*
* @param array<string,mixed> $options
*
* @psalm-param array<string,mixed> $options
*/
public function create(string $plugin, array $options = []): PluginInterface;
/**
* @param array<mixed> $configuration
*
* @psalm-assert PluginArrayConfigurationType $configuration
*
* @throws InvalidArgumentException if the provided configuration is invalid
*/
public function assertValidConfigurationStructure(array $configuration): void;
}

View File

@@ -0,0 +1,14 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Service;
use _JchOptimizeVendor\Laminas\Cache\Storage\PluginManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
final class StoragePluginManagerFactory
{
public function __invoke(ContainerInterface $container): PluginManager
{
return new PluginManager($container);
}
}

View File

@@ -0,0 +1,327 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter;
use _JchOptimizeVendor\Laminas\Cache\Exception;
use _JchOptimizeVendor\Laminas\Cache\Storage\Event;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
use _JchOptimizeVendor\Laminas\EventManager\EventsCapableInterface;
use _JchOptimizeVendor\Laminas\Stdlib\AbstractOptions;
use _JchOptimizeVendor\Laminas\Stdlib\ErrorHandler;
/**
* Unless otherwise marked, all options in this class affect all adapters.
*/
class AdapterOptions extends AbstractOptions
{
// @codingStandardsIgnoreStart
/**
* Prioritized properties ordered by prio to be set first
* in case a bulk of options sets set at once.
*
* @var string[]
*/
protected $__prioritizedProperties__ = [];
// @codingStandardsIgnoreEnd
/**
* The adapter using these options.
*
* @var null|StorageInterface
*/
protected $adapter;
/**
* Validate key against pattern.
*
* @var string
*/
protected $keyPattern = '';
/**
* Namespace option.
*
* @var string
*/
protected $namespace = 'laminascache';
/**
* Readable option.
*
* @var bool
*/
protected $readable = \true;
/**
* TTL option.
*
* @var float|int 0 means infinite or maximum of adapter
*/
protected $ttl = 0;
/**
* Writable option.
*
* @var bool
*/
protected $writable = \true;
/**
* Adapter using this instance.
*
* @return AdapterOptions Provides a fluent interface
*/
public function setAdapter(?StorageInterface $adapter = null)
{
$this->adapter = $adapter;
return $this;
}
/**
* Set key pattern.
*
* @param string $keyPattern
*
* @return AdapterOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setKeyPattern($keyPattern)
{
$keyPattern = (string) $keyPattern;
if ($this->keyPattern !== $keyPattern) {
// validate pattern
if ('' !== $keyPattern) {
ErrorHandler::start(\E_WARNING);
$result = \preg_match($keyPattern, '');
$error = ErrorHandler::stop();
if (\false === $result) {
throw new Exception\InvalidArgumentException(\sprintf('Invalid pattern "%s"%s', $keyPattern, $error ? ': '.$error->getMessage() : ''), 0, $error);
}
}
$this->triggerOptionEvent('key_pattern', $keyPattern);
$this->keyPattern = $keyPattern;
}
return $this;
}
/**
* Get key pattern.
*
* @return string
*/
public function getKeyPattern()
{
return $this->keyPattern;
}
/**
* Set namespace.
*
* @param string $namespace
*
* @return AdapterOptions Provides a fluent interface
*/
public function setNamespace($namespace)
{
$namespace = (string) $namespace;
if ($this->namespace !== $namespace) {
$this->triggerOptionEvent('namespace', $namespace);
$this->namespace = $namespace;
}
return $this;
}
/**
* Get namespace.
*
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* Enable/Disable reading data from cache.
*
* @param bool $readable
*
* @return AdapterOptions Provides a fluent interface
*/
public function setReadable($readable)
{
$readable = (bool) $readable;
if ($this->readable !== $readable) {
$this->triggerOptionEvent('readable', $readable);
$this->readable = $readable;
}
return $this;
}
/**
* If reading data from cache enabled.
*
* @return bool
*/
public function getReadable()
{
return $this->readable;
}
/**
* Set time to live.
*
* @param float|int $ttl
*
* @return AdapterOptions Provides a fluent interface
*/
public function setTtl($ttl)
{
$this->normalizeTtl($ttl);
if ($this->ttl !== $ttl) {
$this->triggerOptionEvent('ttl', $ttl);
$this->ttl = $ttl;
}
return $this;
}
/**
* Get time to live.
*
* @return float
*/
public function getTtl()
{
return $this->ttl;
}
/**
* Enable/Disable writing data to cache.
*
* @param bool $writable
*
* @return AdapterOptions Provides a fluent interface
*/
public function setWritable($writable)
{
$writable = (bool) $writable;
if ($this->writable !== $writable) {
$this->triggerOptionEvent('writable', $writable);
$this->writable = $writable;
}
return $this;
}
/**
* If writing data to cache enabled.
*
* @return bool
*/
public function getWritable()
{
return $this->writable;
}
/**
* Cast to array.
*
* @return array
*/
public function toArray()
{
$array = [];
$transform = function ($letters) {
$letter = \array_shift($letters);
return '_'.\strtolower($letter);
};
foreach ($this as $key => $value) {
if ('__strictMode__' === $key || '__prioritizedProperties__' === $key) {
continue;
}
$normalizedKey = \preg_replace_callback('/([A-Z])/', $transform, $key);
$array[$normalizedKey] = $value;
}
return $array;
}
/**
* {@inheritdoc}
*
* NOTE: This method was overwritten just to support prioritized properties
* {@link https://github.com/zendframework/zf2/issues/6381}
*
* @param AbstractOptions|array|\Traversable $options
*
* @return AbstractOptions Provides fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setFromArray($options)
{
if ($this->__prioritizedProperties__) {
if ($options instanceof AbstractOptions) {
$options = $options->toArray();
}
if ($options instanceof \Traversable) {
$options = \iterator_to_array($options);
} elseif (!\is_array($options)) {
throw new Exception\InvalidArgumentException(\sprintf('Parameter provided to %s must be an %s, %s or %s', __METHOD__, 'array', 'Traversable', AbstractOptions::class));
}
// Sort prioritized options to top
$options = \array_change_key_case($options, \CASE_LOWER);
foreach (\array_reverse($this->__prioritizedProperties__) as $key) {
if (isset($options[$key])) {
$options = [$key => $options[$key]] + $options;
} elseif (isset($options[$key = \str_replace('_', '', $key)])) {
$options = [$key => $options[$key]] + $options;
}
}
}
return parent::setFromArray($options);
}
/**
* Triggers an option event if this options instance has a connection to
* an adapter implements EventsCapableInterface.
*
* @param string $optionName
* @param mixed $optionValue
*/
protected function triggerOptionEvent($optionName, $optionValue)
{
if ($this->adapter instanceof EventsCapableInterface) {
$event = new Event('option', $this->adapter, new \ArrayObject([$optionName => $optionValue]));
$this->adapter->getEventManager()->triggerEvent($event);
}
}
/**
* Validates and normalize a TTL.
*
* @param float|int $ttl
*
* @throws Exception\InvalidArgumentException
*/
protected function normalizeTtl(&$ttl)
{
if (!\is_int($ttl)) {
$ttl = (float) $ttl;
// convert to int if possible
if ($ttl === (float) (int) $ttl) {
$ttl = (int) $ttl;
}
}
if ($ttl < 0) {
throw new Exception\InvalidArgumentException("TTL can't be negative");
}
}
}

View File

@@ -0,0 +1,167 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Adapter;
use _JchOptimizeVendor\Laminas\Cache\Storage\IteratorInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
use ReturnTypeWillChange;
use function count;
/**
* @see ReturnTypeWillChange
*/
class KeyListIterator implements IteratorInterface, \Countable
{
/**
* The storage instance.
*
* @var StorageInterface
*/
protected $storage;
/**
* The iterator mode.
*
* @var int
*/
protected $mode = IteratorInterface::CURRENT_AS_KEY;
/**
* Keys to iterate over.
*
* @var string[]
*/
protected $keys;
/**
* Number of keys.
*
* @var int
*/
protected $count;
/**
* Current iterator position.
*
* @var int
*/
protected $position = 0;
/**
* Constructor.
*/
public function __construct(StorageInterface $storage, array $keys)
{
$this->storage = $storage;
$this->keys = $keys;
$this->count = \count($keys);
}
/**
* Get storage instance.
*
* @return StorageInterface
*/
public function getStorage()
{
return $this->storage;
}
/**
* Get iterator mode.
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode()
{
return $this->mode;
}
/**
* Set iterator mode.
*
* @param int $mode
*
* @return KeyListIterator Provides a fluent interface
*/
public function setMode($mode)
{
$this->mode = (int) $mode;
return $this;
}
/**
* Get current key, value or metadata.
*
* @return mixed
*/
#[\ReturnTypeWillChange]
public function current()
{
if (IteratorInterface::CURRENT_AS_SELF === $this->mode) {
return $this;
}
$key = $this->key();
if (IteratorInterface::CURRENT_AS_METADATA === $this->mode) {
return $this->storage->getMetadata($key);
}
if (IteratorInterface::CURRENT_AS_VALUE === $this->mode) {
return $this->storage->getItem($key);
}
return $key;
}
/**
* Get current key.
*
* @return string
*/
#[\ReturnTypeWillChange]
public function key()
{
return $this->keys[$this->position];
}
/**
* Checks if current position is valid.
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function valid()
{
return $this->position < $this->count;
}
/**
* Move forward to next element.
*/
#[\ReturnTypeWillChange]
public function next()
{
++$this->position;
}
/**
* Rewind the Iterator to the first element.
*/
#[\ReturnTypeWillChange]
public function rewind()
{
$this->position = 0;
}
/**
* Count number of items.
*
* @return int
*/
#[\ReturnTypeWillChange]
public function count()
{
return $this->count;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\ServiceManager\AbstractPluginManager;
/**
* Plugin manager implementation for cache storage adapters.
*
* Enforces that adapters retrieved are instances of
* StorageInterface. Additionally, it registers a number of default
* adapters available.
*/
final class AdapterPluginManager extends AbstractPluginManager
{
/**
* Do not share by default.
*
* @var bool
*/
protected $sharedByDefault = \false;
/** @var string */
protected $instanceOf = StorageInterface::class;
}

View File

@@ -0,0 +1,13 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface AvailableSpaceCapableInterface
{
/**
* Get available space in bytes.
*
* @return float|int
*/
public function getAvailableSpace();
}

View File

@@ -0,0 +1,520 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\Cache\Exception;
use _JchOptimizeVendor\Laminas\EventManager\EventsCapableInterface;
class Capabilities
{
public const UNKNOWN_KEY_LENGTH = -1;
public const UNLIMITED_KEY_LENGTH = 0;
/**
* The storage instance.
*
* @var StorageInterface
*/
protected $storage;
/**
* A marker to set/change capabilities.
*
* @var \stdClass
*/
protected $marker;
/**
* Base capabilities.
*
* @var null|Capabilities
*/
protected $baseCapabilities;
/**
* "lock-on-expire" support in seconds.
*
* 0 = Expired items will never be retrieved
* >0 = Time in seconds an expired item could be retrieved
* -1 = Expired items could be retrieved forever
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $lockOnExpire;
/**
* Max. key length.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $maxKeyLength;
/**
* Min. TTL (0 means items never expire).
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $minTtl;
/**
* Max. TTL (0 means infinite).
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $maxTtl;
/**
* Namespace is prefix.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $namespaceIsPrefix;
/**
* Namespace separator.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|string
*/
protected $namespaceSeparator;
/**
* Static ttl.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $staticTtl;
/**
* Supported datatypes.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|array
*/
protected $supportedDatatypes;
/**
* Supported metadata.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|array
*/
protected $supportedMetadata;
/**
* TTL precision.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|int
*/
protected $ttlPrecision;
/**
* Use request time.
*
* If it's NULL the capability isn't set and the getter
* returns the base capability or the default value.
*
* @var null|bool
*/
protected $useRequestTime;
/**
* Constructor.
*/
public function __construct(StorageInterface $storage, \stdClass $marker, array $capabilities = [], ?Capabilities $baseCapabilities = null)
{
$this->storage = $storage;
$this->marker = $marker;
$this->baseCapabilities = $baseCapabilities;
foreach ($capabilities as $name => $value) {
$this->setCapability($marker, $name, $value);
}
}
/**
* Get the storage adapter.
*
* @return StorageInterface
*/
public function getAdapter()
{
return $this->storage;
}
/**
* Get supported datatypes.
*
* @return array
*/
public function getSupportedDatatypes()
{
return $this->getCapability('supportedDatatypes', ['NULL' => \false, 'boolean' => \false, 'integer' => \false, 'double' => \false, 'string' => \true, 'array' => \false, 'object' => \false, 'resource' => \false]);
}
/**
* Set supported datatypes.
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setSupportedDatatypes(\stdClass $marker, array $datatypes)
{
$allTypes = ['array', 'boolean', 'double', 'integer', 'NULL', 'object', 'resource', 'string'];
// check/normalize datatype values
foreach ($datatypes as $type => &$toType) {
if (!\in_array($type, $allTypes)) {
throw new Exception\InvalidArgumentException("Unknown datatype '{$type}'");
}
if (\is_string($toType)) {
$toType = \strtolower($toType);
if (!\in_array($toType, $allTypes)) {
throw new Exception\InvalidArgumentException("Unknown datatype '{$toType}'");
}
} else {
$toType = (bool) $toType;
}
}
// add missing datatypes as not supported
$missingTypes = \array_diff($allTypes, \array_keys($datatypes));
foreach ($missingTypes as $type) {
$datatypes[$type] = \false;
}
return $this->setCapability($marker, 'supportedDatatypes', $datatypes);
}
/**
* Get supported metadata.
*
* @return array
*/
public function getSupportedMetadata()
{
return $this->getCapability('supportedMetadata', []);
}
/**
* Set supported metadata.
*
* @param string[] $metadata
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setSupportedMetadata(\stdClass $marker, array $metadata)
{
foreach ($metadata as $name) {
if (!\is_string($name)) {
throw new Exception\InvalidArgumentException('$metadata must be an array of strings');
}
}
return $this->setCapability($marker, 'supportedMetadata', $metadata);
}
/**
* Get minimum supported time-to-live.
*
* @return int 0 means items never expire
*/
public function getMinTtl()
{
return $this->getCapability('minTtl', 0);
}
/**
* Set minimum supported time-to-live.
*
* @param int $minTtl
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setMinTtl(\stdClass $marker, $minTtl)
{
$minTtl = (int) $minTtl;
if ($minTtl < 0) {
throw new Exception\InvalidArgumentException('$minTtl must be greater or equal 0');
}
return $this->setCapability($marker, 'minTtl', $minTtl);
}
/**
* Get maximum supported time-to-live.
*
* @return int 0 means infinite
*/
public function getMaxTtl()
{
return $this->getCapability('maxTtl', 0);
}
/**
* Set maximum supported time-to-live.
*
* @param int $maxTtl
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setMaxTtl(\stdClass $marker, $maxTtl)
{
$maxTtl = (int) $maxTtl;
if ($maxTtl < 0) {
throw new Exception\InvalidArgumentException('$maxTtl must be greater or equal 0');
}
return $this->setCapability($marker, 'maxTtl', $maxTtl);
}
/**
* Is the time-to-live handled static (on write)
* or dynamic (on read).
*
* @return bool
*/
public function getStaticTtl()
{
return $this->getCapability('staticTtl', \false);
}
/**
* Set if the time-to-live handled static (on write) or dynamic (on read).
*
* @param bool $flag
*
* @return Capabilities Fluent interface
*/
public function setStaticTtl(\stdClass $marker, $flag)
{
return $this->setCapability($marker, 'staticTtl', (bool) $flag);
}
/**
* Get time-to-live precision.
*
* @return float
*/
public function getTtlPrecision()
{
return $this->getCapability('ttlPrecision', 1);
}
/**
* Set time-to-live precision.
*
* @param float $ttlPrecision
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setTtlPrecision(\stdClass $marker, $ttlPrecision)
{
$ttlPrecision = (float) $ttlPrecision;
if ($ttlPrecision <= 0) {
throw new Exception\InvalidArgumentException('$ttlPrecision must be greater than 0');
}
return $this->setCapability($marker, 'ttlPrecision', $ttlPrecision);
}
/**
* Get use request time.
*
* @return bool
*/
public function getUseRequestTime()
{
return $this->getCapability('useRequestTime', \false);
}
/**
* Set use request time.
*
* @param bool $flag
*
* @return Capabilities Fluent interface
*/
public function setUseRequestTime(\stdClass $marker, $flag)
{
return $this->setCapability($marker, 'useRequestTime', (bool) $flag);
}
/**
* Get "lock-on-expire" support in seconds.
*
* @return int 0 = Expired items will never be retrieved
* >0 = Time in seconds an expired item could be retrieved
* -1 = Expired items could be retrieved forever
*/
public function getLockOnExpire()
{
return $this->getCapability('lockOnExpire', 0);
}
/**
* Set "lock-on-expire" support in seconds.
*
* @param int $timeout
*
* @return Capabilities Fluent interface
*/
public function setLockOnExpire(\stdClass $marker, $timeout)
{
return $this->setCapability($marker, 'lockOnExpire', (int) $timeout);
}
/**
* Get maximum key length.
*
* @return int -1 means unknown, 0 means infinite
*/
public function getMaxKeyLength()
{
return $this->getCapability('maxKeyLength', self::UNKNOWN_KEY_LENGTH);
}
/**
* Set maximum key length.
*
* @param int $maxKeyLength
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setMaxKeyLength(\stdClass $marker, $maxKeyLength)
{
$maxKeyLength = (int) $maxKeyLength;
if ($maxKeyLength < -1) {
throw new Exception\InvalidArgumentException('$maxKeyLength must be greater or equal than -1');
}
return $this->setCapability($marker, 'maxKeyLength', $maxKeyLength);
}
/**
* Get if namespace support is implemented as prefix.
*
* @return bool
*/
public function getNamespaceIsPrefix()
{
return $this->getCapability('namespaceIsPrefix', \true);
}
/**
* Set if namespace support is implemented as prefix.
*
* @param bool $flag
*
* @return Capabilities Fluent interface
*/
public function setNamespaceIsPrefix(\stdClass $marker, $flag)
{
return $this->setCapability($marker, 'namespaceIsPrefix', (bool) $flag);
}
/**
* Get namespace separator if namespace is implemented as prefix.
*
* @return string
*/
public function getNamespaceSeparator()
{
return $this->getCapability('namespaceSeparator', '');
}
/**
* Set the namespace separator if namespace is implemented as prefix.
*
* @param string $separator
*
* @return Capabilities Fluent interface
*/
public function setNamespaceSeparator(\stdClass $marker, $separator)
{
return $this->setCapability($marker, 'namespaceSeparator', (string) $separator);
}
/**
* Get a capability.
*
* @param string $property
* @param mixed $default
*
* @return mixed
*/
protected function getCapability($property, $default = null)
{
if (null !== $this->{$property}) {
return $this->{$property};
}
if ($this->baseCapabilities) {
$getMethod = 'get'.$property;
return $this->baseCapabilities->{$getMethod}();
}
return $default;
}
/**
* Change a capability.
*
* @param string $property
* @param mixed $value
*
* @return Capabilities Fluent interface
*
* @throws Exception\InvalidArgumentException
*/
protected function setCapability(\stdClass $marker, $property, $value)
{
if ($this->marker !== $marker) {
throw new Exception\InvalidArgumentException('Invalid marker');
}
if ($this->{$property} !== $value) {
$this->{$property} = $value;
// trigger event
if ($this->storage instanceof EventsCapableInterface) {
$this->storage->getEventManager()->trigger('capability', $this->storage, new \ArrayObject([$property => $value]));
}
}
return $this;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface ClearByNamespaceInterface
{
/**
* Remove items of given namespace.
*
* @param string $namespace
*
* @return bool
*/
public function clearByNamespace($namespace);
}

View File

@@ -0,0 +1,15 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface ClearByPrefixInterface
{
/**
* Remove items matching given prefix.
*
* @param string $prefix
*
* @return bool
*/
public function clearByPrefix($prefix);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface ClearExpiredInterface
{
/**
* Remove expired items.
*
* @return bool
*/
public function clearExpired();
}

View File

@@ -0,0 +1,58 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\EventManager\Event as BaseEvent;
class Event extends BaseEvent
{
/**
* Constructor.
*
* Accept a storage adapter and its parameters.
*
* @param string $name Event name
*/
public function __construct($name, StorageInterface $storage, \ArrayObject $params)
{
parent::__construct($name, $storage, $params);
}
/**
* Set the event target/context.
*
* @see Laminas\EventManager\Event::setTarget()
*
* @param StorageInterface $target
*
* @return Event
*/
public function setTarget($target)
{
return $this->setStorage($target);
}
/**
* Alias of setTarget.
*
* @see Laminas\EventManager\Event::setTarget()
*
* @return Event
*/
public function setStorage(StorageInterface $storage)
{
$this->target = $storage;
return $this;
}
/**
* Alias of getTarget.
*
* @return StorageInterface
*/
public function getStorage()
{
return $this->getTarget();
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use Exception;
class ExceptionEvent extends PostEvent
{
/**
* The exception to be thrown.
*
* @var \Exception
*/
protected $exception;
/**
* Throw the exception or use the result.
*
* @var bool
*/
protected $throwException = \true;
/**
* Constructor.
*
* Accept a target and its parameters.
*
* @param string $name
* @param mixed $result
*/
public function __construct($name, StorageInterface $storage, \ArrayObject $params, &$result, \Exception $exception)
{
parent::__construct($name, $storage, $params, $result);
$this->setException($exception);
}
/**
* Set the exception to be thrown.
*
* @return ExceptionEvent
*/
public function setException(\Exception $exception)
{
$this->exception = $exception;
return $this;
}
/**
* Get the exception to be thrown.
*
* @return \Exception
*/
public function getException()
{
return $this->exception;
}
/**
* Throw the exception or use the result.
*
* @param bool $flag
*
* @return ExceptionEvent
*/
public function setThrowException($flag)
{
$this->throwException = (bool) $flag;
return $this;
}
/**
* Throw the exception or use the result.
*
* @return bool
*/
public function getThrowException()
{
return $this->throwException;
}
}

View File

@@ -0,0 +1,13 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface FlushableInterface
{
/**
* Flush the whole storage.
*
* @return bool
*/
public function flush();
}

View File

@@ -0,0 +1,10 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
/**
* @method IteratorInterface getIterator() Get the storage iterator
*/
interface IterableInterface extends \IteratorAggregate
{
}

View File

@@ -0,0 +1,36 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use Iterator;
interface IteratorInterface extends \Iterator
{
public const CURRENT_AS_SELF = 0;
public const CURRENT_AS_KEY = 1;
public const CURRENT_AS_VALUE = 2;
public const CURRENT_AS_METADATA = 3;
/**
* Get storage instance.
*
* @return StorageInterface
*/
public function getStorage();
/**
* Get iterator mode.
*
* @return int Value of IteratorInterface::CURRENT_AS_*
*/
public function getMode();
/**
* Set iterator mode.
*
* @param int $mode Value of IteratorInterface::CURRENT_AS_*
*
* @return IteratorInterface Fluent interface
*/
public function setMode($mode);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface OptimizableInterface
{
/**
* Optimize the storage.
*
* @return bool
*/
public function optimize();
}

View File

@@ -0,0 +1,37 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\EventManager\AbstractListenerAggregate;
abstract class AbstractPlugin extends AbstractListenerAggregate implements PluginInterface
{
/** @var PluginOptions */
protected $options;
/**
* Set pattern options.
*
* @return AbstractPlugin Provides a fluent interface
*/
public function setOptions(PluginOptions $options)
{
$this->options = $options;
return $this;
}
/**
* Get all pattern options.
*
* @return PluginOptions
*/
public function getOptions()
{
if (null === $this->options) {
$this->setOptions(new PluginOptions());
}
return $this->options;
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\Cache\Storage\ClearExpiredInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\PostEvent;
use _JchOptimizeVendor\Laminas\EventManager\EventManagerInterface;
class ClearExpiredByFactor extends AbstractPlugin
{
public function attach(EventManagerInterface $events, $priority = 1)
{
$callback = [$this, 'clearExpiredByFactor'];
$this->listeners[] = $events->attach('setItem.post', $callback, $priority);
$this->listeners[] = $events->attach('setItems.post', $callback, $priority);
$this->listeners[] = $events->attach('addItem.post', $callback, $priority);
$this->listeners[] = $events->attach('addItems.post', $callback, $priority);
}
/**
* Clear expired items by factor after writing new item(s).
*
* @phpcs:disable Generic.NamingConventions.ConstructorName.OldStyle
*/
public function clearExpiredByFactor(PostEvent $event)
{
$storage = $event->getStorage();
if (!$storage instanceof ClearExpiredInterface) {
return;
}
$factor = $this->getOptions()->getClearingFactor();
if ($factor && 1 === \random_int(1, $factor)) {
$storage->clearExpired();
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\Cache\Storage\ExceptionEvent;
use _JchOptimizeVendor\Laminas\EventManager\EventManagerInterface;
class ExceptionHandler extends AbstractPlugin
{
public function attach(EventManagerInterface $events, $priority = 1)
{
$callback = [$this, 'onException'];
// read
$this->listeners[] = $events->attach('getItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('getItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('hasItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('hasItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('getMetadata.exception', $callback, $priority);
$this->listeners[] = $events->attach('getMetadatas.exception', $callback, $priority);
// write
$this->listeners[] = $events->attach('setItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('setItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('addItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('addItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('replaceItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('replaceItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('touchItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('touchItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('removeItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('removeItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('checkAndSetItem.exception', $callback, $priority);
// increment / decrement item(s)
$this->listeners[] = $events->attach('incrementItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('incrementItems.exception', $callback, $priority);
$this->listeners[] = $events->attach('decrementItem.exception', $callback, $priority);
$this->listeners[] = $events->attach('decrementItems.exception', $callback, $priority);
// utility
$this->listeners[] = $events->attach('clearExpired.exception', $callback, $priority);
}
/**
* On exception.
*/
public function onException(ExceptionEvent $event)
{
$options = $this->getOptions();
$callback = $options->getExceptionCallback();
if ($callback) {
\call_user_func($callback, $event->getException());
}
$event->setThrowException($options->getThrowExceptions());
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\Cache\Storage\Event;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
use _JchOptimizeVendor\Laminas\EventManager\EventManagerInterface;
use function ignore_user_abort;
class IgnoreUserAbort extends AbstractPlugin
{
/**
* The storage who activated ignore_user_abort.
*
* @var null|StorageInterface
*/
protected $activatedTarget;
public function attach(EventManagerInterface $events, $priority = 1)
{
$cbOnBefore = [$this, 'onBefore'];
$cbOnAfter = [$this, 'onAfter'];
$this->listeners[] = $events->attach('setItem.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('setItem.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('setItem.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('setItems.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('setItems.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('setItems.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('addItem.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('addItem.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('addItem.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('addItems.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('addItems.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('addItems.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('replaceItem.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('replaceItem.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('replaceItem.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('replaceItems.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('replaceItems.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('replaceItems.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('checkAndSetItem.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('checkAndSetItem.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('checkAndSetItem.exception', $cbOnAfter, $priority);
// increment / decrement item(s)
$this->listeners[] = $events->attach('incrementItem.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('incrementItem.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('incrementItem.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('incrementItems.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('incrementItems.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('incrementItems.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('decrementItem.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('decrementItem.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('decrementItem.exception', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('decrementItems.pre', $cbOnBefore, $priority);
$this->listeners[] = $events->attach('decrementItems.post', $cbOnAfter, $priority);
$this->listeners[] = $events->attach('decrementItems.exception', $cbOnAfter, $priority);
}
/**
* Activate ignore_user_abort if not already done
* and save the target who activated it.
*/
public function onBefore(Event $event)
{
if (null === $this->activatedTarget && !\ignore_user_abort(\true)) {
$this->activatedTarget = $event->getStorage();
}
}
/**
* Reset ignore_user_abort if it's activated and if it's the same target
* who activated it.
*
* If exit_on_abort is enabled and the connection has been aborted
* exit the script.
*/
public function onAfter(Event $event)
{
if ($this->activatedTarget === $event->getStorage()) {
// exit if connection aborted
if ($this->getOptions()->getExitOnAbort() && \connection_aborted()) {
exit;
}
// reset ignore_user_abort
\ignore_user_abort(\false);
// remove activated target
$this->activatedTarget = null;
}
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\Cache\Storage\OptimizableInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\PostEvent;
use _JchOptimizeVendor\Laminas\EventManager\EventManagerInterface;
class OptimizeByFactor extends AbstractPlugin
{
public function attach(EventManagerInterface $events, $priority = 1)
{
$callback = [$this, 'optimizeByFactor'];
$this->listeners[] = $events->attach('removeItem.post', $callback, $priority);
$this->listeners[] = $events->attach('removeItems.post', $callback, $priority);
}
/**
* Optimize by factor on a success _RESULT_.
*
* @phpcs:disable Generic.NamingConventions.ConstructorName.OldStyle
*/
public function optimizeByFactor(PostEvent $event)
{
$storage = $event->getStorage();
if (!$storage instanceof OptimizableInterface) {
return;
}
$factor = $this->getOptions()->getOptimizingFactor();
if ($factor && 1 === \random_int(1, $factor)) {
$storage->optimize();
}
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\EventManager\ListenerAggregateInterface;
interface PluginInterface extends ListenerAggregateInterface
{
/**
* Set options.
*
* @return PluginInterface
*/
public function setOptions(PluginOptions $options);
/**
* Get options.
*
* @return PluginOptions
*/
public function getOptions();
}

View File

@@ -0,0 +1,317 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\Cache\Exception;
use _JchOptimizeVendor\Laminas\Serializer\Adapter\AdapterInterface as SerializerAdapter;
use _JchOptimizeVendor\Laminas\Serializer\Serializer as SerializerFactory;
use _JchOptimizeVendor\Laminas\Stdlib\AbstractOptions;
class PluginOptions extends AbstractOptions
{
/**
* Used by:
* - ClearByFactor.
*
* @var int
*/
protected $clearingFactor = 0;
/**
* Used by:
* - ExceptionHandler.
*
* @var null|callable
*/
protected $exceptionCallback;
/**
* Used by:
* - IgnoreUserAbort.
*
* @var bool
*/
protected $exitOnAbort = \true;
/**
* Used by:
* - OptimizeByFactor.
*
* @var int
*/
protected $optimizingFactor = 0;
/**
* Used by:
* - Serializer.
*
* @var SerializerAdapter|string
*/
protected $serializer;
/**
* Used by:
* - Serializer.
*
* @var array
*/
protected $serializerOptions = [];
/**
* Used by:
* - ExceptionHandler.
*
* @var bool
*/
protected $throwExceptions = \true;
/**
* Set automatic clearing factor.
*
* Used by:
* - ClearExpiredByFactor
*
* @param int $clearingFactor
*
* @return PluginOptions Provides a fluent interface
*/
public function setClearingFactor($clearingFactor)
{
$this->clearingFactor = $this->normalizeFactor($clearingFactor);
return $this;
}
/**
* Get automatic clearing factor.
*
* Used by:
* - ClearExpiredByFactor
*
* @return int
*/
public function getClearingFactor()
{
return $this->clearingFactor;
}
/**
* Set callback to call on intercepted exception.
*
* Used by:
* - ExceptionHandler
*
* @param null|callable $exceptionCallback
*
* @return PluginOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setExceptionCallback($exceptionCallback)
{
if (null !== $exceptionCallback && !\is_callable($exceptionCallback, \true)) {
throw new Exception\InvalidArgumentException('Not a valid callback');
}
$this->exceptionCallback = $exceptionCallback;
return $this;
}
/**
* Get callback to call on intercepted exception.
*
* Used by:
* - ExceptionHandler
*
* @return null|callable
*/
public function getExceptionCallback()
{
return $this->exceptionCallback;
}
/**
* Exit if connection aborted and ignore_user_abort is disabled.
*
* @param bool $exitOnAbort
*
* @return PluginOptions Provides a fluent interface
*/
public function setExitOnAbort($exitOnAbort)
{
$this->exitOnAbort = (bool) $exitOnAbort;
return $this;
}
/**
* Exit if connection aborted and ignore_user_abort is disabled.
*
* @return bool
*/
public function getExitOnAbort()
{
return $this->exitOnAbort;
}
/**
* Set automatic optimizing factor.
*
* Used by:
* - OptimizeByFactor
*
* @param int $optimizingFactor
*
* @return PluginOptions Provides a fluent interface
*/
public function setOptimizingFactor($optimizingFactor)
{
$this->optimizingFactor = $this->normalizeFactor($optimizingFactor);
return $this;
}
/**
* Set automatic optimizing factor.
*
* Used by:
* - OptimizeByFactor
*
* @return int
*/
public function getOptimizingFactor()
{
return $this->optimizingFactor;
}
/**
* Set serializer.
*
* Used by:
* - Serializer
*
* @param SerializerAdapter|string $serializer
*
* @return PluginOptions Provides a fluent interface
*
* @throws Exception\InvalidArgumentException
*/
public function setSerializer($serializer)
{
if (!\is_string($serializer) && !$serializer instanceof SerializerAdapter) {
/*
* @psalm-suppress RedundantConditionGivenDocblockType, DocblockTypeContradiction
* Until we do lack native type-hint we should check the `$serializer` twice.
*/
throw new Exception\InvalidArgumentException(\sprintf('%s expects either a string serializer name or Laminas\\Serializer\\Adapter\\AdapterInterface instance; received "%s"', __METHOD__, \is_object($serializer) ? \get_class($serializer) : \gettype($serializer)));
}
$this->serializer = $serializer;
return $this;
}
/**
* Get serializer.
*
* Used by:
* - Serializer
*
* @return SerializerAdapter
*/
public function getSerializer()
{
if (!$this->serializer instanceof SerializerAdapter) {
// use default serializer
if (!$this->serializer) {
$this->setSerializer(SerializerFactory::getDefaultAdapter());
// instantiate by class name + serializer_options
} else {
$options = $this->getSerializerOptions();
$this->setSerializer(SerializerFactory::factory($this->serializer, $options));
}
}
return $this->serializer;
}
/**
* Set configuration options for instantiating a serializer adapter.
*
* Used by:
* - Serializer
*
* @param mixed $serializerOptions
*
* @return PluginOptions Provides a fluent interface
*/
public function setSerializerOptions($serializerOptions)
{
$this->serializerOptions = $serializerOptions;
return $this;
}
/**
* Get configuration options for instantiating a serializer adapter.
*
* Used by:
* - Serializer
*
* @return array
*/
public function getSerializerOptions()
{
return $this->serializerOptions;
}
/**
* Set flag indicating we should re-throw exceptions.
*
* Used by:
* - ExceptionHandler
*
* @param bool $throwExceptions
*
* @return PluginOptions Provides a fluent interface
*/
public function setThrowExceptions($throwExceptions)
{
$this->throwExceptions = (bool) $throwExceptions;
return $this;
}
/**
* Should we re-throw exceptions?
*
* Used by:
* - ExceptionHandler
*
* @return bool
*/
public function getThrowExceptions()
{
return $this->throwExceptions;
}
/**
* Normalize a factor.
*
* Cast to int and ensure we have a value greater than zero.
*
* @param int $factor
*
* @return int
*
* @throws Exception\InvalidArgumentException
*/
protected function normalizeFactor($factor)
{
$factor = (int) $factor;
if ($factor < 0) {
throw new Exception\InvalidArgumentException("Invalid factor '{$factor}': must be greater or equal 0");
}
return $factor;
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage\Plugin;
use _JchOptimizeVendor\Laminas\Cache\Storage\Capabilities;
use _JchOptimizeVendor\Laminas\Cache\Storage\Event;
use _JchOptimizeVendor\Laminas\Cache\Storage\PostEvent;
use _JchOptimizeVendor\Laminas\Cache\Storage\StorageInterface;
use _JchOptimizeVendor\Laminas\EventManager\EventManagerInterface;
class Serializer extends AbstractPlugin
{
/** @var array */
protected $capabilities = [];
public function attach(EventManagerInterface $events, $priority = 1)
{
// The higher the priority the sooner the plugin will be called on pre events
// but the later it will be called on post events.
$prePriority = $priority;
$postPriority = -$priority;
// read
$this->listeners[] = $events->attach('getItem.post', [$this, 'onReadItemPost'], $postPriority);
$this->listeners[] = $events->attach('getItems.post', [$this, 'onReadItemsPost'], $postPriority);
// write
$this->listeners[] = $events->attach('setItem.pre', [$this, 'onWriteItemPre'], $prePriority);
$this->listeners[] = $events->attach('setItems.pre', [$this, 'onWriteItemsPre'], $prePriority);
$this->listeners[] = $events->attach('addItem.pre', [$this, 'onWriteItemPre'], $prePriority);
$this->listeners[] = $events->attach('addItems.pre', [$this, 'onWriteItemsPre'], $prePriority);
$this->listeners[] = $events->attach('replaceItem.pre', [$this, 'onWriteItemPre'], $prePriority);
$this->listeners[] = $events->attach('replaceItems.pre', [$this, 'onWriteItemsPre'], $prePriority);
$this->listeners[] = $events->attach('checkAndSetItem.pre', [$this, 'onWriteItemPre'], $prePriority);
// increment / decrement item(s)
$this->listeners[] = $events->attach('incrementItem.pre', [$this, 'onIncrementItemPre'], $prePriority);
$this->listeners[] = $events->attach('incrementItems.pre', [$this, 'onIncrementItemsPre'], $prePriority);
$this->listeners[] = $events->attach('decrementItem.pre', [$this, 'onDecrementItemPre'], $prePriority);
$this->listeners[] = $events->attach('decrementItems.pre', [$this, 'onDecrementItemsPre'], $prePriority);
// overwrite capabilities
$this->listeners[] = $events->attach('getCapabilities.post', [$this, 'onGetCapabilitiesPost'], $postPriority);
}
/**
* On read item post.
*/
public function onReadItemPost(PostEvent $event)
{
$result = $event->getResult();
if (null !== $result) {
$serializer = $this->getOptions()->getSerializer();
$result = $serializer->unserialize($result);
$event->setResult($result);
}
}
/**
* On read items post.
*/
public function onReadItemsPost(PostEvent $event)
{
$serializer = $this->getOptions()->getSerializer();
$result = $event->getResult();
foreach ($result as &$value) {
$value = $serializer->unserialize($value);
}
$event->setResult($result);
}
/**
* On write item pre.
*/
public function onWriteItemPre(Event $event)
{
$serializer = $this->getOptions()->getSerializer();
$params = $event->getParams();
$params['value'] = $serializer->serialize($params['value']);
}
/**
* On write items pre.
*/
public function onWriteItemsPre(Event $event)
{
$serializer = $this->getOptions()->getSerializer();
$params = $event->getParams();
foreach ($params['keyValuePairs'] as &$value) {
$value = $serializer->serialize($value);
}
}
/**
* On increment item pre.
*
* @return mixed
*/
public function onIncrementItemPre(Event $event)
{
/** @var StorageInterface $storage */
$storage = $event->getTarget();
$params = $event->getParams();
$casToken = null;
$success = null;
$oldValue = $storage->getItem($params['key'], $success, $casToken) ?? null;
$newValue = $oldValue + $params['value'];
$event->stopPropagation(\true);
if ($storage->checkAndSetItem($casToken, $params['key'], $oldValue + $params['value'])) {
return $newValue;
}
return \false;
}
/**
* On increment items pre.
*
* @return mixed
*/
public function onIncrementItemsPre(Event $event)
{
$storage = $event->getTarget();
$params = $event->getParams();
$keyValuePairs = $storage->getItems(\array_keys($params['keyValuePairs']));
foreach ($params['keyValuePairs'] as $key => &$value) {
if (isset($keyValuePairs[$key])) {
$keyValuePairs[$key] += $value;
} else {
$keyValuePairs[$key] = $value;
}
}
$failedKeys = $storage->setItems($keyValuePairs);
foreach ($failedKeys as $failedKey) {
unset($keyValuePairs[$failedKey]);
}
$event->stopPropagation(\true);
return $keyValuePairs;
}
/**
* On decrement item pre.
*
* @return mixed
*/
public function onDecrementItemPre(Event $event)
{
/** @var StorageInterface $storage */
$storage = $event->getTarget();
$params = $event->getParams();
$success = null;
$casToken = null;
$oldValue = $storage->getItem($params['key'], $success, $casToken) ?? 0;
$newValue = $oldValue - $params['value'];
$event->stopPropagation(\true);
if ($storage->checkAndSetItem($casToken, $params['key'], $newValue)) {
return $newValue;
}
return \false;
}
/**
* On decrement items pre.
*
* @return mixed
*/
public function onDecrementItemsPre(Event $event)
{
$storage = $event->getTarget();
$params = $event->getParams();
$keyValuePairs = $storage->getItems(\array_keys($params['keyValuePairs']));
foreach ($params['keyValuePairs'] as $key => &$value) {
if (isset($keyValuePairs[$key])) {
$keyValuePairs[$key] -= $value;
} else {
$keyValuePairs[$key] = -$value;
}
}
$failedKeys = $storage->setItems($keyValuePairs);
foreach ($failedKeys as $failedKey) {
unset($keyValuePairs[$failedKey]);
}
$event->stopPropagation(\true);
return $keyValuePairs;
}
/**
* On get capabilities.
*/
public function onGetCapabilitiesPost(PostEvent $event)
{
$baseCapabilities = $event->getResult();
$index = \spl_object_hash($baseCapabilities);
if (!isset($this->capabilities[$index])) {
$this->capabilities[$index] = new Capabilities(
$baseCapabilities->getAdapter(),
new \stdClass(),
// marker
['supportedDatatypes' => ['NULL' => \true, 'boolean' => \true, 'integer' => \true, 'double' => \true, 'string' => \true, 'array' => \true, 'object' => 'object', 'resource' => \false]],
$baseCapabilities
);
}
$event->setResult($this->capabilities[$index]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\Cache\Exception;
interface PluginAwareInterface extends PluginCapableInterface
{
/**
* Register a plugin.
*
* @param int $priority
*
* @return StorageInterface
*
* @throws Exception\LogicException
*/
public function addPlugin(Plugin\PluginInterface $plugin, $priority = 1);
/**
* Unregister an already registered plugin.
*
* @return StorageInterface
*
* @throws Exception\LogicException
*/
public function removePlugin(Plugin\PluginInterface $plugin);
}

View File

@@ -0,0 +1,22 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\EventManager\EventsCapableInterface;
interface PluginCapableInterface extends EventsCapableInterface
{
/**
* Check if a plugin is registered.
*
* @return bool
*/
public function hasPlugin(Plugin\PluginInterface $plugin);
/**
* Return registry of plugins.
*
* @return \SplObjectStorage
*/
public function getPluginRegistry();
}

View File

@@ -0,0 +1,52 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\Cache\Storage\Plugin\PluginInterface;
use _JchOptimizeVendor\Laminas\Cache\Storage\Plugin\PluginOptions;
use _JchOptimizeVendor\Laminas\ServiceManager\AbstractPluginManager;
use _JchOptimizeVendor\Laminas\ServiceManager\Factory\InvokableFactory;
/**
* Plugin manager implementation for cache plugins.
*
* Enforces that plugins retrieved are instances of
* Plugin\PluginInterface. Additionally, it registers a number of default
* plugins available.
*/
final class PluginManager extends AbstractPluginManager
{
/** @var array<string,string> */
protected $aliases = ['clear_expired_by_factor' => Plugin\ClearExpiredByFactor::class, 'clearexpiredbyfactor' => Plugin\ClearExpiredByFactor::class, 'clearExpiredByFactor' => Plugin\ClearExpiredByFactor::class, 'ClearExpiredByFactor' => Plugin\ClearExpiredByFactor::class, 'exception_handler' => Plugin\ExceptionHandler::class, 'exceptionhandler' => Plugin\ExceptionHandler::class, 'exceptionHandler' => Plugin\ExceptionHandler::class, 'ExceptionHandler' => Plugin\ExceptionHandler::class, 'ignore_user_abort' => Plugin\IgnoreUserAbort::class, 'ignoreuserabort' => Plugin\IgnoreUserAbort::class, 'ignoreUserAbort' => Plugin\IgnoreUserAbort::class, 'IgnoreUserAbort' => Plugin\IgnoreUserAbort::class, 'optimize_by_factor' => Plugin\OptimizeByFactor::class, 'optimizebyfactor' => Plugin\OptimizeByFactor::class, 'optimizeByFactor' => Plugin\OptimizeByFactor::class, 'OptimizeByFactor' => Plugin\OptimizeByFactor::class, 'serializer' => Plugin\Serializer::class, 'Serializer' => Plugin\Serializer::class];
/** @var array<string,string> */
protected $factories = [Plugin\ClearExpiredByFactor::class => InvokableFactory::class, Plugin\ExceptionHandler::class => InvokableFactory::class, Plugin\IgnoreUserAbort::class => InvokableFactory::class, Plugin\OptimizeByFactor::class => InvokableFactory::class, Plugin\Serializer::class => InvokableFactory::class];
/**
* Do not share by default.
*
* @var bool
*/
protected $sharedByDefault = \false;
/** @var string */
protected $instanceOf = PluginInterface::class;
/**
* @param string $name
*
* @return mixed
*/
public function build($name, ?array $options = null)
{
$options = $options ?? [];
/** @psalm-suppress MixedAssignment */
$plugin = parent::build($name);
if ([] !== $options && $plugin instanceof PluginInterface) {
$plugin->setOptions(new PluginOptions($options));
}
return $plugin;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
class PostEvent extends Event
{
/**
* The result/return value.
*
* @var mixed
*/
protected $result;
/**
* Constructor.
*
* Accept a target and its parameters.
*
* @param string $name
* @param mixed $result
*/
public function __construct($name, StorageInterface $storage, \ArrayObject $params, &$result)
{
parent::__construct($name, $storage, $params);
$this->setResult($result);
}
/**
* Set the result/return value.
*
* @param mixed $value
*
* @return PostEvent
*/
public function setResult(&$value)
{
$this->result = &$value;
return $this;
}
/**
* Get the result/return value.
*
* @return mixed
*/
public function &getResult()
{
return $this->result;
}
}

View File

@@ -0,0 +1,260 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
use _JchOptimizeVendor\Laminas\Cache\Exception\ExceptionInterface;
interface StorageInterface
{
/**
* Set options.
*
* @param Adapter\AdapterOptions|array|\Traversable $options
*
* @return StorageInterface Fluent interface
*/
public function setOptions($options);
/**
* Get options.
*
* @return Adapter\AdapterOptions
*/
public function getOptions();
// reading
/**
* Get an item.
*
* @param string $key
* @param bool $success
* @param mixed $casToken
*
* @return mixed Data on success, null on failure
*
* @throws ExceptionInterface
*/
public function getItem($key, &$success = null, &$casToken = null);
/**
* Get multiple items.
*
* @return array Associative array of keys and values
*
* @throws ExceptionInterface
*/
public function getItems(array $keys);
/**
* Test if an item exists.
*
* @param string $key
*
* @return bool
*
* @throws ExceptionInterface
*/
public function hasItem($key);
/**
* Test multiple items.
*
* @return array Array of found keys
*
* @throws ExceptionInterface
*/
public function hasItems(array $keys);
/**
* Get metadata of an item.
*
* @param string $key
*
* @return array|bool Metadata on success, false on failure
*
* @throws ExceptionInterface
*/
public function getMetadata($key);
/**
* Get multiple metadata.
*
* @return array Associative array of keys and metadata
*
* @throws ExceptionInterface
*/
public function getMetadatas(array $keys);
// writing
/**
* Store an item.
*
* @param string $key
* @param mixed $value
*
* @return bool
*
* @throws ExceptionInterface
*/
public function setItem($key, $value);
/**
* Store multiple items.
*
* @return array Array of not stored keys
*
* @throws ExceptionInterface
*/
public function setItems(array $keyValuePairs);
/**
* Add an item.
*
* @param string $key
* @param mixed $value
*
* @return bool
*
* @throws ExceptionInterface
*/
public function addItem($key, $value);
/**
* Add multiple items.
*
* @return array Array of not stored keys
*
* @throws ExceptionInterface
*/
public function addItems(array $keyValuePairs);
/**
* Replace an existing item.
*
* @param string $key
* @param mixed $value
*
* @return bool
*
* @throws ExceptionInterface
*/
public function replaceItem($key, $value);
/**
* Replace multiple existing items.
*
* @return array Array of not stored keys
*
* @throws ExceptionInterface
*/
public function replaceItems(array $keyValuePairs);
/**
* Set an item only if token matches.
*
* It uses the token received from getItem() to check if the item has
* changed before overwriting it.
*
* @see getItem()
* @see setItem()
*
* @param mixed $token
* @param string $key
* @param mixed $value
*
* @return bool
*
* @throws ExceptionInterface
*/
public function checkAndSetItem($token, $key, $value);
/**
* Reset lifetime of an item.
*
* @param string $key
*
* @return bool
*
* @throws ExceptionInterface
*/
public function touchItem($key);
/**
* Reset lifetime of multiple items.
*
* @return array Array of not updated keys
*
* @throws ExceptionInterface
*/
public function touchItems(array $keys);
/**
* Remove an item.
*
* @param string $key
*
* @return bool
*
* @throws ExceptionInterface
*/
public function removeItem($key);
/**
* Remove multiple items.
*
* @return array Array of not removed keys
*
* @throws ExceptionInterface
*/
public function removeItems(array $keys);
/**
* Increment an item.
*
* @param string $key
* @param int $value
*
* @return bool|int The new value on success, false on failure
*
* @throws ExceptionInterface
*/
public function incrementItem($key, $value);
/**
* Increment multiple items.
*
* @return array Associative array of keys and new values
*
* @throws ExceptionInterface
*/
public function incrementItems(array $keyValuePairs);
/**
* Decrement an item.
*
* @param string $key
* @param int $value
*
* @return bool|int The new value on success, false on failure
*
* @throws ExceptionInterface
*/
public function decrementItem($key, $value);
/**
* Decrement multiple items.
*
* @return array Associative array of keys and new values
*
* @throws ExceptionInterface
*/
public function decrementItems(array $keyValuePairs);
// status
/**
* Capabilities of this storage.
*
* @return Capabilities
*/
public function getCapabilities();
}

View File

@@ -0,0 +1,39 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface TaggableInterface
{
/**
* Set tags to an item by given key.
* An empty array will remove all tags.
*
* @param string $key
* @param string[] $tags
*
* @return bool
*/
public function setTags($key, array $tags);
/**
* Get tags of an item by given key.
*
* @param string $key
*
* @return false|string[]
*/
public function getTags($key);
/**
* Remove items matching given tags.
*
* If $disjunction only one of the given tags must match
* else all given tags must match.
*
* @param string[] $tags
* @param bool $disjunction
*
* @return bool
*/
public function clearByTags(array $tags, $disjunction = \false);
}

View File

@@ -0,0 +1,13 @@
<?php
namespace _JchOptimizeVendor\Laminas\Cache\Storage;
interface TotalSpaceCapableInterface
{
/**
* Get total space in bytes.
*
* @return float|int
*/
public function getTotalSpace();
}

View File

@@ -0,0 +1,20 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Abstract aggregate listener.
*/
abstract class AbstractListenerAggregate implements ListenerAggregateInterface
{
/** @var callable[] */
protected $listeners = [];
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $callback) {
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Representation of an event.
*
* Encapsulates the target context and parameters passed, and provides some
* behavior for interacting with the event manager.
*/
class Event implements EventInterface
{
/** @var string Event name */
protected $name;
/** @var object|string The event target */
protected $target;
/** @var array|\ArrayAccess|object The event parameters */
protected $params = [];
/** @var bool Whether or not to stop propagation */
protected $stopPropagation = \false;
/**
* Constructor.
*
* Accept a target and its parameters.
*
* @param string $name Event name
* @param object|string $target
* @param array|\ArrayAccess $params
*/
public function __construct($name = null, $target = null, $params = null)
{
if (null !== $name) {
$this->setName($name);
}
if (null !== $target) {
$this->setTarget($target);
}
if (null !== $params) {
$this->setParams($params);
}
}
/**
* Get event name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Get the event target.
*
* This may be either an object, or the name of a static method.
*
* @return object|string
*/
public function getTarget()
{
return $this->target;
}
/**
* Set parameters.
*
* Overwrites parameters
*
* @param array|\ArrayAccess|object $params
*
* @throws Exception\InvalidArgumentException
*/
public function setParams($params)
{
if (!\is_array($params) && !\is_object($params)) {
throw new Exception\InvalidArgumentException(\sprintf('Event parameters must be an array or object; received "%s"', \gettype($params)));
}
$this->params = $params;
}
/**
* Get all parameters.
*
* @return array|\ArrayAccess|object
*/
public function getParams()
{
return $this->params;
}
/**
* Get an individual parameter.
*
* If the parameter does not exist, the $default value will be returned.
*
* @param int|string $name
* @param mixed $default
*
* @return mixed
*/
public function getParam($name, $default = null)
{
// Check in params that are arrays or implement array access
if (\is_array($this->params) || $this->params instanceof \ArrayAccess) {
if (!isset($this->params[$name])) {
return $default;
}
return $this->params[$name];
}
// Check in normal objects
if (!isset($this->params->{$name})) {
return $default;
}
return $this->params->{$name};
}
/**
* Set the event name.
*
* @param string $name
*/
public function setName($name)
{
$this->name = (string) $name;
}
/**
* Set the event target/context.
*
* @param null|object|string $target
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Set an individual parameter to a value.
*
* @param int|string $name
* @param mixed $value
*/
public function setParam($name, $value)
{
if (\is_array($this->params) || $this->params instanceof \ArrayAccess) {
// Arrays or objects implementing array access
$this->params[$name] = $value;
return;
}
// Objects
$this->params->{$name} = $value;
}
/**
* Stop further event propagation.
*
* @param bool $flag
*/
public function stopPropagation($flag = \true)
{
$this->stopPropagation = (bool) $flag;
}
/**
* Is propagation stopped?
*
* @return bool
*/
public function propagationIsStopped()
{
return $this->stopPropagation;
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Representation of an event.
*/
interface EventInterface
{
/**
* Get event name.
*
* @return string
*/
public function getName();
/**
* Get target/context from which event was triggered.
*
* @return null|object|string
*/
public function getTarget();
/**
* Get parameters passed to the event.
*
* @return array|\ArrayAccess
*/
public function getParams();
/**
* Get a single parameter by name.
*
* @param string $name
* @param mixed $default Default value to return if parameter does not exist
*
* @return mixed
*/
public function getParam($name, $default = null);
/**
* Set the event name.
*
* @param string $name
*/
public function setName($name);
/**
* Set the event target/context.
*
* @param null|object|string $target
*/
public function setTarget($target);
/**
* Set event parameters.
*
* @param array|\ArrayAccess $params
*/
public function setParams($params);
/**
* Set a single parameter by key.
*
* @param string $name
* @param mixed $value
*/
public function setParam($name, $value);
/**
* Indicate whether or not the parent EventManagerInterface should stop propagating events.
*
* @param bool $flag
*/
public function stopPropagation($flag = \true);
/**
* Has this event indicated event propagation should stop?
*
* @return bool
*/
public function propagationIsStopped();
}

View File

@@ -0,0 +1,269 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
use ArrayObject;
/**
* Event manager: notification system.
*
* Use the EventManager when you want to create a per-instance notification
* system for your objects.
*/
class EventManager implements EventManagerInterface
{
/**
* Subscribed events and their listeners.
*
* STRUCTURE:
* [
* <string name> => [
* <int priority> => [
* 0 => [<callable listener>, ...]
* ],
* ...
* ],
* ...
* ]
*
* NOTE:
* This structure helps us to reuse the list of listeners
* instead of first iterating over it and generating a new one
* -> In result it improves performance by up to 25% even if it looks a bit strange
*
* @var array[]
*/
protected $events = [];
/** @var EventInterface Prototype to use when creating an event at trigger(). */
protected $eventPrototype;
/**
* Identifiers, used to pull shared signals from SharedEventManagerInterface instance.
*
* @var array
*/
protected $identifiers = [];
/**
* Shared event manager.
*
* @var null|SharedEventManagerInterface
*/
protected $sharedManager;
/**
* Constructor.
*
* Allows optionally specifying identifier(s) to use to pull signals from a
* SharedEventManagerInterface.
*/
public function __construct(?SharedEventManagerInterface $sharedEventManager = null, array $identifiers = [])
{
if ($sharedEventManager) {
$this->sharedManager = $sharedEventManager;
$this->setIdentifiers($identifiers);
}
$this->eventPrototype = new Event();
}
public function setEventPrototype(EventInterface $prototype)
{
$this->eventPrototype = $prototype;
}
/**
* Retrieve the shared event manager, if composed.
*
* @return null|SharedEventManagerInterface $sharedEventManager
*/
public function getSharedManager()
{
return $this->sharedManager;
}
public function getIdentifiers()
{
return $this->identifiers;
}
public function setIdentifiers(array $identifiers)
{
$this->identifiers = \array_unique($identifiers);
}
public function addIdentifiers(array $identifiers)
{
$this->identifiers = \array_unique(\array_merge($this->identifiers, $identifiers));
}
public function trigger($eventName, $target = null, $argv = [])
{
$event = clone $this->eventPrototype;
$event->setName($eventName);
if (null !== $target) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event);
}
public function triggerUntil(callable $callback, $eventName, $target = null, $argv = [])
{
$event = clone $this->eventPrototype;
$event->setName($eventName);
if (null !== $target) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
public function attach($eventName, callable $listener, $priority = 1)
{
if (!\is_string($eventName)) {
throw new Exception\InvalidArgumentException(\sprintf('%s expects a string for the event; received %s', __METHOD__, \is_object($eventName) ? \get_class($eventName) : \gettype($eventName)));
}
$this->events[$eventName][(int) $priority][0][] = $listener;
return $listener;
}
/**
* @throws Exception\InvalidArgumentException for invalid event types
*/
public function detach(callable $listener, $eventName = null, $force = \false)
{
// If event is wildcard, we need to iterate through each listeners
if (null === $eventName || '*' === $eventName && !$force) {
foreach (\array_keys($this->events) as $eventName) {
$this->detach($listener, $eventName, \true);
}
return;
}
if (!\is_string($eventName)) {
throw new Exception\InvalidArgumentException(\sprintf('%s expects a string for the event; received %s', __METHOD__, \is_object($eventName) ? \get_class($eventName) : \gettype($eventName)));
}
if (!isset($this->events[$eventName])) {
return;
}
foreach ($this->events[$eventName] as $priority => $listeners) {
foreach ($listeners[0] as $index => $evaluatedListener) {
if ($evaluatedListener !== $listener) {
continue;
}
// Found the listener; remove it.
unset($this->events[$eventName][$priority][0][$index]);
// If the queue for the given priority is empty, remove it.
if (empty($this->events[$eventName][$priority][0])) {
unset($this->events[$eventName][$priority]);
break;
}
}
}
// If the queue for the given event is empty, remove it.
if (empty($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
public function clearListeners($eventName)
{
if (isset($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
/**
* Prepare arguments.
*
* Use this method if you want to be able to modify arguments from within a
* listener. It returns an ArrayObject of the arguments, which may then be
* passed to trigger().
*
* @return \ArrayObject
*/
public function prepareArgs(array $args)
{
return new \ArrayObject($args);
}
/**
* Trigger listeners.
*
* Actual functionality for triggering listeners, to which trigger() delegate.
*
* @return ResponseCollection
*/
protected function triggerListeners(EventInterface $event, ?callable $callback = null)
{
$name = $event->getName();
if (empty($name)) {
throw new Exception\RuntimeException('Event is missing a name; cannot trigger!');
}
if (isset($this->events[$name])) {
$listOfListenersByPriority = $this->events[$name];
if (isset($this->events['*'])) {
foreach ($this->events['*'] as $priority => $listOfListeners) {
$listOfListenersByPriority[$priority][] = $listOfListeners[0];
}
}
} elseif (isset($this->events['*'])) {
$listOfListenersByPriority = $this->events['*'];
} else {
$listOfListenersByPriority = [];
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
\krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(\false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(\true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(\true);
return $responses;
}
}
}
}
return $responses;
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Interface to automate setter injection for an EventManager instance.
*/
interface EventManagerAwareInterface extends EventsCapableInterface
{
/**
* Inject an EventManager instance.
*/
public function setEventManager(EventManagerInterface $eventManager);
}

View File

@@ -0,0 +1,60 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* A trait for objects that provide events.
*
* If you use this trait in an object, you will probably want to also implement
* EventManagerAwareInterface, which will make it so the default initializer in
* a Laminas MVC application will automatically inject an instance of the
* EventManager into your object when it is pulled from the ServiceManager.
*
* @see Laminas\Mvc\Service\ServiceManagerConfig
*/
trait EventManagerAwareTrait
{
/** @var EventManagerInterface */
protected $events;
/**
* Set the event manager instance used by this context.
*
* For convenience, this method will also set the class name / LSB name as
* identifiers, in addition to any string or array of strings set to the
* $this->eventIdentifier property.
*/
public function setEventManager(EventManagerInterface $events)
{
$identifiers = [self::class, static::class];
if (isset($this->eventIdentifier)) {
if (\is_string($this->eventIdentifier) || \is_array($this->eventIdentifier) || $this->eventIdentifier instanceof \Traversable) {
$identifiers = \array_unique(\array_merge($identifiers, (array) $this->eventIdentifier));
} elseif (\is_object($this->eventIdentifier)) {
$identifiers[] = $this->eventIdentifier;
}
// silently ignore invalid eventIdentifier types
}
$events->setIdentifiers($identifiers);
$this->events = $events;
if (\method_exists($this, 'attachDefaultListeners')) {
$this->attachDefaultListeners();
}
}
/**
* Retrieve the event manager.
*
* Lazy-loads an EventManager instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager()
{
if (!$this->events instanceof EventManagerInterface) {
$this->setEventManager(new EventManager());
}
return $this->events;
}
}

View File

@@ -0,0 +1,145 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Interface for messengers.
*/
interface EventManagerInterface extends SharedEventsCapableInterface
{
/**
* Create and trigger an event.
*
* Use this method when you do not want to create an EventInterface
* instance prior to triggering. You will be required to pass:
*
* - the event name
* - the event target (can be null)
* - any event parameters you want to provide (empty array by default)
*
* It will create the Event instance for you and then trigger all listeners
* related to the event.
*
* @param string $eventName
* @param null|object|string $target
* @param array|object $argv
*
* @return ResponseCollection
*/
public function trigger($eventName, $target = null, $argv = []);
/**
* Create and trigger an event, applying a callback to each listener result.
*
* Use this method when you do not want to create an EventInterface
* instance prior to triggering. You will be required to pass:
*
* - the event name
* - the event target (can be null)
* - any event parameters you want to provide (empty array by default)
*
* It will create the Event instance for you, and trigger all listeners
* related to the event.
*
* The result of each listener is passed to $callback; if $callback returns
* a boolean true value, the manager must short-circuit listener execution.
*
* @param string $eventName
* @param null|object|string $target
* @param array|object $argv
*
* @return ResponseCollection
*/
public function triggerUntil(callable $callback, $eventName, $target = null, $argv = []);
/**
* Trigger an event.
*
* Provided an EventInterface instance, this method will trigger listeners
* based on the event name, raising an exception if the event name is missing.
*
* @return ResponseCollection
*/
public function triggerEvent(EventInterface $event);
/**
* Trigger an event, applying a callback to each listener result.
*
* Provided an EventInterface instance, this method will trigger listeners
* based on the event name, raising an exception if the event name is missing.
*
* The result of each listener is passed to $callback; if $callback returns
* a boolean true value, the manager must short-circuit listener execution.
*
* @return ResponseCollection
*/
public function triggerEventUntil(callable $callback, EventInterface $event);
/**
* Attach a listener to an event.
*
* The first argument is the event, and the next argument is a
* callable that will respond to that event.
*
* The last argument indicates a priority at which the event should be
* executed; by default, this value is 1; however, you may set it for any
* integer value. Higher values have higher priority (i.e., execute first).
*
* You can specify "*" for the event name. In such cases, the listener will
* be triggered for every event *that has registered listeners at the time
* it is attached*. As such, register wildcard events last whenever possible!
*
* @param string $eventName event to which to listen
* @param int $priority priority at which to register listener
*
* @return callable
*/
public function attach($eventName, callable $listener, $priority = 1);
/**
* Detach a listener.
*
* If no $event or '*' is provided, detaches listener from all events;
* otherwise, detaches only from the named event.
*
* @param null|string $eventName event from which to detach; null and '*'
* indicate all events
*/
public function detach(callable $listener, $eventName = null);
/**
* Clear all listeners for a given event.
*
* @param string $eventName
*/
public function clearListeners($eventName);
/**
* Provide an event prototype to use with trigger().
*
* When `trigger()` needs to create an event instance, it should clone the
* prototype provided to this method.
*/
public function setEventPrototype(EventInterface $prototype);
/**
* Get the identifier(s) for this EventManager.
*
* @return array
*/
public function getIdentifiers();
/**
* Set the identifiers (overrides any currently set identifiers).
*
* @param string[] $identifiers
*/
public function setIdentifiers(array $identifiers);
/**
* Add identifier(s) (appends to any currently set identifiers).
*
* @param string[] $identifiers
*/
public function addIdentifiers(array $identifiers);
}

View File

@@ -0,0 +1,18 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Interface indicating that an object composes an EventManagerInterface instance.
*/
interface EventsCapableInterface
{
/**
* Retrieve the event manager.
*
* Lazy-loads an EventManager instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager();
}

View File

@@ -0,0 +1,7 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Exception;
class DomainException extends \DomainException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Exception;
/**
* Base exception interface.
*/
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Exception;
/**
* Invalid argument exception.
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Exception;
/**
* Invalid callback exception.
*/
class InvalidCallbackException extends DomainException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Exception;
use RuntimeException as SplRuntimeException;
class RuntimeException extends SplRuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,51 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Filter;
use _JchOptimizeVendor\Laminas\EventManager\ResponseCollection;
/**
* Interface for intercepting filter chains.
*/
interface FilterInterface
{
/**
* Execute the filter chain.
*
* @param object|string $context
*
* @return mixed
*/
public function run($context, array $params = []);
/**
* Attach an intercepting filter.
*/
public function attach(callable $callback);
/**
* Detach an intercepting filter.
*
* @return bool
*/
public function detach(callable $filter);
/**
* Get all intercepting filters.
*
* @return array
*/
public function getFilters();
/**
* Clear all filters.
*/
public function clearFilters();
/**
* Get all filter responses.
*
* @return ResponseCollection
*/
public function getResponses();
}

View File

@@ -0,0 +1,110 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager\Filter;
use _JchOptimizeVendor\Laminas\EventManager\Exception;
use _JchOptimizeVendor\Laminas\Stdlib\FastPriorityQueue;
/**
* Specialized priority queue implementation for use with an intercepting
* filter chain.
*
* Allows removal
*/
class FilterIterator extends FastPriorityQueue
{
/**
* Does the queue contain a given value?
*
* @param mixed $datum
*
* @return bool
*/
public function contains($datum)
{
foreach ($this as $item) {
if ($item === $datum) {
return \true;
}
}
return \false;
}
/**
* Insert a value into the queue.
*
* Requires a callable.
*
* @param callable $value
* @param mixed $priority
*
* @throws Exception\InvalidArgumentException for non-callable $value
*/
public function insert($value, $priority)
{
if (!\is_callable($value)) {
throw new Exception\InvalidArgumentException(\sprintf('%s can only aggregate callables; received %s', self::class, \is_object($value) ? \get_class($value) : \gettype($value)));
}
parent::insert($value, $priority);
}
/**
* Remove a value from the queue.
*
* This is an expensive operation. It must first iterate through all values,
* and then re-populate itself. Use only if absolutely necessary.
*
* @param mixed $datum
*
* @return bool
*/
public function remove($datum)
{
$this->setExtractFlags(self::EXTR_BOTH);
// Iterate and remove any matches
$removed = \false;
$items = [];
$this->rewind();
while (!$this->isEmpty()) {
$item = $this->extract();
if ($item['data'] === $datum) {
$removed = \true;
continue;
}
$items[] = $item;
}
// Repopulate
foreach ($items as $item) {
$this->insert($item['data'], $item['priority']);
}
$this->setExtractFlags(self::EXTR_DATA);
return $removed;
}
/**
* Iterate the next filter in the chain.
*
* Iterates and calls the next filter in the chain.
*
* @param mixed $context
* @param FilterIterator $chain
*
* @return mixed
*/
public function next($context = null, array $params = [], $chain = null)
{
if (empty($context) || $chain instanceof FilterIterator && $chain->isEmpty()) {
return;
}
// We can't extract from an empty heap
if ($this->isEmpty()) {
return;
}
$next = $this->extract();
return $next($context, $params, $chain);
}
}

View File

@@ -0,0 +1,102 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* FilterChain: intercepting filter manager.
*/
class FilterChain implements Filter\FilterInterface
{
/** @var Filter\FilterIterator All filters */
protected $filters;
/**
* Constructor.
*
* Initializes Filter\FilterIterator in which filters will be aggregated
*/
public function __construct()
{
$this->filters = new Filter\FilterIterator();
}
/**
* Apply the filters.
*
* Begins iteration of the filters.
*
* @param mixed $context Object under observation
* @param mixed $argv Associative array of arguments
*
* @return mixed
*/
public function run($context, array $argv = [])
{
$chain = clone $this->getFilters();
if ($chain->isEmpty()) {
return;
}
$next = $chain->extract();
return $next($context, $argv, $chain);
}
/**
* Connect a filter to the chain.
*
* @param callable $callback PHP Callback
* @param int $priority Priority in the queue at which to execute;
* defaults to 1 (higher numbers == higher priority)
*
* @return CallbackHandler (to allow later unsubscribe)
*
* @throws Exception\InvalidCallbackException
*/
public function attach(callable $callback, $priority = 1)
{
$this->filters->insert($callback, $priority);
return $callback;
}
/**
* Detach a filter from the chain.
*
* @return bool Returns true if filter found and unsubscribed; returns false otherwise
*/
public function detach(callable $filter)
{
return $this->filters->remove($filter);
}
/**
* Retrieve all filters.
*
* @return Filter\FilterIterator
*/
public function getFilters()
{
return $this->filters;
}
/**
* Clear all filters.
*/
public function clearFilters()
{
$this->filters = new Filter\FilterIterator();
}
/**
* Return current responses.
*
* Only available while the chain is still being iterated. Returns the
* current ResponseCollection.
*
* @return null|ResponseCollection
*/
public function getResponses()
{
return null;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
/**
* Lazy listener instance for use with LazyListenerAggregate.
*
* Used as an internal class for the LazyAggregate to allow lazy creation of
* listeners via a dependency injection container.
*
* Lazy event listener definitions add the following members to what the
* LazyListener accepts:
*
* - event: the event name to attach to.
* - priority: the priority at which to attach the listener, if not the default.
*/
class LazyEventListener extends LazyListener
{
/** @var string Event name to which to attach. */
private $event;
/** @var null|int Priority at which to attach. */
private $priority;
public function __construct(array $definition, ContainerInterface $container, array $env = [])
{
parent::__construct($definition, $container, $env);
if (!isset($definition['event']) || !\is_string($definition['event']) || empty($definition['event'])) {
throw new Exception\InvalidArgumentException('Lazy listener definition is missing a valid "event" member; cannot create LazyListener');
}
$this->event = $definition['event'];
$this->priority = isset($definition['priority']) ? (int) $definition['priority'] : null;
}
/**
* @return string
*/
public function getEvent()
{
return $this->event;
}
/**
* @param int $default
*
* @return int
*/
public function getPriority($default = 1)
{
return null !== $this->priority ? $this->priority : $default;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
/**
* Lazy listener instance.
*
* Used to allow lazy creation of listeners via a dependency injection
* container.
*
* Lazy listener definitions have the following members:
*
* - listener: the service name of the listener to use.
* - method: the method name of the listener to invoke for the specified event.
*
* If desired, you can pass $env at instantiation; this will be passed to the
* container's `build()` method, if it has one, when creating the listener
* instance.
*
* Pass instances directly to the event manager's `attach()` method as the
* listener argument.
*/
class LazyListener
{
/** @var ContainerInterface Container from which to pull listener. */
private $container;
/** @var array Variables/options to use during service creation, if any. */
private $env;
/** @var callable Marshaled listener callback. */
private $listener;
/** @var string Method name to invoke on listener. */
private $method;
/** @var string Service name of listener. */
private $service;
public function __construct(array $definition, ContainerInterface $container, array $env = [])
{
if (!isset($definition['listener']) || !\is_string($definition['listener']) || empty($definition['listener'])) {
throw new Exception\InvalidArgumentException('Lazy listener definition is missing a valid "listener" member; cannot create LazyListener');
}
if (!isset($definition['method']) || !\is_string($definition['method']) || empty($definition['method'])) {
throw new Exception\InvalidArgumentException('Lazy listener definition is missing a valid "method" member; cannot create LazyListener');
}
$this->service = $definition['listener'];
$this->method = $definition['method'];
$this->container = $container;
$this->env = $env;
}
/**
* Use the listener as an invokable, allowing direct attachment to an event manager.
*
* @return callable
*/
public function __invoke(EventInterface $event)
{
$listener = $this->fetchListener();
$method = $this->method;
return $listener->{$method}($event);
}
/**
* @return callable
*/
private function fetchListener()
{
if ($this->listener) {
return $this->listener;
}
// In the future, typehint against Laminas\ServiceManager\ServiceLocatorInterface,
// which defines this message starting in v3.
if (\method_exists($this->container, 'build') && !empty($this->env)) {
$this->listener = $this->container->build($this->service, $this->env);
return $this->listener;
}
$this->listener = $this->container->get($this->service);
return $this->listener;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
use _JchOptimizeVendor\Psr\Container\ContainerInterface;
/**
* Aggregate listener for attaching lazy listeners.
*
* Lazy listeners are listeners where creation is deferred until they are
* triggered; this removes the most costly mechanism of pulling a listener
* from a container unless the listener is actually invoked.
*
* Usage is:
*
* <code>
* $aggregate = new LazyListenerAggregate(
* $lazyEventListenersOrDefinitions,
* $container
* );
* $aggregate->attach($events);
* </code>
*/
class LazyListenerAggregate implements ListenerAggregateInterface
{
use ListenerAggregateTrait;
// phpcs:disable SlevomatCodingStandard.Classes.UnusedPrivateElements.WriteOnlyProperty
/** @var ContainerInterface Container from which to pull lazy listeners. */
private $container;
/** @var array Additional environment/option variables to use when creating listener. */
private $env;
// phpcs:enable
/**
* Generated LazyEventListener instances.
*
* @var LazyEventListener[]
*/
private $lazyListeners = [];
/**
* Constructor.
*
* Accepts the composed $listeners, as well as the $container and $env in
* order to create a listener aggregate that defers listener creation until
* the listener is triggered.
*
* Listeners may be either LazyEventListener instances, or lazy event
* listener definitions that can be provided to a LazyEventListener
* constructor in order to create a new instance; in the latter case, the
* $container and $env will be passed at instantiation as well.
*
* @param array $listeners lazyEventListener instances or array definitions
* to pass to the LazyEventListener constructor
*
* @throws Exception\InvalidArgumentException for invalid listener items
*/
public function __construct(array $listeners, ContainerInterface $container, array $env = [])
{
$this->container = $container;
$this->env = $env;
// This would raise an exception for invalid structs
foreach ($listeners as $listener) {
if (\is_array($listener)) {
$listener = new LazyEventListener($listener, $container, $env);
}
if (!$listener instanceof LazyEventListener) {
throw new Exception\InvalidArgumentException(\sprintf('All listeners must be LazyEventListener instances or definitions; received %s', \is_object($listener) ? \get_class($listener) : \gettype($listener)));
}
$this->lazyListeners[] = $listener;
}
}
/**
* Attach the aggregate to the event manager.
*
* Loops through all composed lazy listeners, and attaches them to the
* event manager.
*
* @param int $priority
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
foreach ($this->lazyListeners as $lazyListener) {
$this->listeners[] = $events->attach($lazyListener->getEvent(), $lazyListener, $lazyListener->getPriority($priority));
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Interface for self-registering event listeners.
*
* Classes implementing this interface may be registered by name or instance
* with an EventManager, without an event name. The {@link attach()} method will
* then be called with the current EventManager instance, allowing the class to
* wire up one or more listeners.
*/
interface ListenerAggregateInterface
{
/**
* Attach one or more listeners.
*
* Implementors may add an optional $priority argument; the EventManager
* implementation will pass this to the aggregate.
*
* @param int $priority
*/
public function attach(EventManagerInterface $events, $priority = 1);
/**
* Detach all previously attached listeners.
*/
public function detach(EventManagerInterface $events);
}

View File

@@ -0,0 +1,21 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Provides logic to easily create aggregate listeners, without worrying about
* manually detaching events.
*/
trait ListenerAggregateTrait
{
/** @var callable[] */
protected $listeners = [];
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $callback) {
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Collection of signal handler return values.
*/
class ResponseCollection extends \SplStack
{
/** @var bool */
protected $stopped = \false;
/**
* Did the last response provided trigger a short circuit of the stack?
*
* @return bool
*/
public function stopped()
{
return $this->stopped;
}
/**
* Mark the collection as stopped (or its opposite).
*
* @param bool $flag
*/
public function setStopped($flag)
{
$this->stopped = (bool) $flag;
}
/**
* Convenient access to the first handler return value.
*
* @return mixed The first handler return value
*/
public function first()
{
return parent::bottom();
}
/**
* Convenient access to the last handler return value.
*
* If the collection is empty, returns null. Otherwise, returns value
* returned by last handler.
*
* @return mixed The last handler return value
*/
public function last()
{
if (0 === \count($this)) {
return;
}
return parent::top();
}
/**
* Check if any of the responses match the given value.
*
* @param mixed $value The value to look for among responses
*
* @return bool
*/
public function contains($value)
{
foreach ($this as $response) {
if ($response === $value) {
return \true;
}
}
return \false;
}
}

View File

@@ -0,0 +1,193 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
use function get_class;
/**
* Shared/contextual EventManager.
*
* Allows attaching to EMs composed by other classes without having an instance first.
* The assumption is that the SharedEventManager will be injected into EventManager
* instances, and then queried for additional listeners when triggering an event.
*/
class SharedEventManager implements SharedEventManagerInterface
{
/**
* Identifiers with event connections.
*
* @var array
*/
protected $identifiers = [];
/**
* Attach a listener to an event emitted by components with specific identifiers.
*
* As an example, the following connects to the "getAll" event of both an
* AbstractResource and EntityResource:
*
* <code>
* $sharedEventManager = new SharedEventManager();
* foreach (['My\Resource\AbstractResource', 'My\Resource\EntityResource'] as $identifier) {
* $sharedEventManager->attach(
* $identifier,
* 'getAll',
* function ($e) use ($cache) {
* if (!$id = $e->getParam('id', false)) {
* return;
* }
* if (!$data = $cache->load(get_class($resource) . '::getOne::' . $id )) {
* return;
* }
* return $data;
* }
* );
* }
* </code>
*
* @param string $identifier identifier for event emitting component
* @param string $event
* @param callable $listener listener that will handle the event
* @param int $priority Priority at which listener should execute
*
* @throws Exception\InvalidArgumentException for invalid identifier arguments
* @throws Exception\InvalidArgumentException for invalid event arguments
*/
public function attach($identifier, $event, callable $listener, $priority = 1)
{
if (!\is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(\sprintf('Invalid identifier provided; must be a string; received "%s"', \is_object($identifier) ? \get_class($identifier) : \gettype($identifier)));
}
if (!\is_string($event) || empty($event)) {
throw new Exception\InvalidArgumentException(\sprintf('Invalid event provided; must be a non-empty string; received "%s"', \is_object($event) ? \get_class($event) : \gettype($event)));
}
$this->identifiers[$identifier][$event][(int) $priority][] = $listener;
}
public function detach(callable $listener, $identifier = null, $eventName = null, $force = \false)
{
// No identifier or wildcard identifier: loop through all identifiers and detach
if (null === $identifier || '*' === $identifier && !$force) {
foreach (\array_keys($this->identifiers) as $identifier) {
$this->detach($listener, $identifier, $eventName, \true);
}
return;
}
if (!\is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(\sprintf('Invalid identifier provided; must be a string, received %s', \is_object($identifier) ? \get_class($identifier) : \gettype($identifier)));
}
// Do we have any listeners on the provided identifier?
if (!isset($this->identifiers[$identifier])) {
return;
}
if (null === $eventName || '*' === $eventName && !$force) {
foreach (\array_keys($this->identifiers[$identifier]) as $eventName) {
$this->detach($listener, $identifier, $eventName, \true);
}
return;
}
if (!\is_string($eventName) || empty($eventName)) {
throw new Exception\InvalidArgumentException(\sprintf('Invalid event name provided; must be a string, received %s', \is_object($eventName) ? \get_class($eventName) : \gettype($eventName)));
}
if (!isset($this->identifiers[$identifier][$eventName])) {
return;
}
foreach ($this->identifiers[$identifier][$eventName] as $priority => $listeners) {
foreach ($listeners as $index => $evaluatedListener) {
if ($evaluatedListener !== $listener) {
continue;
}
// Found the listener; remove it.
unset($this->identifiers[$identifier][$eventName][$priority][$index]);
// Is the priority queue empty?
if (empty($this->identifiers[$identifier][$eventName][$priority])) {
unset($this->identifiers[$identifier][$eventName][$priority]);
break;
}
}
// Is the event queue empty?
if (empty($this->identifiers[$identifier][$eventName])) {
unset($this->identifiers[$identifier][$eventName]);
break;
}
}
// Is the identifier queue now empty? Remove it.
if (empty($this->identifiers[$identifier])) {
unset($this->identifiers[$identifier]);
}
}
/**
* Retrieve all listeners for a given identifier and event.
*
* @param string[] $identifiers
* @param string $eventName
*
* @return array[]
*
* @throws Exception\InvalidArgumentException
*/
public function getListeners(array $identifiers, $eventName)
{
if ('*' === $eventName || !\is_string($eventName) || empty($eventName)) {
throw new Exception\InvalidArgumentException(\sprintf('Event name passed to %s must be a non-empty, non-wildcard string', __METHOD__));
}
$returnListeners = [];
foreach ($identifiers as $identifier) {
if ('*' === $identifier || !\is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(\sprintf('Identifier names passed to %s must be non-empty, non-wildcard strings', __METHOD__));
}
if (isset($this->identifiers[$identifier])) {
$listenersByIdentifier = $this->identifiers[$identifier];
if (isset($listenersByIdentifier[$eventName])) {
foreach ($listenersByIdentifier[$eventName] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
if (isset($listenersByIdentifier['*'])) {
foreach ($listenersByIdentifier['*'] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
}
}
if (isset($this->identifiers['*'])) {
$wildcardIdentifier = $this->identifiers['*'];
if (isset($wildcardIdentifier[$eventName])) {
foreach ($wildcardIdentifier[$eventName] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
if (isset($wildcardIdentifier['*'])) {
foreach ($wildcardIdentifier['*'] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
}
foreach ($returnListeners as $priority => $listOfListeners) {
$returnListeners[$priority] = \array_merge(...$listOfListeners);
}
return $returnListeners;
}
public function clearListeners($identifier, $eventName = null)
{
if (!isset($this->identifiers[$identifier])) {
return \false;
}
if (null === $eventName) {
unset($this->identifiers[$identifier]);
return;
}
if (!isset($this->identifiers[$identifier][$eventName])) {
return;
}
unset($this->identifiers[$identifier][$eventName]);
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace _JchOptimizeVendor\Laminas\EventManager;
/**
* Interface for shared event listener collections.
*/
interface SharedEventManagerInterface
{
/**
* Attach a listener to an event emitted by components with specific identifiers.
*
* @param string $identifier Identifier for event emitting component
* @param string $eventName
* @param callable $listener listener that will handle the event
* @param int $priority Priority at which listener should execute
*/
public function attach($identifier, $eventName, callable $listener, $priority = 1);
/**
* Detach a shared listener.
*
* Allows detaching a listener from one or more events to which it may be
* attached.
*
* @param callable $listener listener to detach
* @param null|string $identifier identifier from which to detach; null indicates
* all registered identifiers
* @param null|string $eventName event from which to detach; null indicates
* all registered events
*
* @throws Exception\InvalidArgumentException for invalid identifier arguments
* @throws Exception\InvalidArgumentException for invalid event arguments
*/
public function detach(callable $listener, $identifier = null, $eventName = null);
/**
* Retrieve all listeners for given identifiers.
*
* @param string $eventName
*
* @return array
*/
public function getListeners(array $identifiers, $eventName);
/**
* Clear all listeners for a given identifier, optionally for a specific event.
*
* @param string $identifier
* @param null|string $eventName
*/
public function clearListeners($identifier, $eventName = null);
}

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