Initial commit

This commit is contained in:
2020-10-07 10:37:15 +02:00
commit ce5f440392
28157 changed files with 4429172 additions and 0 deletions

View File

@@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitcf0e80547367544c26e9561d5957499d::getLoader();

View File

@@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath.'\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,260 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Doctrine\\Common\\Cache\\ApcCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php',
'Doctrine\\Common\\Cache\\ApcuCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php',
'Doctrine\\Common\\Cache\\ArrayCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php',
'Doctrine\\Common\\Cache\\Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php',
'Doctrine\\Common\\Cache\\CacheProvider' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php',
'Doctrine\\Common\\Cache\\ChainCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php',
'Doctrine\\Common\\Cache\\ClearableCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php',
'Doctrine\\Common\\Cache\\CouchbaseCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php',
'Doctrine\\Common\\Cache\\FileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php',
'Doctrine\\Common\\Cache\\FilesystemCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php',
'Doctrine\\Common\\Cache\\FlushableCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php',
'Doctrine\\Common\\Cache\\MemcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php',
'Doctrine\\Common\\Cache\\MemcachedCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php',
'Doctrine\\Common\\Cache\\MongoDBCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php',
'Doctrine\\Common\\Cache\\MultiGetCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php',
'Doctrine\\Common\\Cache\\MultiPutCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php',
'Doctrine\\Common\\Cache\\PhpFileCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php',
'Doctrine\\Common\\Cache\\PredisCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php',
'Doctrine\\Common\\Cache\\RedisCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php',
'Doctrine\\Common\\Cache\\RiakCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php',
'Doctrine\\Common\\Cache\\SQLite3Cache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php',
'Doctrine\\Common\\Cache\\Version' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/Version.php',
'Doctrine\\Common\\Cache\\VoidCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php',
'Doctrine\\Common\\Cache\\WinCacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php',
'Doctrine\\Common\\Cache\\XcacheCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php',
'Doctrine\\Common\\Cache\\ZendDataCache' => $vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php',
'GuzzleHttp\\BatchResults' => $vendorDir . '/guzzlehttp/guzzle/src/BatchResults.php',
'GuzzleHttp\\Client' => $vendorDir . '/guzzlehttp/guzzle/src/Client.php',
'GuzzleHttp\\ClientInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ClientInterface.php',
'GuzzleHttp\\Collection' => $vendorDir . '/guzzlehttp/guzzle/src/Collection.php',
'GuzzleHttp\\Cookie\\CookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
'GuzzleHttp\\Cookie\\CookieJarInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php',
'GuzzleHttp\\Cookie\\FileCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php',
'GuzzleHttp\\Cookie\\SessionCookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php',
'GuzzleHttp\\Cookie\\SetCookie' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php',
'GuzzleHttp\\Event\\AbstractEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/AbstractEvent.php',
'GuzzleHttp\\Event\\AbstractRequestEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php',
'GuzzleHttp\\Event\\AbstractRetryableEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php',
'GuzzleHttp\\Event\\AbstractTransferEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php',
'GuzzleHttp\\Event\\BeforeEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/BeforeEvent.php',
'GuzzleHttp\\Event\\CompleteEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/CompleteEvent.php',
'GuzzleHttp\\Event\\Emitter' => $vendorDir . '/guzzlehttp/guzzle/src/Event/Emitter.php',
'GuzzleHttp\\Event\\EmitterInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Event/EmitterInterface.php',
'GuzzleHttp\\Event\\EndEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/EndEvent.php',
'GuzzleHttp\\Event\\ErrorEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/ErrorEvent.php',
'GuzzleHttp\\Event\\EventInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Event/EventInterface.php',
'GuzzleHttp\\Event\\HasEmitterInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php',
'GuzzleHttp\\Event\\HasEmitterTrait' => $vendorDir . '/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php',
'GuzzleHttp\\Event\\ListenerAttacherTrait' => $vendorDir . '/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php',
'GuzzleHttp\\Event\\ProgressEvent' => $vendorDir . '/guzzlehttp/guzzle/src/Event/ProgressEvent.php',
'GuzzleHttp\\Event\\RequestEvents' => $vendorDir . '/guzzlehttp/guzzle/src/Event/RequestEvents.php',
'GuzzleHttp\\Event\\SubscriberInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Event/SubscriberInterface.php',
'GuzzleHttp\\Exception\\BadResponseException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php',
'GuzzleHttp\\Exception\\ClientException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ClientException.php',
'GuzzleHttp\\Exception\\ConnectException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ConnectException.php',
'GuzzleHttp\\Exception\\CouldNotRewindStreamException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php',
'GuzzleHttp\\Exception\\ParseException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ParseException.php',
'GuzzleHttp\\Exception\\RequestException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/RequestException.php',
'GuzzleHttp\\Exception\\ServerException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/ServerException.php',
'GuzzleHttp\\Exception\\StateException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/StateException.php',
'GuzzleHttp\\Exception\\TooManyRedirectsException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php',
'GuzzleHttp\\Exception\\TransferException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/TransferException.php',
'GuzzleHttp\\Exception\\XmlParseException' => $vendorDir . '/guzzlehttp/guzzle/src/Exception/XmlParseException.php',
'GuzzleHttp\\HasDataTrait' => $vendorDir . '/guzzlehttp/guzzle/src/HasDataTrait.php',
'GuzzleHttp\\Message\\AbstractMessage' => $vendorDir . '/guzzlehttp/guzzle/src/Message/AbstractMessage.php',
'GuzzleHttp\\Message\\AppliesHeadersInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php',
'GuzzleHttp\\Message\\FutureResponse' => $vendorDir . '/guzzlehttp/guzzle/src/Message/FutureResponse.php',
'GuzzleHttp\\Message\\MessageFactory' => $vendorDir . '/guzzlehttp/guzzle/src/Message/MessageFactory.php',
'GuzzleHttp\\Message\\MessageFactoryInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php',
'GuzzleHttp\\Message\\MessageInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Message/MessageInterface.php',
'GuzzleHttp\\Message\\MessageParser' => $vendorDir . '/guzzlehttp/guzzle/src/Message/MessageParser.php',
'GuzzleHttp\\Message\\Request' => $vendorDir . '/guzzlehttp/guzzle/src/Message/Request.php',
'GuzzleHttp\\Message\\RequestInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Message/RequestInterface.php',
'GuzzleHttp\\Message\\Response' => $vendorDir . '/guzzlehttp/guzzle/src/Message/Response.php',
'GuzzleHttp\\Message\\ResponseInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Message/ResponseInterface.php',
'GuzzleHttp\\Mimetypes' => $vendorDir . '/guzzlehttp/guzzle/src/Mimetypes.php',
'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php',
'GuzzleHttp\\Post\\MultipartBody' => $vendorDir . '/guzzlehttp/guzzle/src/Post/MultipartBody.php',
'GuzzleHttp\\Post\\PostBody' => $vendorDir . '/guzzlehttp/guzzle/src/Post/PostBody.php',
'GuzzleHttp\\Post\\PostBodyInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Post/PostBodyInterface.php',
'GuzzleHttp\\Post\\PostFile' => $vendorDir . '/guzzlehttp/guzzle/src/Post/PostFile.php',
'GuzzleHttp\\Post\\PostFileInterface' => $vendorDir . '/guzzlehttp/guzzle/src/Post/PostFileInterface.php',
'GuzzleHttp\\Query' => $vendorDir . '/guzzlehttp/guzzle/src/Query.php',
'GuzzleHttp\\QueryParser' => $vendorDir . '/guzzlehttp/guzzle/src/QueryParser.php',
'GuzzleHttp\\RequestFsm' => $vendorDir . '/guzzlehttp/guzzle/src/RequestFsm.php',
'GuzzleHttp\\RingBridge' => $vendorDir . '/guzzlehttp/guzzle/src/RingBridge.php',
'GuzzleHttp\\Ring\\Client\\ClientUtils' => $vendorDir . '/guzzlehttp/ringphp/src/Client/ClientUtils.php',
'GuzzleHttp\\Ring\\Client\\CurlFactory' => $vendorDir . '/guzzlehttp/ringphp/src/Client/CurlFactory.php',
'GuzzleHttp\\Ring\\Client\\CurlHandler' => $vendorDir . '/guzzlehttp/ringphp/src/Client/CurlHandler.php',
'GuzzleHttp\\Ring\\Client\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php',
'GuzzleHttp\\Ring\\Client\\Middleware' => $vendorDir . '/guzzlehttp/ringphp/src/Client/Middleware.php',
'GuzzleHttp\\Ring\\Client\\MockHandler' => $vendorDir . '/guzzlehttp/ringphp/src/Client/MockHandler.php',
'GuzzleHttp\\Ring\\Client\\StreamHandler' => $vendorDir . '/guzzlehttp/ringphp/src/Client/StreamHandler.php',
'GuzzleHttp\\Ring\\Core' => $vendorDir . '/guzzlehttp/ringphp/src/Core.php',
'GuzzleHttp\\Ring\\Exception\\CancelledException' => $vendorDir . '/guzzlehttp/ringphp/src/Exception/CancelledException.php',
'GuzzleHttp\\Ring\\Exception\\CancelledFutureAccessException' => $vendorDir . '/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php',
'GuzzleHttp\\Ring\\Exception\\ConnectException' => $vendorDir . '/guzzlehttp/ringphp/src/Exception/ConnectException.php',
'GuzzleHttp\\Ring\\Exception\\RingException' => $vendorDir . '/guzzlehttp/ringphp/src/Exception/RingException.php',
'GuzzleHttp\\Ring\\Future\\BaseFutureTrait' => $vendorDir . '/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php',
'GuzzleHttp\\Ring\\Future\\CompletedFutureArray' => $vendorDir . '/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php',
'GuzzleHttp\\Ring\\Future\\CompletedFutureValue' => $vendorDir . '/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php',
'GuzzleHttp\\Ring\\Future\\FutureArray' => $vendorDir . '/guzzlehttp/ringphp/src/Future/FutureArray.php',
'GuzzleHttp\\Ring\\Future\\FutureArrayInterface' => $vendorDir . '/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php',
'GuzzleHttp\\Ring\\Future\\FutureInterface' => $vendorDir . '/guzzlehttp/ringphp/src/Future/FutureInterface.php',
'GuzzleHttp\\Ring\\Future\\FutureValue' => $vendorDir . '/guzzlehttp/ringphp/src/Future/FutureValue.php',
'GuzzleHttp\\Ring\\Future\\MagicFutureTrait' => $vendorDir . '/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php',
'GuzzleHttp\\Stream\\AppendStream' => $vendorDir . '/guzzlehttp/streams/src/AppendStream.php',
'GuzzleHttp\\Stream\\AsyncReadStream' => $vendorDir . '/guzzlehttp/streams/src/AsyncReadStream.php',
'GuzzleHttp\\Stream\\BufferStream' => $vendorDir . '/guzzlehttp/streams/src/BufferStream.php',
'GuzzleHttp\\Stream\\CachingStream' => $vendorDir . '/guzzlehttp/streams/src/CachingStream.php',
'GuzzleHttp\\Stream\\DroppingStream' => $vendorDir . '/guzzlehttp/streams/src/DroppingStream.php',
'GuzzleHttp\\Stream\\Exception\\CannotAttachException' => $vendorDir . '/guzzlehttp/streams/src/Exception/CannotAttachException.php',
'GuzzleHttp\\Stream\\Exception\\SeekException' => $vendorDir . '/guzzlehttp/streams/src/Exception/SeekException.php',
'GuzzleHttp\\Stream\\FnStream' => $vendorDir . '/guzzlehttp/streams/src/FnStream.php',
'GuzzleHttp\\Stream\\GuzzleStreamWrapper' => $vendorDir . '/guzzlehttp/streams/src/GuzzleStreamWrapper.php',
'GuzzleHttp\\Stream\\InflateStream' => $vendorDir . '/guzzlehttp/streams/src/InflateStream.php',
'GuzzleHttp\\Stream\\LazyOpenStream' => $vendorDir . '/guzzlehttp/streams/src/LazyOpenStream.php',
'GuzzleHttp\\Stream\\LimitStream' => $vendorDir . '/guzzlehttp/streams/src/LimitStream.php',
'GuzzleHttp\\Stream\\MetadataStreamInterface' => $vendorDir . '/guzzlehttp/streams/src/MetadataStreamInterface.php',
'GuzzleHttp\\Stream\\NoSeekStream' => $vendorDir . '/guzzlehttp/streams/src/NoSeekStream.php',
'GuzzleHttp\\Stream\\NullStream' => $vendorDir . '/guzzlehttp/streams/src/NullStream.php',
'GuzzleHttp\\Stream\\PumpStream' => $vendorDir . '/guzzlehttp/streams/src/PumpStream.php',
'GuzzleHttp\\Stream\\Stream' => $vendorDir . '/guzzlehttp/streams/src/Stream.php',
'GuzzleHttp\\Stream\\StreamDecoratorTrait' => $vendorDir . '/guzzlehttp/streams/src/StreamDecoratorTrait.php',
'GuzzleHttp\\Stream\\StreamInterface' => $vendorDir . '/guzzlehttp/streams/src/StreamInterface.php',
'GuzzleHttp\\Stream\\Utils' => $vendorDir . '/guzzlehttp/streams/src/Utils.php',
'GuzzleHttp\\Subscriber\\Cache\\CacheStorage' => $vendorDir . '/guzzlehttp/cache-subscriber/src/CacheStorage.php',
'GuzzleHttp\\Subscriber\\Cache\\CacheStorageInterface' => $vendorDir . '/guzzlehttp/cache-subscriber/src/CacheStorageInterface.php',
'GuzzleHttp\\Subscriber\\Cache\\CacheSubscriber' => $vendorDir . '/guzzlehttp/cache-subscriber/src/CacheSubscriber.php',
'GuzzleHttp\\Subscriber\\Cache\\PurgeSubscriber' => $vendorDir . '/guzzlehttp/cache-subscriber/src/PurgeSubscriber.php',
'GuzzleHttp\\Subscriber\\Cache\\Utils' => $vendorDir . '/guzzlehttp/cache-subscriber/src/Utils.php',
'GuzzleHttp\\Subscriber\\Cache\\ValidationSubscriber' => $vendorDir . '/guzzlehttp/cache-subscriber/src/ValidationSubscriber.php',
'GuzzleHttp\\Subscriber\\Cookie' => $vendorDir . '/guzzlehttp/guzzle/src/Subscriber/Cookie.php',
'GuzzleHttp\\Subscriber\\History' => $vendorDir . '/guzzlehttp/guzzle/src/Subscriber/History.php',
'GuzzleHttp\\Subscriber\\HttpError' => $vendorDir . '/guzzlehttp/guzzle/src/Subscriber/HttpError.php',
'GuzzleHttp\\Subscriber\\Mock' => $vendorDir . '/guzzlehttp/guzzle/src/Subscriber/Mock.php',
'GuzzleHttp\\Subscriber\\Prepare' => $vendorDir . '/guzzlehttp/guzzle/src/Subscriber/Prepare.php',
'GuzzleHttp\\Subscriber\\Redirect' => $vendorDir . '/guzzlehttp/guzzle/src/Subscriber/Redirect.php',
'GuzzleHttp\\ToArrayInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ToArrayInterface.php',
'GuzzleHttp\\Transaction' => $vendorDir . '/guzzlehttp/guzzle/src/Transaction.php',
'GuzzleHttp\\UriTemplate' => $vendorDir . '/guzzlehttp/guzzle/src/UriTemplate.php',
'GuzzleHttp\\Url' => $vendorDir . '/guzzlehttp/guzzle/src/Url.php',
'GuzzleHttp\\Utils' => $vendorDir . '/guzzlehttp/guzzle/src/Utils.php',
'PrestaShop\\CircuitBreaker\\AdvancedCircuitBreaker' => $vendorDir . '/prestashop/circuit-breaker/src/AdvancedCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\AdvancedCircuitBreakerFactory' => $vendorDir . '/prestashop/circuit-breaker/src/AdvancedCircuitBreakerFactory.php',
'PrestaShop\\CircuitBreaker\\Client\\GuzzleClient' => $vendorDir . '/prestashop/circuit-breaker/src/Client/GuzzleClient.php',
'PrestaShop\\CircuitBreaker\\Contract\\CircuitBreakerInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/CircuitBreakerInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\ClientInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/ClientInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\FactoryInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/FactoryInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\FactorySettingsInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/FactorySettingsInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\PlaceInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/PlaceInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\StorageInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/StorageInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\SystemInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/SystemInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\TransactionInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/TransactionInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\TransitionDispatcherInterface' => $vendorDir . '/prestashop/circuit-breaker/src/Contract/TransitionDispatcherInterface.php',
'PrestaShop\\CircuitBreaker\\Event\\TransitionEvent' => $vendorDir . '/prestashop/circuit-breaker/src/Event/TransitionEvent.php',
'PrestaShop\\CircuitBreaker\\Exception\\CircuitBreakerException' => $vendorDir . '/prestashop/circuit-breaker/src/Exception/CircuitBreakerException.php',
'PrestaShop\\CircuitBreaker\\Exception\\InvalidPlaceException' => $vendorDir . '/prestashop/circuit-breaker/src/Exception/InvalidPlaceException.php',
'PrestaShop\\CircuitBreaker\\Exception\\InvalidTransactionException' => $vendorDir . '/prestashop/circuit-breaker/src/Exception/InvalidTransactionException.php',
'PrestaShop\\CircuitBreaker\\Exception\\TransactionNotFoundException' => $vendorDir . '/prestashop/circuit-breaker/src/Exception/TransactionNotFoundException.php',
'PrestaShop\\CircuitBreaker\\Exception\\UnavailableServiceException' => $vendorDir . '/prestashop/circuit-breaker/src/Exception/UnavailableServiceException.php',
'PrestaShop\\CircuitBreaker\\Exception\\UnsupportedMethodException' => $vendorDir . '/prestashop/circuit-breaker/src/Exception/UnsupportedMethodException.php',
'PrestaShop\\CircuitBreaker\\FactorySettings' => $vendorDir . '/prestashop/circuit-breaker/src/FactorySettings.php',
'PrestaShop\\CircuitBreaker\\PartialCircuitBreaker' => $vendorDir . '/prestashop/circuit-breaker/src/PartialCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\Place\\AbstractPlace' => $vendorDir . '/prestashop/circuit-breaker/src/Place/AbstractPlace.php',
'PrestaShop\\CircuitBreaker\\Place\\ClosedPlace' => $vendorDir . '/prestashop/circuit-breaker/src/Place/ClosedPlace.php',
'PrestaShop\\CircuitBreaker\\Place\\HalfOpenPlace' => $vendorDir . '/prestashop/circuit-breaker/src/Place/HalfOpenPlace.php',
'PrestaShop\\CircuitBreaker\\Place\\OpenPlace' => $vendorDir . '/prestashop/circuit-breaker/src/Place/OpenPlace.php',
'PrestaShop\\CircuitBreaker\\SimpleCircuitBreaker' => $vendorDir . '/prestashop/circuit-breaker/src/SimpleCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\SimpleCircuitBreakerFactory' => $vendorDir . '/prestashop/circuit-breaker/src/SimpleCircuitBreakerFactory.php',
'PrestaShop\\CircuitBreaker\\State' => $vendorDir . '/prestashop/circuit-breaker/src/State.php',
'PrestaShop\\CircuitBreaker\\Storage\\DoctrineCache' => $vendorDir . '/prestashop/circuit-breaker/src/Storage/DoctrineCache.php',
'PrestaShop\\CircuitBreaker\\Storage\\SimpleArray' => $vendorDir . '/prestashop/circuit-breaker/src/Storage/SimpleArray.php',
'PrestaShop\\CircuitBreaker\\Storage\\SymfonyCache' => $vendorDir . '/prestashop/circuit-breaker/src/Storage/SymfonyCache.php',
'PrestaShop\\CircuitBreaker\\SymfonyCircuitBreaker' => $vendorDir . '/prestashop/circuit-breaker/src/SymfonyCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\System\\MainSystem' => $vendorDir . '/prestashop/circuit-breaker/src/System/MainSystem.php',
'PrestaShop\\CircuitBreaker\\Transaction\\SimpleTransaction' => $vendorDir . '/prestashop/circuit-breaker/src/Transaction/SimpleTransaction.php',
'PrestaShop\\CircuitBreaker\\Transition' => $vendorDir . '/prestashop/circuit-breaker/src/Transition.php',
'PrestaShop\\CircuitBreaker\\Transition\\EventDispatcher' => $vendorDir . '/prestashop/circuit-breaker/src/Transition/EventDispatcher.php',
'PrestaShop\\CircuitBreaker\\Transition\\NullDispatcher' => $vendorDir . '/prestashop/circuit-breaker/src/Transition/NullDispatcher.php',
'PrestaShop\\CircuitBreaker\\Util\\Assert' => $vendorDir . '/prestashop/circuit-breaker/src/Util/Assert.php',
'PrestaShop\\CircuitBreaker\\Util\\ErrorFormatter' => $vendorDir . '/prestashop/circuit-breaker/src/Util/ErrorFormatter.php',
'PrestaShop\\Module\\ProductComment\\Addons\\CategoryFetcher' => $baseDir . '/src/Addons/CategoryFetcher.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductComment' => $baseDir . '/src/Entity/ProductComment.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentCriterion' => $baseDir . '/src/Entity/ProductCommentCriterion.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentGrade' => $baseDir . '/src/Entity/ProductCommentGrade.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentReport' => $baseDir . '/src/Entity/ProductCommentReport.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentUsefulness' => $baseDir . '/src/Entity/ProductCommentUsefulness.php',
'PrestaShop\\Module\\ProductComment\\Repository\\ProductCommentCriterionRepository' => $baseDir . '/src/Repository/ProductCommentCriterionRepository.php',
'PrestaShop\\Module\\ProductComment\\Repository\\ProductCommentRepository' => $baseDir . '/src/Repository/ProductCommentRepository.php',
'ProductComments' => $baseDir . '/productcomments.php',
'React\\Promise\\CancellablePromiseInterface' => $vendorDir . '/react/promise/src/CancellablePromiseInterface.php',
'React\\Promise\\CancellationQueue' => $vendorDir . '/react/promise/src/CancellationQueue.php',
'React\\Promise\\Deferred' => $vendorDir . '/react/promise/src/Deferred.php',
'React\\Promise\\Exception\\LengthException' => $vendorDir . '/react/promise/src/Exception/LengthException.php',
'React\\Promise\\ExtendedPromiseInterface' => $vendorDir . '/react/promise/src/ExtendedPromiseInterface.php',
'React\\Promise\\FulfilledPromise' => $vendorDir . '/react/promise/src/FulfilledPromise.php',
'React\\Promise\\LazyPromise' => $vendorDir . '/react/promise/src/LazyPromise.php',
'React\\Promise\\Promise' => $vendorDir . '/react/promise/src/Promise.php',
'React\\Promise\\PromiseInterface' => $vendorDir . '/react/promise/src/PromiseInterface.php',
'React\\Promise\\PromisorInterface' => $vendorDir . '/react/promise/src/PromisorInterface.php',
'React\\Promise\\RejectedPromise' => $vendorDir . '/react/promise/src/RejectedPromise.php',
'React\\Promise\\UnhandledRejectionException' => $vendorDir . '/react/promise/src/UnhandledRejectionException.php',
'Symfony\\Component\\CssSelector\\CssSelectorConverter' => $vendorDir . '/symfony/css-selector/CssSelectorConverter.php',
'Symfony\\Component\\CssSelector\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/css-selector/Exception/ExceptionInterface.php',
'Symfony\\Component\\CssSelector\\Exception\\ExpressionErrorException' => $vendorDir . '/symfony/css-selector/Exception/ExpressionErrorException.php',
'Symfony\\Component\\CssSelector\\Exception\\InternalErrorException' => $vendorDir . '/symfony/css-selector/Exception/InternalErrorException.php',
'Symfony\\Component\\CssSelector\\Exception\\ParseException' => $vendorDir . '/symfony/css-selector/Exception/ParseException.php',
'Symfony\\Component\\CssSelector\\Exception\\SyntaxErrorException' => $vendorDir . '/symfony/css-selector/Exception/SyntaxErrorException.php',
'Symfony\\Component\\CssSelector\\Node\\AbstractNode' => $vendorDir . '/symfony/css-selector/Node/AbstractNode.php',
'Symfony\\Component\\CssSelector\\Node\\AttributeNode' => $vendorDir . '/symfony/css-selector/Node/AttributeNode.php',
'Symfony\\Component\\CssSelector\\Node\\ClassNode' => $vendorDir . '/symfony/css-selector/Node/ClassNode.php',
'Symfony\\Component\\CssSelector\\Node\\CombinedSelectorNode' => $vendorDir . '/symfony/css-selector/Node/CombinedSelectorNode.php',
'Symfony\\Component\\CssSelector\\Node\\ElementNode' => $vendorDir . '/symfony/css-selector/Node/ElementNode.php',
'Symfony\\Component\\CssSelector\\Node\\FunctionNode' => $vendorDir . '/symfony/css-selector/Node/FunctionNode.php',
'Symfony\\Component\\CssSelector\\Node\\HashNode' => $vendorDir . '/symfony/css-selector/Node/HashNode.php',
'Symfony\\Component\\CssSelector\\Node\\NegationNode' => $vendorDir . '/symfony/css-selector/Node/NegationNode.php',
'Symfony\\Component\\CssSelector\\Node\\NodeInterface' => $vendorDir . '/symfony/css-selector/Node/NodeInterface.php',
'Symfony\\Component\\CssSelector\\Node\\PseudoNode' => $vendorDir . '/symfony/css-selector/Node/PseudoNode.php',
'Symfony\\Component\\CssSelector\\Node\\SelectorNode' => $vendorDir . '/symfony/css-selector/Node/SelectorNode.php',
'Symfony\\Component\\CssSelector\\Node\\Specificity' => $vendorDir . '/symfony/css-selector/Node/Specificity.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\CommentHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/CommentHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\HandlerInterface' => $vendorDir . '/symfony/css-selector/Parser/Handler/HandlerInterface.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\HashHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/HashHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\IdentifierHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/IdentifierHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\NumberHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/NumberHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\StringHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/StringHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\WhitespaceHandler' => $vendorDir . '/symfony/css-selector/Parser/Handler/WhitespaceHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Parser' => $vendorDir . '/symfony/css-selector/Parser/Parser.php',
'Symfony\\Component\\CssSelector\\Parser\\ParserInterface' => $vendorDir . '/symfony/css-selector/Parser/ParserInterface.php',
'Symfony\\Component\\CssSelector\\Parser\\Reader' => $vendorDir . '/symfony/css-selector/Parser/Reader.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ClassParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/ClassParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ElementParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/ElementParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\EmptyStringParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\HashParser' => $vendorDir . '/symfony/css-selector/Parser/Shortcut/HashParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Token' => $vendorDir . '/symfony/css-selector/Parser/Token.php',
'Symfony\\Component\\CssSelector\\Parser\\TokenStream' => $vendorDir . '/symfony/css-selector/Parser/TokenStream.php',
'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\Tokenizer' => $vendorDir . '/symfony/css-selector/Parser/Tokenizer/Tokenizer.php',
'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerEscaping' => $vendorDir . '/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php',
'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerPatterns' => $vendorDir . '/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\AbstractExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/AbstractExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\AttributeMatchingExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\CombinationExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/CombinationExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\ExtensionInterface' => $vendorDir . '/symfony/css-selector/XPath/Extension/ExtensionInterface.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\FunctionExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/FunctionExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\HtmlExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/HtmlExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\NodeExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/NodeExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\PseudoClassExtension' => $vendorDir . '/symfony/css-selector/XPath/Extension/PseudoClassExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Translator' => $vendorDir . '/symfony/css-selector/XPath/Translator.php',
'Symfony\\Component\\CssSelector\\XPath\\TranslatorInterface' => $vendorDir . '/symfony/css-selector/XPath/TranslatorInterface.php',
'Symfony\\Component\\CssSelector\\XPath\\XPathExpr' => $vendorDir . '/symfony/css-selector/XPath/XPathExpr.php',
);

View File

@@ -0,0 +1,10 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ad155f8f1cf0d418fe49e248db8c661b' => $vendorDir . '/react/promise/src/functions_include.php',
);

View File

@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -0,0 +1,18 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
'React\\Promise\\' => array($vendorDir . '/react/promise/src'),
'PrestaShop\\Module\\ProductComment\\' => array($baseDir . '/src'),
'PrestaShop\\CircuitBreaker\\' => array($vendorDir . '/prestashop/circuit-breaker/src'),
'GuzzleHttp\\Subscriber\\Cache\\' => array($vendorDir . '/guzzlehttp/cache-subscriber/src'),
'GuzzleHttp\\Stream\\' => array($vendorDir . '/guzzlehttp/streams/src'),
'GuzzleHttp\\Ring\\' => array($vendorDir . '/guzzlehttp/ringphp/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
);

View File

@@ -0,0 +1,61 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitcf0e80547367544c26e9561d5957499d
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitcf0e80547367544c26e9561d5957499d', 'loadClassLoader'), true, false);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInitcf0e80547367544c26e9561d5957499d', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitcf0e80547367544c26e9561d5957499d::getInitializer($loader));
} else {
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->setClassMapAuthoritative(true);
$loader->register(false);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitcf0e80547367544c26e9561d5957499d::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirecf0e80547367544c26e9561d5957499d($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequirecf0e80547367544c26e9561d5957499d($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}

View File

@@ -0,0 +1,342 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitcf0e80547367544c26e9561d5957499d
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
);
public static $prefixLengthsPsr4 = array (
'S' =>
array (
'Symfony\\Component\\CssSelector\\' => 30,
),
'R' =>
array (
'React\\Promise\\' => 14,
),
'P' =>
array (
'PrestaShop\\Module\\ProductComment\\' => 33,
'PrestaShop\\CircuitBreaker\\' => 26,
),
'G' =>
array (
'GuzzleHttp\\Subscriber\\Cache\\' => 28,
'GuzzleHttp\\Stream\\' => 18,
'GuzzleHttp\\Ring\\' => 16,
'GuzzleHttp\\' => 11,
),
'D' =>
array (
'Doctrine\\Common\\Cache\\' => 22,
),
);
public static $prefixDirsPsr4 = array (
'Symfony\\Component\\CssSelector\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/css-selector',
),
'React\\Promise\\' =>
array (
0 => __DIR__ . '/..' . '/react/promise/src',
),
'PrestaShop\\Module\\ProductComment\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
'PrestaShop\\CircuitBreaker\\' =>
array (
0 => __DIR__ . '/..' . '/prestashop/circuit-breaker/src',
),
'GuzzleHttp\\Subscriber\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src',
),
'GuzzleHttp\\Stream\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/streams/src',
),
'GuzzleHttp\\Ring\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/ringphp/src',
),
'GuzzleHttp\\' =>
array (
0 => __DIR__ . '/..' . '/guzzlehttp/guzzle/src',
),
'Doctrine\\Common\\Cache\\' =>
array (
0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache',
),
);
public static $classMap = array (
'Doctrine\\Common\\Cache\\ApcCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php',
'Doctrine\\Common\\Cache\\ApcuCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php',
'Doctrine\\Common\\Cache\\ArrayCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php',
'Doctrine\\Common\\Cache\\Cache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php',
'Doctrine\\Common\\Cache\\CacheProvider' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php',
'Doctrine\\Common\\Cache\\ChainCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php',
'Doctrine\\Common\\Cache\\ClearableCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php',
'Doctrine\\Common\\Cache\\CouchbaseCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php',
'Doctrine\\Common\\Cache\\FileCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php',
'Doctrine\\Common\\Cache\\FilesystemCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php',
'Doctrine\\Common\\Cache\\FlushableCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php',
'Doctrine\\Common\\Cache\\MemcacheCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php',
'Doctrine\\Common\\Cache\\MemcachedCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php',
'Doctrine\\Common\\Cache\\MongoDBCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php',
'Doctrine\\Common\\Cache\\MultiGetCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php',
'Doctrine\\Common\\Cache\\MultiPutCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php',
'Doctrine\\Common\\Cache\\PhpFileCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php',
'Doctrine\\Common\\Cache\\PredisCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php',
'Doctrine\\Common\\Cache\\RedisCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php',
'Doctrine\\Common\\Cache\\RiakCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php',
'Doctrine\\Common\\Cache\\SQLite3Cache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php',
'Doctrine\\Common\\Cache\\Version' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/Version.php',
'Doctrine\\Common\\Cache\\VoidCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php',
'Doctrine\\Common\\Cache\\WinCacheCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php',
'Doctrine\\Common\\Cache\\XcacheCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php',
'Doctrine\\Common\\Cache\\ZendDataCache' => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php',
'GuzzleHttp\\BatchResults' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/BatchResults.php',
'GuzzleHttp\\Client' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Client.php',
'GuzzleHttp\\ClientInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientInterface.php',
'GuzzleHttp\\Collection' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Collection.php',
'GuzzleHttp\\Cookie\\CookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
'GuzzleHttp\\Cookie\\CookieJarInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php',
'GuzzleHttp\\Cookie\\FileCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php',
'GuzzleHttp\\Cookie\\SessionCookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php',
'GuzzleHttp\\Cookie\\SetCookie' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/SetCookie.php',
'GuzzleHttp\\Event\\AbstractEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/AbstractEvent.php',
'GuzzleHttp\\Event\\AbstractRequestEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/AbstractRequestEvent.php',
'GuzzleHttp\\Event\\AbstractRetryableEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/AbstractRetryableEvent.php',
'GuzzleHttp\\Event\\AbstractTransferEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/AbstractTransferEvent.php',
'GuzzleHttp\\Event\\BeforeEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/BeforeEvent.php',
'GuzzleHttp\\Event\\CompleteEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/CompleteEvent.php',
'GuzzleHttp\\Event\\Emitter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/Emitter.php',
'GuzzleHttp\\Event\\EmitterInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/EmitterInterface.php',
'GuzzleHttp\\Event\\EndEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/EndEvent.php',
'GuzzleHttp\\Event\\ErrorEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/ErrorEvent.php',
'GuzzleHttp\\Event\\EventInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/EventInterface.php',
'GuzzleHttp\\Event\\HasEmitterInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/HasEmitterInterface.php',
'GuzzleHttp\\Event\\HasEmitterTrait' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/HasEmitterTrait.php',
'GuzzleHttp\\Event\\ListenerAttacherTrait' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/ListenerAttacherTrait.php',
'GuzzleHttp\\Event\\ProgressEvent' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/ProgressEvent.php',
'GuzzleHttp\\Event\\RequestEvents' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/RequestEvents.php',
'GuzzleHttp\\Event\\SubscriberInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Event/SubscriberInterface.php',
'GuzzleHttp\\Exception\\BadResponseException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/BadResponseException.php',
'GuzzleHttp\\Exception\\ClientException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ClientException.php',
'GuzzleHttp\\Exception\\ConnectException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ConnectException.php',
'GuzzleHttp\\Exception\\CouldNotRewindStreamException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/CouldNotRewindStreamException.php',
'GuzzleHttp\\Exception\\ParseException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ParseException.php',
'GuzzleHttp\\Exception\\RequestException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/RequestException.php',
'GuzzleHttp\\Exception\\ServerException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/ServerException.php',
'GuzzleHttp\\Exception\\StateException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/StateException.php',
'GuzzleHttp\\Exception\\TooManyRedirectsException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TooManyRedirectsException.php',
'GuzzleHttp\\Exception\\TransferException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/TransferException.php',
'GuzzleHttp\\Exception\\XmlParseException' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Exception/XmlParseException.php',
'GuzzleHttp\\HasDataTrait' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/HasDataTrait.php',
'GuzzleHttp\\Message\\AbstractMessage' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/AbstractMessage.php',
'GuzzleHttp\\Message\\AppliesHeadersInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/AppliesHeadersInterface.php',
'GuzzleHttp\\Message\\FutureResponse' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/FutureResponse.php',
'GuzzleHttp\\Message\\MessageFactory' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/MessageFactory.php',
'GuzzleHttp\\Message\\MessageFactoryInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/MessageFactoryInterface.php',
'GuzzleHttp\\Message\\MessageInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/MessageInterface.php',
'GuzzleHttp\\Message\\MessageParser' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/MessageParser.php',
'GuzzleHttp\\Message\\Request' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/Request.php',
'GuzzleHttp\\Message\\RequestInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/RequestInterface.php',
'GuzzleHttp\\Message\\Response' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/Response.php',
'GuzzleHttp\\Message\\ResponseInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Message/ResponseInterface.php',
'GuzzleHttp\\Mimetypes' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Mimetypes.php',
'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php',
'GuzzleHttp\\Post\\MultipartBody' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Post/MultipartBody.php',
'GuzzleHttp\\Post\\PostBody' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Post/PostBody.php',
'GuzzleHttp\\Post\\PostBodyInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Post/PostBodyInterface.php',
'GuzzleHttp\\Post\\PostFile' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Post/PostFile.php',
'GuzzleHttp\\Post\\PostFileInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Post/PostFileInterface.php',
'GuzzleHttp\\Query' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Query.php',
'GuzzleHttp\\QueryParser' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/QueryParser.php',
'GuzzleHttp\\RequestFsm' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RequestFsm.php',
'GuzzleHttp\\RingBridge' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RingBridge.php',
'GuzzleHttp\\Ring\\Client\\ClientUtils' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/ClientUtils.php',
'GuzzleHttp\\Ring\\Client\\CurlFactory' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/CurlFactory.php',
'GuzzleHttp\\Ring\\Client\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/CurlHandler.php',
'GuzzleHttp\\Ring\\Client\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/CurlMultiHandler.php',
'GuzzleHttp\\Ring\\Client\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/Middleware.php',
'GuzzleHttp\\Ring\\Client\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/MockHandler.php',
'GuzzleHttp\\Ring\\Client\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Client/StreamHandler.php',
'GuzzleHttp\\Ring\\Core' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Core.php',
'GuzzleHttp\\Ring\\Exception\\CancelledException' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Exception/CancelledException.php',
'GuzzleHttp\\Ring\\Exception\\CancelledFutureAccessException' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Exception/CancelledFutureAccessException.php',
'GuzzleHttp\\Ring\\Exception\\ConnectException' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Exception/ConnectException.php',
'GuzzleHttp\\Ring\\Exception\\RingException' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Exception/RingException.php',
'GuzzleHttp\\Ring\\Future\\BaseFutureTrait' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/BaseFutureTrait.php',
'GuzzleHttp\\Ring\\Future\\CompletedFutureArray' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/CompletedFutureArray.php',
'GuzzleHttp\\Ring\\Future\\CompletedFutureValue' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/CompletedFutureValue.php',
'GuzzleHttp\\Ring\\Future\\FutureArray' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/FutureArray.php',
'GuzzleHttp\\Ring\\Future\\FutureArrayInterface' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/FutureArrayInterface.php',
'GuzzleHttp\\Ring\\Future\\FutureInterface' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/FutureInterface.php',
'GuzzleHttp\\Ring\\Future\\FutureValue' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/FutureValue.php',
'GuzzleHttp\\Ring\\Future\\MagicFutureTrait' => __DIR__ . '/..' . '/guzzlehttp/ringphp/src/Future/MagicFutureTrait.php',
'GuzzleHttp\\Stream\\AppendStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/AppendStream.php',
'GuzzleHttp\\Stream\\AsyncReadStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/AsyncReadStream.php',
'GuzzleHttp\\Stream\\BufferStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/BufferStream.php',
'GuzzleHttp\\Stream\\CachingStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/CachingStream.php',
'GuzzleHttp\\Stream\\DroppingStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/DroppingStream.php',
'GuzzleHttp\\Stream\\Exception\\CannotAttachException' => __DIR__ . '/..' . '/guzzlehttp/streams/src/Exception/CannotAttachException.php',
'GuzzleHttp\\Stream\\Exception\\SeekException' => __DIR__ . '/..' . '/guzzlehttp/streams/src/Exception/SeekException.php',
'GuzzleHttp\\Stream\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/FnStream.php',
'GuzzleHttp\\Stream\\GuzzleStreamWrapper' => __DIR__ . '/..' . '/guzzlehttp/streams/src/GuzzleStreamWrapper.php',
'GuzzleHttp\\Stream\\InflateStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/InflateStream.php',
'GuzzleHttp\\Stream\\LazyOpenStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/LazyOpenStream.php',
'GuzzleHttp\\Stream\\LimitStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/LimitStream.php',
'GuzzleHttp\\Stream\\MetadataStreamInterface' => __DIR__ . '/..' . '/guzzlehttp/streams/src/MetadataStreamInterface.php',
'GuzzleHttp\\Stream\\NoSeekStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/NoSeekStream.php',
'GuzzleHttp\\Stream\\NullStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/NullStream.php',
'GuzzleHttp\\Stream\\PumpStream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/PumpStream.php',
'GuzzleHttp\\Stream\\Stream' => __DIR__ . '/..' . '/guzzlehttp/streams/src/Stream.php',
'GuzzleHttp\\Stream\\StreamDecoratorTrait' => __DIR__ . '/..' . '/guzzlehttp/streams/src/StreamDecoratorTrait.php',
'GuzzleHttp\\Stream\\StreamInterface' => __DIR__ . '/..' . '/guzzlehttp/streams/src/StreamInterface.php',
'GuzzleHttp\\Stream\\Utils' => __DIR__ . '/..' . '/guzzlehttp/streams/src/Utils.php',
'GuzzleHttp\\Subscriber\\Cache\\CacheStorage' => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src/CacheStorage.php',
'GuzzleHttp\\Subscriber\\Cache\\CacheStorageInterface' => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src/CacheStorageInterface.php',
'GuzzleHttp\\Subscriber\\Cache\\CacheSubscriber' => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src/CacheSubscriber.php',
'GuzzleHttp\\Subscriber\\Cache\\PurgeSubscriber' => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src/PurgeSubscriber.php',
'GuzzleHttp\\Subscriber\\Cache\\Utils' => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src/Utils.php',
'GuzzleHttp\\Subscriber\\Cache\\ValidationSubscriber' => __DIR__ . '/..' . '/guzzlehttp/cache-subscriber/src/ValidationSubscriber.php',
'GuzzleHttp\\Subscriber\\Cookie' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Subscriber/Cookie.php',
'GuzzleHttp\\Subscriber\\History' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Subscriber/History.php',
'GuzzleHttp\\Subscriber\\HttpError' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Subscriber/HttpError.php',
'GuzzleHttp\\Subscriber\\Mock' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Subscriber/Mock.php',
'GuzzleHttp\\Subscriber\\Prepare' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Subscriber/Prepare.php',
'GuzzleHttp\\Subscriber\\Redirect' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Subscriber/Redirect.php',
'GuzzleHttp\\ToArrayInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ToArrayInterface.php',
'GuzzleHttp\\Transaction' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Transaction.php',
'GuzzleHttp\\UriTemplate' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/UriTemplate.php',
'GuzzleHttp\\Url' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Url.php',
'GuzzleHttp\\Utils' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Utils.php',
'PrestaShop\\CircuitBreaker\\AdvancedCircuitBreaker' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/AdvancedCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\AdvancedCircuitBreakerFactory' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/AdvancedCircuitBreakerFactory.php',
'PrestaShop\\CircuitBreaker\\Client\\GuzzleClient' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Client/GuzzleClient.php',
'PrestaShop\\CircuitBreaker\\Contract\\CircuitBreakerInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/CircuitBreakerInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\ClientInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/ClientInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\FactoryInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/FactoryInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\FactorySettingsInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/FactorySettingsInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\PlaceInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/PlaceInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\StorageInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/StorageInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\SystemInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/SystemInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\TransactionInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/TransactionInterface.php',
'PrestaShop\\CircuitBreaker\\Contract\\TransitionDispatcherInterface' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Contract/TransitionDispatcherInterface.php',
'PrestaShop\\CircuitBreaker\\Event\\TransitionEvent' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Event/TransitionEvent.php',
'PrestaShop\\CircuitBreaker\\Exception\\CircuitBreakerException' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Exception/CircuitBreakerException.php',
'PrestaShop\\CircuitBreaker\\Exception\\InvalidPlaceException' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Exception/InvalidPlaceException.php',
'PrestaShop\\CircuitBreaker\\Exception\\InvalidTransactionException' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Exception/InvalidTransactionException.php',
'PrestaShop\\CircuitBreaker\\Exception\\TransactionNotFoundException' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Exception/TransactionNotFoundException.php',
'PrestaShop\\CircuitBreaker\\Exception\\UnavailableServiceException' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Exception/UnavailableServiceException.php',
'PrestaShop\\CircuitBreaker\\Exception\\UnsupportedMethodException' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Exception/UnsupportedMethodException.php',
'PrestaShop\\CircuitBreaker\\FactorySettings' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/FactorySettings.php',
'PrestaShop\\CircuitBreaker\\PartialCircuitBreaker' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/PartialCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\Place\\AbstractPlace' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Place/AbstractPlace.php',
'PrestaShop\\CircuitBreaker\\Place\\ClosedPlace' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Place/ClosedPlace.php',
'PrestaShop\\CircuitBreaker\\Place\\HalfOpenPlace' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Place/HalfOpenPlace.php',
'PrestaShop\\CircuitBreaker\\Place\\OpenPlace' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Place/OpenPlace.php',
'PrestaShop\\CircuitBreaker\\SimpleCircuitBreaker' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/SimpleCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\SimpleCircuitBreakerFactory' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/SimpleCircuitBreakerFactory.php',
'PrestaShop\\CircuitBreaker\\State' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/State.php',
'PrestaShop\\CircuitBreaker\\Storage\\DoctrineCache' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Storage/DoctrineCache.php',
'PrestaShop\\CircuitBreaker\\Storage\\SimpleArray' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Storage/SimpleArray.php',
'PrestaShop\\CircuitBreaker\\Storage\\SymfonyCache' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Storage/SymfonyCache.php',
'PrestaShop\\CircuitBreaker\\SymfonyCircuitBreaker' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/SymfonyCircuitBreaker.php',
'PrestaShop\\CircuitBreaker\\System\\MainSystem' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/System/MainSystem.php',
'PrestaShop\\CircuitBreaker\\Transaction\\SimpleTransaction' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Transaction/SimpleTransaction.php',
'PrestaShop\\CircuitBreaker\\Transition' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Transition.php',
'PrestaShop\\CircuitBreaker\\Transition\\EventDispatcher' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Transition/EventDispatcher.php',
'PrestaShop\\CircuitBreaker\\Transition\\NullDispatcher' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Transition/NullDispatcher.php',
'PrestaShop\\CircuitBreaker\\Util\\Assert' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Util/Assert.php',
'PrestaShop\\CircuitBreaker\\Util\\ErrorFormatter' => __DIR__ . '/..' . '/prestashop/circuit-breaker/src/Util/ErrorFormatter.php',
'PrestaShop\\Module\\ProductComment\\Addons\\CategoryFetcher' => __DIR__ . '/../..' . '/src/Addons/CategoryFetcher.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductComment' => __DIR__ . '/../..' . '/src/Entity/ProductComment.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentCriterion' => __DIR__ . '/../..' . '/src/Entity/ProductCommentCriterion.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentGrade' => __DIR__ . '/../..' . '/src/Entity/ProductCommentGrade.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentReport' => __DIR__ . '/../..' . '/src/Entity/ProductCommentReport.php',
'PrestaShop\\Module\\ProductComment\\Entity\\ProductCommentUsefulness' => __DIR__ . '/../..' . '/src/Entity/ProductCommentUsefulness.php',
'PrestaShop\\Module\\ProductComment\\Repository\\ProductCommentCriterionRepository' => __DIR__ . '/../..' . '/src/Repository/ProductCommentCriterionRepository.php',
'PrestaShop\\Module\\ProductComment\\Repository\\ProductCommentRepository' => __DIR__ . '/../..' . '/src/Repository/ProductCommentRepository.php',
'ProductComments' => __DIR__ . '/../..' . '/productcomments.php',
'React\\Promise\\CancellablePromiseInterface' => __DIR__ . '/..' . '/react/promise/src/CancellablePromiseInterface.php',
'React\\Promise\\CancellationQueue' => __DIR__ . '/..' . '/react/promise/src/CancellationQueue.php',
'React\\Promise\\Deferred' => __DIR__ . '/..' . '/react/promise/src/Deferred.php',
'React\\Promise\\Exception\\LengthException' => __DIR__ . '/..' . '/react/promise/src/Exception/LengthException.php',
'React\\Promise\\ExtendedPromiseInterface' => __DIR__ . '/..' . '/react/promise/src/ExtendedPromiseInterface.php',
'React\\Promise\\FulfilledPromise' => __DIR__ . '/..' . '/react/promise/src/FulfilledPromise.php',
'React\\Promise\\LazyPromise' => __DIR__ . '/..' . '/react/promise/src/LazyPromise.php',
'React\\Promise\\Promise' => __DIR__ . '/..' . '/react/promise/src/Promise.php',
'React\\Promise\\PromiseInterface' => __DIR__ . '/..' . '/react/promise/src/PromiseInterface.php',
'React\\Promise\\PromisorInterface' => __DIR__ . '/..' . '/react/promise/src/PromisorInterface.php',
'React\\Promise\\RejectedPromise' => __DIR__ . '/..' . '/react/promise/src/RejectedPromise.php',
'React\\Promise\\UnhandledRejectionException' => __DIR__ . '/..' . '/react/promise/src/UnhandledRejectionException.php',
'Symfony\\Component\\CssSelector\\CssSelectorConverter' => __DIR__ . '/..' . '/symfony/css-selector/CssSelectorConverter.php',
'Symfony\\Component\\CssSelector\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/css-selector/Exception/ExceptionInterface.php',
'Symfony\\Component\\CssSelector\\Exception\\ExpressionErrorException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/ExpressionErrorException.php',
'Symfony\\Component\\CssSelector\\Exception\\InternalErrorException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/InternalErrorException.php',
'Symfony\\Component\\CssSelector\\Exception\\ParseException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/ParseException.php',
'Symfony\\Component\\CssSelector\\Exception\\SyntaxErrorException' => __DIR__ . '/..' . '/symfony/css-selector/Exception/SyntaxErrorException.php',
'Symfony\\Component\\CssSelector\\Node\\AbstractNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/AbstractNode.php',
'Symfony\\Component\\CssSelector\\Node\\AttributeNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/AttributeNode.php',
'Symfony\\Component\\CssSelector\\Node\\ClassNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/ClassNode.php',
'Symfony\\Component\\CssSelector\\Node\\CombinedSelectorNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/CombinedSelectorNode.php',
'Symfony\\Component\\CssSelector\\Node\\ElementNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/ElementNode.php',
'Symfony\\Component\\CssSelector\\Node\\FunctionNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/FunctionNode.php',
'Symfony\\Component\\CssSelector\\Node\\HashNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/HashNode.php',
'Symfony\\Component\\CssSelector\\Node\\NegationNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/NegationNode.php',
'Symfony\\Component\\CssSelector\\Node\\NodeInterface' => __DIR__ . '/..' . '/symfony/css-selector/Node/NodeInterface.php',
'Symfony\\Component\\CssSelector\\Node\\PseudoNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/PseudoNode.php',
'Symfony\\Component\\CssSelector\\Node\\SelectorNode' => __DIR__ . '/..' . '/symfony/css-selector/Node/SelectorNode.php',
'Symfony\\Component\\CssSelector\\Node\\Specificity' => __DIR__ . '/..' . '/symfony/css-selector/Node/Specificity.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\CommentHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/CommentHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\HandlerInterface' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/HandlerInterface.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\HashHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/HashHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\IdentifierHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/IdentifierHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\NumberHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/NumberHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\StringHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/StringHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Handler\\WhitespaceHandler' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Handler/WhitespaceHandler.php',
'Symfony\\Component\\CssSelector\\Parser\\Parser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Parser.php',
'Symfony\\Component\\CssSelector\\Parser\\ParserInterface' => __DIR__ . '/..' . '/symfony/css-selector/Parser/ParserInterface.php',
'Symfony\\Component\\CssSelector\\Parser\\Reader' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Reader.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ClassParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/ClassParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\ElementParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/ElementParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\EmptyStringParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Shortcut\\HashParser' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Shortcut/HashParser.php',
'Symfony\\Component\\CssSelector\\Parser\\Token' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Token.php',
'Symfony\\Component\\CssSelector\\Parser\\TokenStream' => __DIR__ . '/..' . '/symfony/css-selector/Parser/TokenStream.php',
'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\Tokenizer' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Tokenizer/Tokenizer.php',
'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerEscaping' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php',
'Symfony\\Component\\CssSelector\\Parser\\Tokenizer\\TokenizerPatterns' => __DIR__ . '/..' . '/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\AbstractExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/AbstractExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\AttributeMatchingExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\CombinationExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/CombinationExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\ExtensionInterface' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/ExtensionInterface.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\FunctionExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/FunctionExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\HtmlExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/HtmlExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\NodeExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/NodeExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Extension\\PseudoClassExtension' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Extension/PseudoClassExtension.php',
'Symfony\\Component\\CssSelector\\XPath\\Translator' => __DIR__ . '/..' . '/symfony/css-selector/XPath/Translator.php',
'Symfony\\Component\\CssSelector\\XPath\\TranslatorInterface' => __DIR__ . '/..' . '/symfony/css-selector/XPath/TranslatorInterface.php',
'Symfony\\Component\\CssSelector\\XPath\\XPathExpr' => __DIR__ . '/..' . '/symfony/css-selector/XPath/XPathExpr.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitcf0e80547367544c26e9561d5957499d::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitcf0e80547367544c26e9561d5957499d::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitcf0e80547367544c26e9561d5957499d::$classMap;
}, null, ClassLoader::class);
}
}

View File

@@ -0,0 +1,451 @@
[
{
"name": "doctrine/cache",
"version": "v1.6.2",
"version_normalized": "1.6.2.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b",
"reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b",
"shasum": ""
},
"require": {
"php": "~5.5|~7.0"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
"phpunit/phpunit": "~4.8|~5.0",
"predis/predis": "~1.0",
"satooshi/php-coveralls": "~0.6"
},
"time": "2017-07-22T12:49:21+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com"
},
{
"name": "Jonathan Wage",
"email": "jonwage@gmail.com"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com"
}
],
"description": "Caching library offering an object-oriented API for many cache backends",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"cache",
"caching"
]
},
{
"name": "guzzlehttp/cache-subscriber",
"version": "0.2.0",
"version_normalized": "0.2.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/cache-subscriber.git",
"reference": "8c766ba399e4c46383e3eaa220201be62abd101e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/cache-subscriber/zipball/8c766ba399e4c46383e3eaa220201be62abd101e",
"reference": "8c766ba399e4c46383e3eaa220201be62abd101e",
"shasum": ""
},
"require": {
"doctrine/cache": "~1.3",
"guzzlehttp/guzzle": "~5.0",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"time": "2019-09-16T13:44:55+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "0.2-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Subscriber\\Cache\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle HTTP cache subscriber",
"homepage": "http://guzzlephp.org/",
"keywords": [
"Guzzle",
"cache"
]
},
{
"name": "guzzlehttp/guzzle",
"version": "5.3.4",
"version_normalized": "5.3.4.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "b87eda7a7162f95574032da17e9323c9899cb6b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/b87eda7a7162f95574032da17e9323c9899cb6b2",
"reference": "b87eda7a7162f95574032da17e9323c9899cb6b2",
"shasum": ""
},
"require": {
"guzzlehttp/ringphp": "^1.1",
"php": ">=5.4.0",
"react/promise": "^2.2"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.0"
},
"time": "2019-10-30T09:32:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
"homepage": "http://guzzlephp.org/",
"keywords": [
"client",
"curl",
"framework",
"http",
"http client",
"rest",
"web service"
]
},
{
"name": "guzzlehttp/ringphp",
"version": "1.1.1",
"version_normalized": "1.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/RingPHP.git",
"reference": "5e2a174052995663dd68e6b5ad838afd47dd615b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b",
"reference": "5e2a174052995663dd68e6b5ad838afd47dd615b",
"shasum": ""
},
"require": {
"guzzlehttp/streams": "~3.0",
"php": ">=5.4.0",
"react/promise": "~2.0"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"ext-curl": "Guzzle will use specific adapters if cURL is present"
},
"time": "2018-07-31T13:22:33+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Ring\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
"abandoned": true
},
{
"name": "guzzlehttp/streams",
"version": "3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/streams.git",
"reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
"reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"time": "2014-10-12T19:18:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"GuzzleHttp\\Stream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"description": "Provides a simple abstraction over streams of data",
"homepage": "http://guzzlephp.org/",
"keywords": [
"Guzzle",
"stream"
],
"abandoned": true
},
{
"name": "prestashop/circuit-breaker",
"version": "v3.0.0",
"version_normalized": "3.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/PrestaShop/circuit-breaker.git",
"reference": "8764540d470b533c9484534343688733bc363f77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PrestaShop/circuit-breaker/zipball/8764540d470b533c9484534343688733bc363f77",
"reference": "8764540d470b533c9484534343688733bc363f77",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^5",
"php": ">=5.6"
},
"require-dev": {
"doctrine/cache": "^1.6.0",
"friendsofphp/php-cs-fixer": "^2.12",
"phpunit/phpunit": "^5.7.0",
"squizlabs/php_codesniffer": "3.*",
"symfony/cache": "^3.4.0",
"symfony/event-dispatcher": "^3.4",
"vimeo/psalm": "^1.1"
},
"suggest": {
"doctrine/cache": "Allows use of Doctrine Cache adapters to store transactions",
"ext-apcu": "Allows use of APCu adapter (performant) to store transactions",
"guzzlehttp/cache-subscriber": "Allow use of Guzzle cache (use dev-master for most recent changes)",
"symfony/cache": "Allows use of Symfony Cache adapters to store transactions"
},
"time": "2019-06-13T10:50:14+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PrestaShop\\CircuitBreaker\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PrestaShop SA",
"email": "contact@prestashop.com"
},
{
"name": "PrestaShop Community",
"homepage": "http://contributors.prestashop.com/"
}
],
"description": "A circuit breaker implementation for PHP"
},
{
"name": "react/promise",
"version": "v2.7.1",
"version_normalized": "2.7.1.0",
"source": {
"type": "git",
"url": "https://github.com/reactphp/promise.git",
"reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d",
"reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d",
"shasum": ""
},
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8"
},
"time": "2019-01-07T21:25:54+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"React\\Promise\\": "src/"
},
"files": [
"src/functions_include.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jan Sorgalla",
"email": "jsorgalla@gmail.com"
}
],
"description": "A lightweight implementation of CommonJS Promises/A for PHP",
"keywords": [
"promise",
"promises"
]
},
{
"name": "symfony/css-selector",
"version": "v3.4.37",
"version_normalized": "3.4.37.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "e1b3e1a0621d6e48ee46092b4c7d8280f746b3c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/e1b3e1a0621d6e48ee46092b4c7d8280f746b3c5",
"reference": "e1b3e1a0621d6e48ee46092b4c7d8280f746b3c5",
"shasum": ""
},
"require": {
"php": "^5.5.9|>=7.0.8"
},
"time": "2020-01-01T11:03:25+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\CssSelector\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Jean-François Simon",
"email": "jeanfrancois.simon@sensiolabs.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com"
}
]

View File

@@ -0,0 +1,19 @@
Copyright (c) 2006-2015 Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,14 @@
# Doctrine Cache
Master: [![Build Status](https://secure.travis-ci.org/doctrine/cache.png?branch=master)](http://travis-ci.org/doctrine/cache) [![Coverage Status](https://coveralls.io/repos/doctrine/cache/badge.png?branch=master)](https://coveralls.io/r/doctrine/cache?branch=master)
[![Latest Stable Version](https://poser.pugx.org/doctrine/cache/v/stable.png)](https://packagist.org/packages/doctrine/cache) [![Total Downloads](https://poser.pugx.org/doctrine/cache/downloads.png)](https://packagist.org/packages/doctrine/cache)
Cache component extracted from the Doctrine Common project.
## Changelog
### v1.2
* Added support for MongoDB as Cache Provider
* Fix namespace version reset

View File

@@ -0,0 +1,16 @@
# Upgrade to 1.4
## Minor BC Break: `Doctrine\Common\Cache\FileCache#$extension` is now `private`.
If you need to override the value of `Doctrine\Common\Cache\FileCache#$extension`, then use the
second parameter of `Doctrine\Common\Cache\FileCache#__construct()` instead of overriding
the property in your own implementation.
## Minor BC Break: file based caches paths changed
`Doctrine\Common\Cache\FileCache`, `Doctrine\Common\Cache\PhpFileCache` and
`Doctrine\Common\Cache\FilesystemCache` are using a different cache paths structure.
If you rely on warmed up caches for deployments, consider that caches generated
with `doctrine/cache` `<1.4` are not compatible with the new directory structure,
and will be ignored.

View File

@@ -0,0 +1,3 @@
# Version class and file
project.version_class = Doctrine\\Common\\Cache\\Version
project.version_file = lib/Doctrine/Common/Cache/Version.php

View File

@@ -0,0 +1,110 @@
<?xml version="1.0"?>
<project name="DoctrineCommonCache" default="build" basedir=".">
<property file="build.properties" />
<target name="php">
<exec executable="which" outputproperty="php_executable">
<arg value="php" />
</exec>
</target>
<target name="prepare">
<mkdir dir="build" />
</target>
<target name="build" depends="check-git-checkout-clean,prepare,php,composer">
<exec executable="${php_executable}">
<arg value="build/composer.phar" />
<arg value="archive" />
<arg value="--dir=build" />
</exec>
</target>
<target name="composer" depends="php,composer-check,composer-download">
<exec executable="${php_executable}">
<arg value="build/composer.phar" />
<arg value="install" />
</exec>
</target>
<target name="composer-check" depends="prepare">
<available file="build/composer.phar" property="composer.present"/>
</target>
<target name="composer-download" unless="composer.present">
<exec executable="wget">
<arg value="-Obuild/composer.phar" />
<arg value="http://getcomposer.org/composer.phar" />
</exec>
</target>
<target name="make-release" depends="check-git-checkout-clean,prepare,php">
<replace file="${project.version_file}" token="-DEV" value="" failOnNoReplacements="true" />
<exec executable="git" failonerror="true" outputproperty="current_git_branch">
<arg value="rev-parse" />
<arg value="--abbrev-ref" />
<arg value="HEAD" />
</exec>
<exec executable="${php_executable}" outputproperty="doctrine.current_version" failonerror="true">
<arg value="-r" />
<arg value="require_once '${project.version_file}';echo ${project.version_class}::VERSION;" />
</exec>
<exec executable="${php_executable}" outputproperty="doctrine.next_version" failonerror="true">
<arg value="-r" />
<arg value="$parts = explode('.', str_ireplace(array('-DEV', '-ALPHA', '-BETA'), '', '${doctrine.current_version}'));
if (count($parts) != 3) {
throw new \InvalidArgumentException('Version is assumed in format x.y.z, ${doctrine.current_version} given');
}
if ('${current_git_branch}' === 'master') {
$parts[1]++;
} else {
$parts[2]++;
}
echo implode('.', $parts);
" />
</exec>
<git-commit file="${project.version_file}" message="Release ${doctrine.current_version}" />
<git-tag version="${doctrine.current_version}" />
<replace file="${project.version_file}" token="${doctrine.current_version}" value="${doctrine.next_version}-DEV" />
<git-commit file="${project.version_file}" message="Bump version to ${doctrine.next_version}" />
</target>
<target name="check-git-checkout-clean">
<exec executable="git" failonerror="true">
<arg value="diff-index" />
<arg value="--quiet" />
<arg value="HEAD" />
</exec>
</target>
<macrodef name="git-commit">
<attribute name="file" default="NOT SET"/>
<attribute name="message" default="NOT SET"/>
<sequential>
<exec executable="git">
<arg value="add" />
<arg value="@{file}" />
</exec>
<exec executable="git">
<arg value="commit" />
<arg value="-m" />
<arg value="@{message}" />
</exec>
</sequential>
</macrodef>
<macrodef name="git-tag">
<attribute name="version" default="NOT SET" />
<sequential>
<exec executable="git">
<arg value="tag" />
<arg value="-m" />
<arg value="v@{version}" />
<arg value="v@{version}" />
</exec>
</sequential>
</macrodef>
</project>

View File

@@ -0,0 +1,37 @@
{
"name": "doctrine/cache",
"type": "library",
"description": "Caching library offering an object-oriented API for many cache backends",
"keywords": ["cache", "caching"],
"homepage": "http://www.doctrine-project.org",
"license": "MIT",
"authors": [
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"},
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
],
"require": {
"php": "~5.5|~7.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8|~5.0",
"satooshi/php-coveralls": "~0.6",
"predis/predis": "~1.0"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"autoload": {
"psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" }
},
"autoload-dev": {
"psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" }
},
"extra": {
"branch-alias": {
"dev-master": "1.6.x-dev"
}
}
}

View File

@@ -0,0 +1,118 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* APC cache provider.
*
* @link www.doctrine-project.org
* @deprecated since version 1.6, use ApcuCache instead
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class ApcCache extends CacheProvider
{
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return apc_fetch($id);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return apc_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return apc_store($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
// apc_delete returns false if the id does not exist
return apc_delete($id) || ! apc_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return apc_clear_cache() && apc_clear_cache('user');
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
return apc_fetch($keys) ?: [];
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$result = apc_store($keysAndValues, null, $lifetime);
return empty($result);
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = apc_cache_info('', true);
$sma = apc_sma_info();
// @TODO - Temporary fix @see https://github.com/krakjoe/apcu/pull/42
if (PHP_VERSION_ID >= 50500) {
$info['num_hits'] = isset($info['num_hits']) ? $info['num_hits'] : $info['nhits'];
$info['num_misses'] = isset($info['num_misses']) ? $info['num_misses'] : $info['nmisses'];
$info['start_time'] = isset($info['start_time']) ? $info['start_time'] : $info['stime'];
}
return array(
Cache::STATS_HITS => $info['num_hits'],
Cache::STATS_MISSES => $info['num_misses'],
Cache::STATS_UPTIME => $info['start_time'],
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
);
}
}

View File

@@ -0,0 +1,106 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* APCu cache provider.
*
* @link www.doctrine-project.org
* @since 1.6
* @author Kévin Dunglas <dunglas@gmail.com>
*/
class ApcuCache extends CacheProvider
{
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return apcu_fetch($id);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return apcu_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return apcu_store($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
// apcu_delete returns false if the id does not exist
return apcu_delete($id) || ! apcu_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return apcu_clear_cache();
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
return apcu_fetch($keys) ?: [];
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$result = apcu_store($keysAndValues, null, $lifetime);
return empty($result);
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = apcu_cache_info(true);
$sma = apcu_sma_info();
return array(
Cache::STATS_HITS => $info['num_hits'],
Cache::STATS_MISSES => $info['num_misses'],
Cache::STATS_UPTIME => $info['start_time'],
Cache::STATS_MEMORY_USAGE => $info['mem_size'],
Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'],
);
}
}

View File

@@ -0,0 +1,142 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Array cache driver.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class ArrayCache extends CacheProvider
{
/**
* @var array[] $data each element being a tuple of [$data, $expiration], where the expiration is int|bool
*/
private $data = [];
/**
* @var int
*/
private $hitsCount = 0;
/**
* @var int
*/
private $missesCount = 0;
/**
* @var int
*/
private $upTime;
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->upTime = time();
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
if (! $this->doContains($id)) {
$this->missesCount += 1;
return false;
}
$this->hitsCount += 1;
return $this->data[$id][0];
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
if (! isset($this->data[$id])) {
return false;
}
$expiration = $this->data[$id][1];
if ($expiration && $expiration < time()) {
$this->doDelete($id);
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
$this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false];
return true;
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
unset($this->data[$id]);
return true;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
$this->data = [];
return true;
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
return [
Cache::STATS_HITS => $this->hitsCount,
Cache::STATS_MISSES => $this->missesCount,
Cache::STATS_UPTIME => $this->upTime,
Cache::STATS_MEMORY_USAGE => null,
Cache::STATS_MEMORY_AVAILABLE => null,
];
}
}

View File

@@ -0,0 +1,116 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Interface for cache drivers.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Kévin Dunglas <dunglas@gmail.com>
*/
interface Cache
{
const STATS_HITS = 'hits';
const STATS_MISSES = 'misses';
const STATS_UPTIME = 'uptime';
const STATS_MEMORY_USAGE = 'memory_usage';
const STATS_MEMORY_AVAILABLE = 'memory_available';
/**
* Only for backward compatibility (may be removed in next major release)
*
* @deprecated
*/
const STATS_MEMORY_AVAILIABLE = 'memory_available';
/**
* Fetches an entry from the cache.
*
* @param string $id The id of the cache entry to fetch.
*
* @return mixed The cached data or FALSE, if no cache entry exists for the given id.
*/
public function fetch($id);
/**
* Tests if an entry exists in the cache.
*
* @param string $id The cache id of the entry to check for.
*
* @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
public function contains($id);
/**
* Puts data into the cache.
*
* If a cache entry with the given id already exists, its data will be replaced.
*
* @param string $id The cache id.
* @param mixed $data The cache entry/data.
* @param int $lifeTime The lifetime in number of seconds for this cache entry.
* If zero (the default), the entry never expires (although it may be deleted from the cache
* to make place for other entries).
*
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
public function save($id, $data, $lifeTime = 0);
/**
* Deletes a cache entry.
*
* @param string $id The cache id.
*
* @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
* Deleting a non-existing entry is considered successful.
*/
public function delete($id);
/**
* Retrieves cached information from the data store.
*
* The server's statistics array has the following values:
*
* - <b>hits</b>
* Number of keys that have been requested and found present.
*
* - <b>misses</b>
* Number of items that have been requested and not found.
*
* - <b>uptime</b>
* Time that the server is running.
*
* - <b>memory_usage</b>
* Memory used by this server to store items.
*
* - <b>memory_available</b>
* Memory allowed to use for storage.
*
* @since 2.2
*
* @return array|null An associative array with server's statistics if available, NULL otherwise.
*/
public function getStats();
}

View File

@@ -0,0 +1,312 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Base class for cache provider implementations.
*
* @since 2.2
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache, MultiPutCache
{
const DOCTRINE_NAMESPACE_CACHEKEY = 'DoctrineNamespaceCacheKey[%s]';
/**
* The namespace to prefix all cache ids with.
*
* @var string
*/
private $namespace = '';
/**
* The namespace version.
*
* @var integer|null
*/
private $namespaceVersion;
/**
* Sets the namespace to prefix all cache ids with.
*
* @param string $namespace
*
* @return void
*/
public function setNamespace($namespace)
{
$this->namespace = (string) $namespace;
$this->namespaceVersion = null;
}
/**
* Retrieves the namespace that prefixes all cache ids.
*
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* {@inheritdoc}
*/
public function fetch($id)
{
return $this->doFetch($this->getNamespacedId($id));
}
/**
* {@inheritdoc}
*/
public function fetchMultiple(array $keys)
{
if (empty($keys)) {
return array();
}
// note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys
$namespacedKeys = array_combine($keys, array_map(array($this, 'getNamespacedId'), $keys));
$items = $this->doFetchMultiple($namespacedKeys);
$foundItems = array();
// no internal array function supports this sort of mapping: needs to be iterative
// this filters and combines keys in one pass
foreach ($namespacedKeys as $requestedKey => $namespacedKey) {
if (isset($items[$namespacedKey]) || array_key_exists($namespacedKey, $items)) {
$foundItems[$requestedKey] = $items[$namespacedKey];
}
}
return $foundItems;
}
/**
* {@inheritdoc}
*/
public function saveMultiple(array $keysAndValues, $lifetime = 0)
{
$namespacedKeysAndValues = array();
foreach ($keysAndValues as $key => $value) {
$namespacedKeysAndValues[$this->getNamespacedId($key)] = $value;
}
return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime);
}
/**
* {@inheritdoc}
*/
public function contains($id)
{
return $this->doContains($this->getNamespacedId($id));
}
/**
* {@inheritdoc}
*/
public function save($id, $data, $lifeTime = 0)
{
return $this->doSave($this->getNamespacedId($id), $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
public function delete($id)
{
return $this->doDelete($this->getNamespacedId($id));
}
/**
* {@inheritdoc}
*/
public function getStats()
{
return $this->doGetStats();
}
/**
* {@inheritDoc}
*/
public function flushAll()
{
return $this->doFlush();
}
/**
* {@inheritDoc}
*/
public function deleteAll()
{
$namespaceCacheKey = $this->getNamespaceCacheKey();
$namespaceVersion = $this->getNamespaceVersion() + 1;
if ($this->doSave($namespaceCacheKey, $namespaceVersion)) {
$this->namespaceVersion = $namespaceVersion;
return true;
}
return false;
}
/**
* Prefixes the passed id with the configured namespace value.
*
* @param string $id The id to namespace.
*
* @return string The namespaced id.
*/
private function getNamespacedId($id)
{
$namespaceVersion = $this->getNamespaceVersion();
return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion);
}
/**
* Returns the namespace cache key.
*
* @return string
*/
private function getNamespaceCacheKey()
{
return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace);
}
/**
* Returns the namespace version.
*
* @return integer
*/
private function getNamespaceVersion()
{
if (null !== $this->namespaceVersion) {
return $this->namespaceVersion;
}
$namespaceCacheKey = $this->getNamespaceCacheKey();
$this->namespaceVersion = $this->doFetch($namespaceCacheKey) ?: 1;
return $this->namespaceVersion;
}
/**
* Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it.
*
* @param array $keys Array of keys to retrieve from cache
* @return array Array of values retrieved for the given keys.
*/
protected function doFetchMultiple(array $keys)
{
$returnValues = array();
foreach ($keys as $key) {
if (false !== ($item = $this->doFetch($key)) || $this->doContains($key)) {
$returnValues[$key] = $item;
}
}
return $returnValues;
}
/**
* Fetches an entry from the cache.
*
* @param string $id The id of the cache entry to fetch.
*
* @return mixed|false The cached data or FALSE, if no cache entry exists for the given id.
*/
abstract protected function doFetch($id);
/**
* Tests if an entry exists in the cache.
*
* @param string $id The cache id of the entry to check for.
*
* @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
abstract protected function doContains($id);
/**
* Default implementation of doSaveMultiple. Each driver that supports multi-put should override it.
*
* @param array $keysAndValues Array of keys and values to save in cache
* @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these
* cache entries (0 => infinite lifeTime).
*
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$success = true;
foreach ($keysAndValues as $key => $value) {
if (!$this->doSave($key, $value, $lifetime)) {
$success = false;
}
}
return $success;
}
/**
* Puts data into the cache.
*
* @param string $id The cache id.
* @param string $data The cache entry/data.
* @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this
* cache entry (0 => infinite lifeTime).
*
* @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
abstract protected function doSave($id, $data, $lifeTime = 0);
/**
* Deletes a cache entry.
*
* @param string $id The cache id.
*
* @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise.
*/
abstract protected function doDelete($id);
/**
* Flushes all cache entries.
*
* @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
*/
abstract protected function doFlush();
/**
* Retrieves cached information from the data store.
*
* @since 2.2
*
* @return array|null An associative array with server's statistics if available, NULL otherwise.
*/
abstract protected function doGetStats();
}

View File

@@ -0,0 +1,147 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Cache provider that allows to easily chain multiple cache providers
*
* @author Michaël Gallego <mic.gallego@gmail.com>
*/
class ChainCache extends CacheProvider
{
/**
* @var CacheProvider[]
*/
private $cacheProviders = array();
/**
* Constructor
*
* @param CacheProvider[] $cacheProviders
*/
public function __construct($cacheProviders = array())
{
$this->cacheProviders = $cacheProviders;
}
/**
* {@inheritDoc}
*/
public function setNamespace($namespace)
{
parent::setNamespace($namespace);
foreach ($this->cacheProviders as $cacheProvider) {
$cacheProvider->setNamespace($namespace);
}
}
/**
* {@inheritDoc}
*/
protected function doFetch($id)
{
foreach ($this->cacheProviders as $key => $cacheProvider) {
if ($cacheProvider->doContains($id)) {
$value = $cacheProvider->doFetch($id);
// We populate all the previous cache layers (that are assumed to be faster)
for ($subKey = $key - 1 ; $subKey >= 0 ; $subKey--) {
$this->cacheProviders[$subKey]->doSave($id, $value);
}
return $value;
}
}
return false;
}
/**
* {@inheritDoc}
*/
protected function doContains($id)
{
foreach ($this->cacheProviders as $cacheProvider) {
if ($cacheProvider->doContains($id)) {
return true;
}
}
return false;
}
/**
* {@inheritDoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
$stored = true;
foreach ($this->cacheProviders as $cacheProvider) {
$stored = $cacheProvider->doSave($id, $data, $lifeTime) && $stored;
}
return $stored;
}
/**
* {@inheritDoc}
*/
protected function doDelete($id)
{
$deleted = true;
foreach ($this->cacheProviders as $cacheProvider) {
$deleted = $cacheProvider->doDelete($id) && $deleted;
}
return $deleted;
}
/**
* {@inheritDoc}
*/
protected function doFlush()
{
$flushed = true;
foreach ($this->cacheProviders as $cacheProvider) {
$flushed = $cacheProvider->doFlush() && $flushed;
}
return $flushed;
}
/**
* {@inheritDoc}
*/
protected function doGetStats()
{
// We return all the stats from all adapters
$stats = array();
foreach ($this->cacheProviders as $cacheProvider) {
$stats[] = $cacheProvider->doGetStats();
}
return $stats;
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Interface for cache that can be flushed.
*
* Intended to be used for partial clearing of a cache namespace. For a more
* global "flushing", see {@see FlushableCache}.
*
* @link www.doctrine-project.org
* @since 1.4
* @author Adirelle <adirelle@gmail.com>
*/
interface ClearableCache
{
/**
* Deletes all cache entries in the current cache namespace.
*
* @return bool TRUE if the cache entries were successfully deleted, FALSE otherwise.
*/
public function deleteAll();
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use \Couchbase;
/**
* Couchbase cache provider.
*
* @link www.doctrine-project.org
* @since 2.4
* @author Michael Nitschinger <michael@nitschinger.at>
*/
class CouchbaseCache extends CacheProvider
{
/**
* @var Couchbase|null
*/
private $couchbase;
/**
* Sets the Couchbase instance to use.
*
* @param Couchbase $couchbase
*
* @return void
*/
public function setCouchbase(Couchbase $couchbase)
{
$this->couchbase = $couchbase;
}
/**
* Gets the Couchbase instance used by the cache.
*
* @return Couchbase|null
*/
public function getCouchbase()
{
return $this->couchbase;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return $this->couchbase->get($id) ?: false;
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return (null !== $this->couchbase->get($id));
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
if ($lifeTime > 30 * 24 * 3600) {
$lifeTime = time() + $lifeTime;
}
return $this->couchbase->set($id, $data, (int) $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return $this->couchbase->delete($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return $this->couchbase->flush();
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$stats = $this->couchbase->getStats();
$servers = $this->couchbase->getServers();
$server = explode(":", $servers[0]);
$key = $server[0] . ":" . "11210";
$stats = $stats[$key];
return array(
Cache::STATS_HITS => $stats['get_hits'],
Cache::STATS_MISSES => $stats['get_misses'],
Cache::STATS_UPTIME => $stats['uptime'],
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
);
}
}

View File

@@ -0,0 +1,286 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Base file cache driver.
*
* @since 2.3
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/
abstract class FileCache extends CacheProvider
{
/**
* The cache directory.
*
* @var string
*/
protected $directory;
/**
* The cache file extension.
*
* @var string
*/
private $extension;
/**
* @var int
*/
private $umask;
/**
* @var int
*/
private $directoryStringLength;
/**
* @var int
*/
private $extensionStringLength;
/**
* @var bool
*/
private $isRunningOnWindows;
/**
* Constructor.
*
* @param string $directory The cache directory.
* @param string $extension The cache file extension.
*
* @throws \InvalidArgumentException
*/
public function __construct($directory, $extension = '', $umask = 0002)
{
// YES, this needs to be *before* createPathIfNeeded()
if ( ! is_int($umask)) {
throw new \InvalidArgumentException(sprintf(
'The umask parameter is required to be integer, was: %s',
gettype($umask)
));
}
$this->umask = $umask;
if ( ! $this->createPathIfNeeded($directory)) {
throw new \InvalidArgumentException(sprintf(
'The directory "%s" does not exist and could not be created.',
$directory
));
}
if ( ! is_writable($directory)) {
throw new \InvalidArgumentException(sprintf(
'The directory "%s" is not writable.',
$directory
));
}
// YES, this needs to be *after* createPathIfNeeded()
$this->directory = realpath($directory);
$this->extension = (string) $extension;
$this->directoryStringLength = strlen($this->directory);
$this->extensionStringLength = strlen($this->extension);
$this->isRunningOnWindows = defined('PHP_WINDOWS_VERSION_BUILD');
}
/**
* Gets the cache directory.
*
* @return string
*/
public function getDirectory()
{
return $this->directory;
}
/**
* Gets the cache file extension.
*
* @return string
*/
public function getExtension()
{
return $this->extension;
}
/**
* @param string $id
*
* @return string
*/
protected function getFilename($id)
{
$hash = hash('sha256', $id);
// This ensures that the filename is unique and that there are no invalid chars in it.
if (
'' === $id
|| ((strlen($id) * 2 + $this->extensionStringLength) > 255)
|| ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258)
) {
// Most filesystems have a limit of 255 chars for each path component. On Windows the the whole path is limited
// to 260 chars (including terminating null char). Using long UNC ("\\?\" prefix) does not work with the PHP API.
// And there is a bug in PHP (https://bugs.php.net/bug.php?id=70943) with path lengths of 259.
// So if the id in hex representation would surpass the limit, we use the hash instead. The prefix prevents
// collisions between the hash and bin2hex.
$filename = '_' . $hash;
} else {
$filename = bin2hex($id);
}
return $this->directory
. DIRECTORY_SEPARATOR
. substr($hash, 0, 2)
. DIRECTORY_SEPARATOR
. $filename
. $this->extension;
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
$filename = $this->getFilename($id);
return @unlink($filename) || ! file_exists($filename);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
foreach ($this->getIterator() as $name => $file) {
if ($file->isDir()) {
// Remove the intermediate directories which have been created to balance the tree. It only takes effect
// if the directory is empty. If several caches share the same directory but with different file extensions,
// the other ones are not removed.
@rmdir($name);
} elseif ($this->isFilenameEndingWithExtension($name)) {
// If an extension is set, only remove files which end with the given extension.
// If no extension is set, we have no other choice than removing everything.
@unlink($name);
}
}
return true;
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$usage = 0;
foreach ($this->getIterator() as $name => $file) {
if (! $file->isDir() && $this->isFilenameEndingWithExtension($name)) {
$usage += $file->getSize();
}
}
$free = disk_free_space($this->directory);
return array(
Cache::STATS_HITS => null,
Cache::STATS_MISSES => null,
Cache::STATS_UPTIME => null,
Cache::STATS_MEMORY_USAGE => $usage,
Cache::STATS_MEMORY_AVAILABLE => $free,
);
}
/**
* Create path if needed.
*
* @param string $path
* @return bool TRUE on success or if path already exists, FALSE if path cannot be created.
*/
private function createPathIfNeeded($path)
{
if ( ! is_dir($path)) {
if (false === @mkdir($path, 0777 & (~$this->umask), true) && !is_dir($path)) {
return false;
}
}
return true;
}
/**
* Writes a string content to file in an atomic way.
*
* @param string $filename Path to the file where to write the data.
* @param string $content The content to write
*
* @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error.
*/
protected function writeFile($filename, $content)
{
$filepath = pathinfo($filename, PATHINFO_DIRNAME);
if ( ! $this->createPathIfNeeded($filepath)) {
return false;
}
if ( ! is_writable($filepath)) {
return false;
}
$tmpFile = tempnam($filepath, 'swap');
@chmod($tmpFile, 0666 & (~$this->umask));
if (file_put_contents($tmpFile, $content) !== false) {
if (@rename($tmpFile, $filename)) {
return true;
}
@unlink($tmpFile);
}
return false;
}
/**
* @return \Iterator
*/
private function getIterator()
{
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS),
\RecursiveIteratorIterator::CHILD_FIRST
);
}
/**
* @param string $name The filename
*
* @return bool
*/
private function isFilenameEndingWithExtension($name)
{
return '' === $this->extension
|| strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength);
}
}

View File

@@ -0,0 +1,111 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Filesystem cache driver.
*
* @since 2.3
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class FilesystemCache extends FileCache
{
const EXTENSION = '.doctrinecache.data';
/**
* {@inheritdoc}
*/
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
{
parent::__construct($directory, $extension, $umask);
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
$data = '';
$lifetime = -1;
$filename = $this->getFilename($id);
if ( ! is_file($filename)) {
return false;
}
$resource = fopen($filename, "r");
if (false !== ($line = fgets($resource))) {
$lifetime = (int) $line;
}
if ($lifetime !== 0 && $lifetime < time()) {
fclose($resource);
return false;
}
while (false !== ($line = fgets($resource))) {
$data .= $line;
}
fclose($resource);
return unserialize($data);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
$lifetime = -1;
$filename = $this->getFilename($id);
if ( ! is_file($filename)) {
return false;
}
$resource = fopen($filename, "r");
if (false !== ($line = fgets($resource))) {
$lifetime = (int) $line;
}
fclose($resource);
return $lifetime === 0 || $lifetime > time();
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
if ($lifeTime > 0) {
$lifeTime = time() + $lifeTime;
}
$data = serialize($data);
$filename = $this->getFilename($id);
return $this->writeFile($filename, $lifeTime . PHP_EOL . $data);
}
}

View File

@@ -0,0 +1,37 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Interface for cache that can be flushed.
*
* @link www.doctrine-project.org
* @since 1.4
* @author Adirelle <adirelle@gmail.com>
*/
interface FlushableCache
{
/**
* Flushes all cache entries, globally.
*
* @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise.
*/
public function flushAll();
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use \Memcache;
/**
* Memcache cache provider.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class MemcacheCache extends CacheProvider
{
/**
* @var Memcache|null
*/
private $memcache;
/**
* Sets the memcache instance to use.
*
* @param Memcache $memcache
*
* @return void
*/
public function setMemcache(Memcache $memcache)
{
$this->memcache = $memcache;
}
/**
* Gets the memcache instance used by the cache.
*
* @return Memcache|null
*/
public function getMemcache()
{
return $this->memcache;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return $this->memcache->get($id);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
$flags = null;
$this->memcache->get($id, $flags);
//if memcache has changed the value of "flags", it means the value exists
return ($flags !== null);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
if ($lifeTime > 30 * 24 * 3600) {
$lifeTime = time() + $lifeTime;
}
return $this->memcache->set($id, $data, 0, (int) $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
// Memcache::delete() returns false if entry does not exist
return $this->memcache->delete($id) || ! $this->doContains($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return $this->memcache->flush();
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$stats = $this->memcache->getStats();
return array(
Cache::STATS_HITS => $stats['get_hits'],
Cache::STATS_MISSES => $stats['get_misses'],
Cache::STATS_UPTIME => $stats['uptime'],
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
);
}
}

View File

@@ -0,0 +1,147 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use \Memcached;
/**
* Memcached cache provider.
*
* @link www.doctrine-project.org
* @since 2.2
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class MemcachedCache extends CacheProvider
{
/**
* @var Memcached|null
*/
private $memcached;
/**
* Sets the memcache instance to use.
*
* @param Memcached $memcached
*
* @return void
*/
public function setMemcached(Memcached $memcached)
{
$this->memcached = $memcached;
}
/**
* Gets the memcached instance used by the cache.
*
* @return Memcached|null
*/
public function getMemcached()
{
return $this->memcached;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return $this->memcached->get($id);
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
return $this->memcached->getMulti($keys) ?: [];
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($lifetime > 30 * 24 * 3600) {
$lifetime = time() + $lifetime;
}
return $this->memcached->setMulti($keysAndValues, $lifetime);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
$this->memcached->get($id);
return $this->memcached->getResultCode() === Memcached::RES_SUCCESS;
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
if ($lifeTime > 30 * 24 * 3600) {
$lifeTime = time() + $lifeTime;
}
return $this->memcached->set($id, $data, (int) $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return $this->memcached->delete($id)
|| $this->memcached->getResultCode() === Memcached::RES_NOTFOUND;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return $this->memcached->flush();
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$stats = $this->memcached->getStats();
$servers = $this->memcached->getServerList();
$key = $servers[0]['host'] . ':' . $servers[0]['port'];
$stats = $stats[$key];
return array(
Cache::STATS_HITS => $stats['get_hits'],
Cache::STATS_MISSES => $stats['get_misses'],
Cache::STATS_UPTIME => $stats['uptime'],
Cache::STATS_MEMORY_USAGE => $stats['bytes'],
Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'],
);
}
}

View File

@@ -0,0 +1,197 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use MongoBinData;
use MongoCollection;
use MongoCursorException;
use MongoDate;
/**
* MongoDB cache provider.
*
* @since 1.1
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class MongoDBCache extends CacheProvider
{
/**
* The data field will store the serialized PHP value.
*/
const DATA_FIELD = 'd';
/**
* The expiration field will store a MongoDate value indicating when the
* cache entry should expire.
*
* With MongoDB 2.2+, entries can be automatically deleted by MongoDB by
* indexing this field with the "expireAfterSeconds" option equal to zero.
* This will direct MongoDB to regularly query for and delete any entries
* whose date is older than the current time. Entries without a date value
* in this field will be ignored.
*
* The cache provider will also check dates on its own, in case expired
* entries are fetched before MongoDB's TTLMonitor pass can expire them.
*
* @see http://docs.mongodb.org/manual/tutorial/expire-data/
*/
const EXPIRATION_FIELD = 'e';
/**
* @var MongoCollection
*/
private $collection;
/**
* Constructor.
*
* This provider will default to the write concern and read preference
* options set on the MongoCollection instance (or inherited from MongoDB or
* MongoClient). Using an unacknowledged write concern (< 1) may make the
* return values of delete() and save() unreliable. Reading from secondaries
* may make contain() and fetch() unreliable.
*
* @see http://www.php.net/manual/en/mongo.readpreferences.php
* @see http://www.php.net/manual/en/mongo.writeconcerns.php
* @param MongoCollection $collection
*/
public function __construct(MongoCollection $collection)
{
$this->collection = $collection;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
$document = $this->collection->findOne(array('_id' => $id), array(self::DATA_FIELD, self::EXPIRATION_FIELD));
if ($document === null) {
return false;
}
if ($this->isExpired($document)) {
$this->doDelete($id);
return false;
}
return unserialize($document[self::DATA_FIELD]->bin);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
$document = $this->collection->findOne(array('_id' => $id), array(self::EXPIRATION_FIELD));
if ($document === null) {
return false;
}
if ($this->isExpired($document)) {
$this->doDelete($id);
return false;
}
return true;
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
try {
$result = $this->collection->update(
array('_id' => $id),
array('$set' => array(
self::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null),
self::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY),
)),
array('upsert' => true, 'multiple' => false)
);
} catch (MongoCursorException $e) {
return false;
}
return isset($result['ok']) ? $result['ok'] == 1 : true;
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
$result = $this->collection->remove(array('_id' => $id));
return isset($result['ok']) ? $result['ok'] == 1 : true;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
// Use remove() in lieu of drop() to maintain any collection indexes
$result = $this->collection->remove();
return isset($result['ok']) ? $result['ok'] == 1 : true;
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$serverStatus = $this->collection->db->command(array(
'serverStatus' => 1,
'locks' => 0,
'metrics' => 0,
'recordStats' => 0,
'repl' => 0,
));
$collStats = $this->collection->db->command(array('collStats' => 1));
return array(
Cache::STATS_HITS => null,
Cache::STATS_MISSES => null,
Cache::STATS_UPTIME => (isset($serverStatus['uptime']) ? (int) $serverStatus['uptime'] : null),
Cache::STATS_MEMORY_USAGE => (isset($collStats['size']) ? (int) $collStats['size'] : null),
Cache::STATS_MEMORY_AVAILABLE => null,
);
}
/**
* Check if the document is expired.
*
* @param array $document
*
* @return bool
*/
private function isExpired(array $document)
{
return isset($document[self::EXPIRATION_FIELD]) &&
$document[self::EXPIRATION_FIELD] instanceof MongoDate &&
$document[self::EXPIRATION_FIELD]->sec < time();
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Interface for cache drivers that allows to get many items at once.
*
* @link www.doctrine-project.org
* @since 1.4
* @author Asmir Mustafic <goetas@gmail.com>
*/
interface MultiGetCache
{
/**
* Returns an associative array of values for keys is found in cache.
*
* @param string[] $keys Array of keys to retrieve from cache
* @return mixed[] Array of retrieved values, indexed by the specified keys.
* Values that couldn't be retrieved are not contained in this array.
*/
function fetchMultiple(array $keys);
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Interface for cache drivers that allows to put many items at once.
*
* @link www.doctrine-project.org
* @since 1.6
* @author Daniel Gorgan <danut007ro@gmail.com>
*/
interface MultiPutCache
{
/**
* Returns a boolean value indicating if the operation succeeded.
*
* @param array $keysAndValues Array of keys and values to save in cache
* @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these
* cache entries (0 => infinite lifeTime).
*
* @return bool TRUE if the operation was successful, FALSE if it wasn't.
*/
function saveMultiple(array $keysAndValues, $lifetime = 0);
}

View File

@@ -0,0 +1,120 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Php file cache driver.
*
* @since 2.3
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
*/
class PhpFileCache extends FileCache
{
const EXTENSION = '.doctrinecache.php';
/**
* {@inheritdoc}
*/
public function __construct($directory, $extension = self::EXTENSION, $umask = 0002)
{
parent::__construct($directory, $extension, $umask);
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
$value = $this->includeFileForId($id);
if (! $value) {
return false;
}
if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) {
return false;
}
return $value['data'];
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
$value = $this->includeFileForId($id);
if (! $value) {
return false;
}
return $value['lifetime'] === 0 || $value['lifetime'] > time();
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
if ($lifeTime > 0) {
$lifeTime = time() + $lifeTime;
}
if (is_object($data) && ! method_exists($data, '__set_state')) {
throw new \InvalidArgumentException(
"Invalid argument given, PhpFileCache only allows objects that implement __set_state() " .
"and fully support var_export(). You can use the FilesystemCache to save arbitrary object " .
"graphs using serialize()/deserialize()."
);
}
$filename = $this->getFilename($id);
$value = array(
'lifetime' => $lifeTime,
'data' => $data
);
$value = var_export($value, true);
$code = sprintf('<?php return %s;', $value);
return $this->writeFile($filename, $code);
}
/**
* @param string $id
*
* @return array|false
*/
private function includeFileForId($id)
{
$fileName = $this->getFilename($id);
// note: error suppression is still faster than `file_exists`, `is_file` and `is_readable`
$value = @include $fileName;
if (! isset($value['lifetime'])) {
return false;
}
return $value;
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace Doctrine\Common\Cache;
use Predis\ClientInterface;
/**
* Predis cache provider.
*
* @author othillo <othillo@othillo.nl>
*/
class PredisCache extends CacheProvider
{
/**
* @var ClientInterface
*/
private $client;
/**
* @param ClientInterface $client
*
* @return void
*/
public function __construct(ClientInterface $client)
{
$this->client = $client;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
$result = $this->client->get($id);
if (null === $result) {
return false;
}
return unserialize($result);
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
$fetchedItems = call_user_func_array(array($this->client, 'mget'), $keys);
return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems)));
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($lifetime) {
$success = true;
// Keys have lifetime, use SETEX for each of them
foreach ($keysAndValues as $key => $value) {
$response = $this->client->setex($key, $lifetime, serialize($value));
if ((string) $response != 'OK') {
$success = false;
}
}
return $success;
}
// No lifetime, use MSET
$response = $this->client->mset(array_map(function ($value) {
return serialize($value);
}, $keysAndValues));
return (string) $response == 'OK';
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return (bool) $this->client->exists($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
$data = serialize($data);
if ($lifeTime > 0) {
$response = $this->client->setex($id, $lifeTime, $data);
} else {
$response = $this->client->set($id, $data);
}
return $response === true || $response == 'OK';
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return $this->client->del($id) >= 0;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
$response = $this->client->flushdb();
return $response === true || $response == 'OK';
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = $this->client->info();
return array(
Cache::STATS_HITS => $info['Stats']['keyspace_hits'],
Cache::STATS_MISSES => $info['Stats']['keyspace_misses'],
Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'],
Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'],
Cache::STATS_MEMORY_AVAILABLE => false
);
}
}

View File

@@ -0,0 +1,180 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use Redis;
/**
* Redis cache provider.
*
* @link www.doctrine-project.org
* @since 2.2
* @author Osman Ungur <osmanungur@gmail.com>
*/
class RedisCache extends CacheProvider
{
/**
* @var Redis|null
*/
private $redis;
/**
* Sets the redis instance to use.
*
* @param Redis $redis
*
* @return void
*/
public function setRedis(Redis $redis)
{
$redis->setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue());
$this->redis = $redis;
}
/**
* Gets the redis instance used by the cache.
*
* @return Redis|null
*/
public function getRedis()
{
return $this->redis;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return $this->redis->get($id);
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
$fetchedItems = array_combine($keys, $this->redis->mget($keys));
// Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data.
$foundItems = array();
foreach ($fetchedItems as $key => $value) {
if (false !== $value || $this->redis->exists($key)) {
$foundItems[$key] = $value;
}
}
return $foundItems;
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
if ($lifetime) {
$success = true;
// Keys have lifetime, use SETEX for each of them
foreach ($keysAndValues as $key => $value) {
if (!$this->redis->setex($key, $lifetime, $value)) {
$success = false;
}
}
return $success;
}
// No lifetime, use MSET
return (bool) $this->redis->mset($keysAndValues);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return $this->redis->exists($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
if ($lifeTime > 0) {
return $this->redis->setex($id, $lifeTime, $data);
}
return $this->redis->set($id, $data);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return $this->redis->delete($id) >= 0;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return $this->redis->flushDB();
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = $this->redis->info();
return array(
Cache::STATS_HITS => $info['keyspace_hits'],
Cache::STATS_MISSES => $info['keyspace_misses'],
Cache::STATS_UPTIME => $info['uptime_in_seconds'],
Cache::STATS_MEMORY_USAGE => $info['used_memory'],
Cache::STATS_MEMORY_AVAILABLE => false
);
}
/**
* Returns the serializer constant to use. If Redis is compiled with
* igbinary support, that is used. Otherwise the default PHP serializer is
* used.
*
* @return integer One of the Redis::SERIALIZER_* constants
*/
protected function getSerializerValue()
{
if (defined('HHVM_VERSION')) {
return Redis::SERIALIZER_PHP;
}
if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
return Redis::SERIALIZER_IGBINARY;
}
return Redis::SERIALIZER_PHP;
}
}

View File

@@ -0,0 +1,250 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use Riak\Bucket;
use Riak\Connection;
use Riak\Input;
use Riak\Exception;
use Riak\Object;
/**
* Riak cache provider.
*
* @link www.doctrine-project.org
* @since 1.1
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class RiakCache extends CacheProvider
{
const EXPIRES_HEADER = 'X-Riak-Meta-Expires';
/**
* @var \Riak\Bucket
*/
private $bucket;
/**
* Sets the riak bucket instance to use.
*
* @param \Riak\Bucket $bucket
*/
public function __construct(Bucket $bucket)
{
$this->bucket = $bucket;
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
try {
$response = $this->bucket->get($id);
// No objects found
if ( ! $response->hasObject()) {
return false;
}
// Check for attempted siblings
$object = ($response->hasSiblings())
? $this->resolveConflict($id, $response->getVClock(), $response->getObjectList())
: $response->getFirstObject();
// Check for expired object
if ($this->isExpired($object)) {
$this->bucket->delete($object);
return false;
}
return unserialize($object->getContent());
} catch (Exception\RiakException $e) {
// Covers:
// - Riak\ConnectionException
// - Riak\CommunicationException
// - Riak\UnexpectedResponseException
// - Riak\NotFoundException
}
return false;
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
try {
// We only need the HEAD, not the entire object
$input = new Input\GetInput();
$input->setReturnHead(true);
$response = $this->bucket->get($id, $input);
// No objects found
if ( ! $response->hasObject()) {
return false;
}
$object = $response->getFirstObject();
// Check for expired object
if ($this->isExpired($object)) {
$this->bucket->delete($object);
return false;
}
return true;
} catch (Exception\RiakException $e) {
// Do nothing
}
return false;
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
try {
$object = new Object($id);
$object->setContent(serialize($data));
if ($lifeTime > 0) {
$object->addMetadata(self::EXPIRES_HEADER, (string) (time() + $lifeTime));
}
$this->bucket->put($object);
return true;
} catch (Exception\RiakException $e) {
// Do nothing
}
return false;
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
try {
$this->bucket->delete($id);
return true;
} catch (Exception\BadArgumentsException $e) {
// Key did not exist on cluster already
} catch (Exception\RiakException $e) {
// Covers:
// - Riak\Exception\ConnectionException
// - Riak\Exception\CommunicationException
// - Riak\Exception\UnexpectedResponseException
}
return false;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
try {
$keyList = $this->bucket->getKeyList();
foreach ($keyList as $key) {
$this->bucket->delete($key);
}
return true;
} catch (Exception\RiakException $e) {
// Do nothing
}
return false;
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
// Only exposed through HTTP stats API, not Protocol Buffers API
return null;
}
/**
* Check if a given Riak Object have expired.
*
* @param \Riak\Object $object
*
* @return bool
*/
private function isExpired(Object $object)
{
$metadataMap = $object->getMetadataMap();
return isset($metadataMap[self::EXPIRES_HEADER])
&& $metadataMap[self::EXPIRES_HEADER] < time();
}
/**
* On-read conflict resolution. Applied approach here is last write wins.
* Specific needs may override this method to apply alternate conflict resolutions.
*
* {@internal Riak does not attempt to resolve a write conflict, and store
* it as sibling of conflicted one. By following this approach, it is up to
* the next read to resolve the conflict. When this happens, your fetched
* object will have a list of siblings (read as a list of objects).
* In our specific case, we do not care about the intermediate ones since
* they are all the same read from storage, and we do apply a last sibling
* (last write) wins logic.
* If by any means our resolution generates another conflict, it'll up to
* next read to properly solve it.}
*
* @param string $id
* @param string $vClock
* @param array $objectList
*
* @return \Riak\Object
*/
protected function resolveConflict($id, $vClock, array $objectList)
{
// Our approach here is last-write wins
$winner = $objectList[count($objectList)];
$putInput = new Input\PutInput();
$putInput->setVClock($vClock);
$mergedObject = new Object($id);
$mergedObject->setContent($winner->getContent());
$this->bucket->put($mergedObject, $putInput);
return $mergedObject;
}
}

View File

@@ -0,0 +1,220 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
use SQLite3;
use SQLite3Result;
/**
* SQLite3 cache provider.
*
* @since 1.4
* @author Jake Bell <jake@theunraveler.com>
*/
class SQLite3Cache extends CacheProvider
{
/**
* The ID field will store the cache key.
*/
const ID_FIELD = 'k';
/**
* The data field will store the serialized PHP value.
*/
const DATA_FIELD = 'd';
/**
* The expiration field will store a date value indicating when the
* cache entry should expire.
*/
const EXPIRATION_FIELD = 'e';
/**
* @var SQLite3
*/
private $sqlite;
/**
* @var string
*/
private $table;
/**
* Constructor.
*
* Calling the constructor will ensure that the database file and table
* exist and will create both if they don't.
*
* @param SQLite3 $sqlite
* @param string $table
*/
public function __construct(SQLite3 $sqlite, $table)
{
$this->sqlite = $sqlite;
$this->table = (string) $table;
list($id, $data, $exp) = $this->getFields();
return $this->sqlite->exec(sprintf(
'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)',
$table,
$id,
$data,
$exp
));
}
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
if ($item = $this->findById($id)) {
return unserialize($item[self::DATA_FIELD]);
}
return false;
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return null !== $this->findById($id, false);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
$statement = $this->sqlite->prepare(sprintf(
'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)',
$this->table,
implode(',', $this->getFields())
));
$statement->bindValue(':id', $id);
$statement->bindValue(':data', serialize($data), SQLITE3_BLOB);
$statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null);
return $statement->execute() instanceof SQLite3Result;
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
list($idField) = $this->getFields();
$statement = $this->sqlite->prepare(sprintf(
'DELETE FROM %s WHERE %s = :id',
$this->table,
$idField
));
$statement->bindValue(':id', $id);
return $statement->execute() instanceof SQLite3Result;
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table));
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
// no-op.
}
/**
* Find a single row by ID.
*
* @param mixed $id
* @param bool $includeData
*
* @return array|null
*/
private function findById($id, $includeData = true)
{
list($idField) = $fields = $this->getFields();
if (!$includeData) {
$key = array_search(static::DATA_FIELD, $fields);
unset($fields[$key]);
}
$statement = $this->sqlite->prepare(sprintf(
'SELECT %s FROM %s WHERE %s = :id LIMIT 1',
implode(',', $fields),
$this->table,
$idField
));
$statement->bindValue(':id', $id, SQLITE3_TEXT);
$item = $statement->execute()->fetchArray(SQLITE3_ASSOC);
if ($item === false) {
return null;
}
if ($this->isExpired($item)) {
$this->doDelete($id);
return null;
}
return $item;
}
/**
* Gets an array of the fields in our table.
*
* @return array
*/
private function getFields()
{
return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD);
}
/**
* Check if the item is expired.
*
* @param array $item
*
* @return bool
*/
private function isExpired(array $item)
{
return isset($item[static::EXPIRATION_FIELD]) &&
$item[self::EXPIRATION_FIELD] !== null &&
$item[self::EXPIRATION_FIELD] < time();
}
}

View File

@@ -0,0 +1,25 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
class Version
{
const VERSION = '1.6.1-DEV';
}

View File

@@ -0,0 +1,78 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Void cache driver. The cache could be of use in tests where you don`t need to cache anything.
*
* @link www.doctrine-project.org
* @since 1.5
* @author Kotlyar Maksim <kotlyar.maksim@gmail.com>
*/
class VoidCache extends CacheProvider
{
/**
* {@inheritDoc}
*/
protected function doFetch($id)
{
return false;
}
/**
* {@inheritDoc}
*/
protected function doContains($id)
{
return false;
}
/**
* {@inheritDoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return true;
}
/**
* {@inheritDoc}
*/
protected function doDelete($id)
{
return true;
}
/**
* {@inheritDoc}
*/
protected function doFlush()
{
return true;
}
/**
* {@inheritDoc}
*/
protected function doGetStats()
{
return;
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* WinCache cache provider.
*
* @link www.doctrine-project.org
* @since 2.2
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class WinCacheCache extends CacheProvider
{
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return wincache_ucache_get($id);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return wincache_ucache_exists($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return wincache_ucache_set($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return wincache_ucache_delete($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
return wincache_ucache_clear();
}
/**
* {@inheritdoc}
*/
protected function doFetchMultiple(array $keys)
{
return wincache_ucache_get($keys);
}
/**
* {@inheritdoc}
*/
protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
{
$result = wincache_ucache_set($keysAndValues, null, $lifetime);
return empty($result);
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$info = wincache_ucache_info();
$meminfo = wincache_ucache_meminfo();
return array(
Cache::STATS_HITS => $info['total_hit_count'],
Cache::STATS_MISSES => $info['total_miss_count'],
Cache::STATS_UPTIME => $info['total_cache_uptime'],
Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'],
Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'],
);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Xcache cache driver.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Jonathan Wage <jonwage@gmail.com>
* @author Roman Borschel <roman@code-factory.org>
* @author David Abdemoulaie <dave@hobodave.com>
*/
class XcacheCache extends CacheProvider
{
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return $this->doContains($id) ? unserialize(xcache_get($id)) : false;
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return xcache_isset($id);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return xcache_set($id, serialize($data), (int) $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return xcache_unset($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
$this->checkAuthorization();
xcache_clear_cache(XC_TYPE_VAR);
return true;
}
/**
* Checks that xcache.admin.enable_auth is Off.
*
* @return void
*
* @throws \BadMethodCallException When xcache.admin.enable_auth is On.
*/
protected function checkAuthorization()
{
if (ini_get('xcache.admin.enable_auth')) {
throw new \BadMethodCallException(
'To use all features of \Doctrine\Common\Cache\XcacheCache, '
. 'you must set "xcache.admin.enable_auth" to "Off" in your php.ini.'
);
}
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
$this->checkAuthorization();
$info = xcache_info(XC_TYPE_VAR, 0);
return array(
Cache::STATS_HITS => $info['hits'],
Cache::STATS_MISSES => $info['misses'],
Cache::STATS_UPTIME => null,
Cache::STATS_MEMORY_USAGE => $info['size'],
Cache::STATS_MEMORY_AVAILABLE => $info['avail'],
);
}
}

View File

@@ -0,0 +1,83 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\Common\Cache;
/**
* Zend Data Cache cache driver.
*
* @link www.doctrine-project.org
* @since 2.0
* @author Ralph Schindler <ralph.schindler@zend.com>
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class ZendDataCache extends CacheProvider
{
/**
* {@inheritdoc}
*/
protected function doFetch($id)
{
return zend_shm_cache_fetch($id);
}
/**
* {@inheritdoc}
*/
protected function doContains($id)
{
return (false !== zend_shm_cache_fetch($id));
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
return zend_shm_cache_store($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doDelete($id)
{
return zend_shm_cache_delete($id);
}
/**
* {@inheritdoc}
*/
protected function doFlush()
{
$namespace = $this->getNamespace();
if (empty($namespace)) {
return zend_shm_cache_clear();
}
return zend_shm_cache_clear($namespace);
}
/**
* {@inheritdoc}
*/
protected function doGetStats()
{
return null;
}
}

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Doctrine Cache Test Suite">
<directory>./tests/Doctrine/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./lib/Doctrine/</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,28 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\ApcCache;
/**
* @requires extension apc
*/
class ApcCacheTest extends CacheTest
{
protected function setUp()
{
if (!ini_get('apc.enable_cli')) {
$this->markTestSkipped('APC must be enabled for the CLI with the ini setting apc.enable_cli=1');
}
}
protected function _getCacheDriver()
{
return new ApcCache();
}
public function testLifetime()
{
$this->markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084');
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\ApcuCache;
/**
* @requires extension apcu
*/
class ApcuCacheTest extends CacheTest
{
protected function setUp()
{
if (!ini_get('apc.enable_cli')) {
$this->markTestSkipped('APC must be enabled for the CLI with the ini setting apc.enable_cli=1');
}
}
protected function _getCacheDriver()
{
return new ApcuCache();
}
public function testLifetime()
{
$this->markTestSkipped('The APC cache TTL is not working in a single process/request. See https://bugs.php.net/bug.php?id=58084');
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\Cache;
class ArrayCacheTest extends CacheTest
{
protected function _getCacheDriver()
{
return new ArrayCache();
}
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$cache->fetch('test1');
$cache->fetch('test2');
$cache->fetch('test3');
$cache->save('test1', 123);
$cache->save('test2', 123);
$cache->fetch('test1');
$cache->fetch('test2');
$cache->fetch('test3');
$stats = $cache->getStats();
$this->assertEquals(2, $stats[Cache::STATS_HITS]);
$this->assertEquals(5, $stats[Cache::STATS_MISSES]); // +1 for internal call to DoctrineNamespaceCacheKey
$this->assertNotNull($stats[Cache::STATS_UPTIME]);
$this->assertNull($stats[Cache::STATS_MEMORY_USAGE]);
$this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]);
$cache->delete('test1');
$cache->delete('test2');
$cache->fetch('test1');
$cache->fetch('test2');
$cache->fetch('test3');
$stats = $cache->getStats();
$this->assertEquals(2, $stats[Cache::STATS_HITS]);
$this->assertEquals(8, $stats[Cache::STATS_MISSES]); // +1 for internal call to DoctrineNamespaceCacheKey
}
protected function isSharedStorage()
{
return false;
}
}

View File

@@ -0,0 +1,155 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\FileCache;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
abstract class BaseFileCacheTest extends CacheTest
{
protected $directory;
protected function setUp()
{
do {
$this->directory = sys_get_temp_dir() . '/doctrine_cache_'. uniqid();
} while (file_exists($this->directory));
}
protected function tearDown()
{
if ( ! is_dir($this->directory)) {
return;
}
$iterator = new RecursiveDirectoryIterator($this->directory);
foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::CHILD_FIRST) as $file) {
if ($file->isFile()) {
@unlink($file->getRealPath());
} elseif ($file->isDir()) {
@rmdir($file->getRealPath());
}
}
@rmdir($this->directory);
}
public function testFlushAllRemovesBalancingDirectories()
{
$cache = $this->_getCacheDriver();
$this->assertTrue($cache->save('key1', 1));
$this->assertTrue($cache->save('key2', 2));
$this->assertTrue($cache->flushAll());
$iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST);
$this->assertCount(0, $iterator);
}
protected function isSharedStorage()
{
return false;
}
public function getPathLengthsToTest()
{
// Windows officially supports 260 bytes including null terminator
// 258 bytes available to use due to php bug #70943
// Windows officially supports 260 bytes including null terminator
// 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943)
// 260 characters is too large - null terminator is included in allowable length
return array(
array(257, false),
array(258, false),
array(259, true),
array(260, true)
);
}
private static function getBasePathForWindowsPathLengthTests($pathLength)
{
return FileCacheTest::getBasePathForWindowsPathLengthTests($pathLength);
}
/**
* @param int $length
* @param string $basePath
*
* @return array
*/
private static function getKeyAndPathFittingLength($length, $basePath)
{
$baseDirLength = strlen($basePath);
$extensionLength = strlen('.doctrine.cache');
$directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR);
$namespaceAndBracketLength = strlen(bin2hex("[][1]"));
$keyLength = $length
- ($baseDirLength
+ $extensionLength
+ $directoryLength
+ $namespaceAndBracketLength);
$key = str_repeat('a', floor($keyLength / 2));
$namespacedKey = '[' . $key . '][1]';
$keyHash = hash('sha256', $namespacedKey);
$keyPath = $basePath
. DIRECTORY_SEPARATOR
. substr($keyHash, 0, 2)
. DIRECTORY_SEPARATOR
. bin2hex($namespacedKey)
. '.doctrine.cache';
$hashedKeyPath = $basePath
. DIRECTORY_SEPARATOR
. substr($keyHash, 0, 2)
. DIRECTORY_SEPARATOR
. '_' . $keyHash
. '.doctrine.cache';
return array($key, $keyPath, $hashedKeyPath);
}
/**
* @dataProvider getPathLengthsToTest
*
* @param int $length
* @param bool $pathShouldBeHashed
*/
public function testWindowsPathLengthLimitIsCorrectlyHandled($length, $pathShouldBeHashed)
{
$this->directory = self::getBasePathForWindowsPathLengthTests($length);
list($key, $keyPath, $hashedKeyPath) = self::getKeyAndPathFittingLength($length, $this->directory);
$this->assertEquals($length, strlen($keyPath), 'Unhashed path should be of correct length.');
$cacheClass = get_class($this->_getCacheDriver());
/* @var $cache \Doctrine\Common\Cache\FileCache */
$cache = new $cacheClass($this->directory, '.doctrine.cache');
// Trick it into thinking this is windows.
$reflClass = new \ReflectionClass(FileCache::class);
$reflProp = $reflClass->getProperty('isRunningOnWindows');
$reflProp->setAccessible(true);
$reflProp->setValue($cache, true);
$reflProp->setAccessible(false);
$value = uniqid('value', true);
$cache->save($key, $value);
$this->assertEquals($value, $cache->fetch($key));
if ($pathShouldBeHashed) {
$this->assertFileExists($hashedKeyPath, 'Path generated for key should be hashed.');
unlink($hashedKeyPath);
} else {
$this->assertFileExists($keyPath, 'Path generated for key should not be hashed.');
unlink($keyPath);
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace Doctrine\Tests\Common\Cache;
class CacheProviderTest extends \Doctrine\Tests\DoctrineTestCase
{
public function testFetchMultiWillFilterNonRequestedKeys()
{
/* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */
$cache = $this->getMockForAbstractClass(
'Doctrine\Common\Cache\CacheProvider',
array(),
'',
true,
true,
true,
array('doFetchMultiple')
);
$cache
->expects($this->once())
->method('doFetchMultiple')
->will($this->returnValue(array(
'[foo][1]' => 'bar',
'[bar][1]' => 'baz',
'[baz][1]' => 'tab',
)));
$this->assertEquals(
array('foo' => 'bar', 'bar' => 'baz'),
$cache->fetchMultiple(array('foo', 'bar'))
);
}
public function testFailedDeleteAllDoesNotChangeNamespaceVersion()
{
/* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */
$cache = $this->getMockForAbstractClass(
'Doctrine\Common\Cache\CacheProvider',
array(),
'',
true,
true,
true,
array('doFetch', 'doSave', 'doContains')
);
$cache
->expects($this->once())
->method('doFetch')
->with('DoctrineNamespaceCacheKey[]')
->will($this->returnValue(false));
// doSave is only called once from deleteAll as we do not need to persist the default version in getNamespaceVersion()
$cache
->expects($this->once())
->method('doSave')
->with('DoctrineNamespaceCacheKey[]')
->will($this->returnValue(false));
// After a failed deleteAll() the local namespace version is not increased (still 1). Otherwise all data written afterwards
// would be lost outside the current instance.
$cache
->expects($this->once())
->method('doContains')
->with('[key][1]')
->will($this->returnValue(true));
$this->assertFalse($cache->deleteAll(), 'deleteAll() returns false when saving the namespace version fails');
$cache->contains('key');
}
public function testSaveMultipleNoFail()
{
/* @var $cache \Doctrine\Common\Cache\CacheProvider|\PHPUnit_Framework_MockObject_MockObject */
$cache = $this->getMockForAbstractClass(
'Doctrine\Common\Cache\CacheProvider',
array(),
'',
true,
true,
true,
array('doSave')
);
$cache
->expects($this->at(1))
->method('doSave')
->with('[kerr][1]', 'verr', 0)
->will($this->returnValue(false));
$cache
->expects($this->at(2))
->method('doSave')
->with('[kok][1]', 'vok', 0)
->will($this->returnValue(true));
$cache->saveMultiple(array(
'kerr' => 'verr',
'kok' => 'vok',
));
}
}

View File

@@ -0,0 +1,473 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
use ArrayObject;
abstract class CacheTest extends \Doctrine\Tests\DoctrineTestCase
{
/**
* @dataProvider provideDataToCache
*/
public function testSetContainsFetchDelete($value)
{
$cache = $this->_getCacheDriver();
// Test saving a value, checking if it exists, and fetching it back
$this->assertTrue($cache->save('key', $value));
$this->assertTrue($cache->contains('key'));
if (is_object($value)) {
$this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference');
} else {
$this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type');
}
// Test deleting a value
$this->assertTrue($cache->delete('key'));
$this->assertFalse($cache->contains('key'));
$this->assertFalse($cache->fetch('key'));
}
/**
* @dataProvider provideDataToCache
*/
public function testUpdateExistingEntry($value)
{
$cache = $this->_getCacheDriver();
$this->assertTrue($cache->save('key', 'old-value'));
$this->assertTrue($cache->contains('key'));
$this->assertTrue($cache->save('key', $value));
$this->assertTrue($cache->contains('key'));
if (is_object($value)) {
$this->assertEquals($value, $cache->fetch('key'), 'Objects retrieved from the cache must be equal but not necessarily the same reference');
} else {
$this->assertSame($value, $cache->fetch('key'), 'Scalar and array data retrieved from the cache must be the same as the original, e.g. same type');
}
}
public function testCacheKeyIsCaseSensitive()
{
$cache = $this->_getCacheDriver();
$this->assertTrue($cache->save('key', 'value'));
$this->assertTrue($cache->contains('key'));
$this->assertSame('value', $cache->fetch('key'));
$this->assertFalse($cache->contains('KEY'));
$this->assertFalse($cache->fetch('KEY'));
$cache->delete('KEY');
$this->assertTrue($cache->contains('key', 'Deleting cache item with different case must not affect other cache item'));
}
public function testFetchMultiple()
{
$cache = $this->_getCacheDriver();
$values = $this->provideDataToCache();
$saved = array();
foreach ($values as $key => $value) {
$cache->save($key, $value[0]);
$saved[$key] = $value[0];
}
$keys = array_keys($saved);
$this->assertEquals(
$saved,
$cache->fetchMultiple($keys),
'Testing fetchMultiple with different data types'
);
$this->assertEquals(
array_slice($saved, 0, 1),
$cache->fetchMultiple(array_slice($keys, 0, 1)),
'Testing fetchMultiple with a single key'
);
$keysWithNonExisting = array();
$keysWithNonExisting[] = 'non_existing1';
$keysWithNonExisting[] = $keys[0];
$keysWithNonExisting[] = 'non_existing2';
$keysWithNonExisting[] = $keys[1];
$keysWithNonExisting[] = 'non_existing3';
$this->assertEquals(
array_slice($saved, 0, 2),
$cache->fetchMultiple($keysWithNonExisting),
'Testing fetchMultiple with a subset of keys and mixed with non-existing ones'
);
}
public function testFetchMultipleWithNoKeys()
{
$cache = $this->_getCacheDriver();
$this->assertSame(array(), $cache->fetchMultiple(array()));
}
public function testSaveMultiple()
{
$cache = $this->_getCacheDriver();
$cache->deleteAll();
$data = array_map(function ($value) {
return $value[0];
}, $this->provideDataToCache());
$this->assertTrue($cache->saveMultiple($data));
$keys = array_keys($data);
$this->assertEquals($data, $cache->fetchMultiple($keys));
}
public function provideDataToCache()
{
$obj = new \stdClass();
$obj->foo = 'bar';
$obj2 = new \stdClass();
$obj2->bar = 'foo';
$obj2->obj = $obj;
$obj->obj2 = $obj2;
return array(
'array' => array(array('one', 2, 3.01)),
'string' => array('value'),
'string_invalid_utf8' => array("\xc3\x28"),
'string_null_byte' => array('with'."\0".'null char'),
'integer' => array(1),
'float' => array(1.5),
'object' => array(new ArrayObject(array('one', 2, 3.01))),
'object_recursive' => array($obj),
'true' => array(true),
// the following are considered FALSE in boolean context, but caches should still recognize their existence
'null' => array(null),
'false' => array(false),
'array_empty' => array(array()),
'string_zero' => array('0'),
'integer_zero' => array(0),
'float_zero' => array(0.0),
'string_empty' => array(''),
);
}
public function testDeleteIsSuccessfulWhenKeyDoesNotExist()
{
$cache = $this->_getCacheDriver();
$cache->delete('key');
$this->assertFalse($cache->contains('key'));
$this->assertTrue($cache->delete('key'));
}
public function testDeleteAll()
{
$cache = $this->_getCacheDriver();
$this->assertTrue($cache->save('key1', 1));
$this->assertTrue($cache->save('key2', 2));
$this->assertTrue($cache->deleteAll());
$this->assertFalse($cache->contains('key1'));
$this->assertFalse($cache->contains('key2'));
}
/**
* @dataProvider provideCacheIds
*/
public function testCanHandleSpecialCacheIds($id)
{
$cache = $this->_getCacheDriver();
$this->assertTrue($cache->save($id, 'value'));
$this->assertTrue($cache->contains($id));
$this->assertEquals('value', $cache->fetch($id));
$this->assertTrue($cache->delete($id));
$this->assertFalse($cache->contains($id));
$this->assertFalse($cache->fetch($id));
}
public function testNoCacheIdCollisions()
{
$cache = $this->_getCacheDriver();
$ids = $this->provideCacheIds();
// fill cache with each id having a different value
foreach ($ids as $index => $id) {
$cache->save($id[0], $index);
}
// then check value of each cache id
foreach ($ids as $index => $id) {
$value = $cache->fetch($id[0]);
$this->assertNotFalse($value, sprintf('Failed to retrieve data for cache id "%s".', $id[0]));
if ($index !== $value) {
$this->fail(sprintf('Cache id "%s" collides with id "%s".', $id[0], $ids[$value][0]));
}
}
}
/**
* Returns cache ids with special characters that should still work.
*
* For example, the characters :\/<>"*?| are not valid in Windows filenames. So they must be encoded properly.
* Each cache id should be considered different from the others.
*
* @return array
*/
public function provideCacheIds()
{
return array(
array(':'),
array('\\'),
array('/'),
array('<'),
array('>'),
array('"'),
array('*'),
array('?'),
array('|'),
array('['),
array(']'),
array('ä'),
array('a'),
array('é'),
array('e'),
array('.'), // directory traversal
array('..'), // directory traversal
array('-'),
array('_'),
array('$'),
array('%'),
array(' '),
array("\0"),
array(''),
array(str_repeat('a', 300)), // long key
array(str_repeat('a', 113)),
);
}
public function testLifetime()
{
$cache = $this->_getCacheDriver();
$cache->save('expire', 'value', 1);
$this->assertTrue($cache->contains('expire'), 'Data should not be expired yet');
// @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead
sleep(2);
$this->assertFalse($cache->contains('expire'), 'Data should be expired');
}
public function testNoExpire()
{
$cache = $this->_getCacheDriver();
$cache->save('noexpire', 'value', 0);
// @TODO should more TTL-based tests pop up, so then we should mock the `time` API instead
sleep(1);
$this->assertTrue($cache->contains('noexpire'), 'Data with lifetime of zero should not expire');
}
public function testLongLifetime()
{
$cache = $this->_getCacheDriver();
$cache->save('longlifetime', 'value', 30 * 24 * 3600 + 1);
$this->assertTrue($cache->contains('longlifetime'), 'Data with lifetime > 30 days should be accepted');
}
public function testDeleteAllAndNamespaceVersioningBetweenCaches()
{
if ( ! $this->isSharedStorage()) {
$this->markTestSkipped('The cache storage needs to be shared.');
}
$cache1 = $this->_getCacheDriver();
$cache2 = $this->_getCacheDriver();
$this->assertTrue($cache1->save('key1', 1));
$this->assertTrue($cache2->save('key2', 2));
/* Both providers are initialized with the same namespace version, so
* they can see entries set by each other.
*/
$this->assertTrue($cache1->contains('key1'));
$this->assertTrue($cache1->contains('key2'));
$this->assertTrue($cache2->contains('key1'));
$this->assertTrue($cache2->contains('key2'));
/* Deleting all entries through one provider will only increment the
* namespace version on that object (and in the cache itself, which new
* instances will use to initialize). The second provider will retain
* its original version and still see stale data.
*/
$this->assertTrue($cache1->deleteAll());
$this->assertFalse($cache1->contains('key1'));
$this->assertFalse($cache1->contains('key2'));
$this->assertTrue($cache2->contains('key1'));
$this->assertTrue($cache2->contains('key2'));
/* A new cache provider should not see the deleted entries, since its
* namespace version will be initialized.
*/
$cache3 = $this->_getCacheDriver();
$this->assertFalse($cache3->contains('key1'));
$this->assertFalse($cache3->contains('key2'));
}
public function testFlushAll()
{
$cache = $this->_getCacheDriver();
$this->assertTrue($cache->save('key1', 1));
$this->assertTrue($cache->save('key2', 2));
$this->assertTrue($cache->flushAll());
$this->assertFalse($cache->contains('key1'));
$this->assertFalse($cache->contains('key2'));
}
public function testFlushAllAndNamespaceVersioningBetweenCaches()
{
if ( ! $this->isSharedStorage()) {
$this->markTestSkipped('The cache storage needs to be shared.');
}
$cache1 = $this->_getCacheDriver();
$cache2 = $this->_getCacheDriver();
/* Deleting all elements from the first provider should increment its
* namespace version before saving the first entry.
*/
$cache1->deleteAll();
$this->assertTrue($cache1->save('key1', 1));
/* The second provider will be initialized with the same namespace
* version upon its first save operation.
*/
$this->assertTrue($cache2->save('key2', 2));
/* Both providers have the same namespace version and can see entries
* set by each other.
*/
$this->assertTrue($cache1->contains('key1'));
$this->assertTrue($cache1->contains('key2'));
$this->assertTrue($cache2->contains('key1'));
$this->assertTrue($cache2->contains('key2'));
/* Flushing all entries through one cache will remove all entries from
* the cache but leave their namespace version as-is.
*/
$this->assertTrue($cache1->flushAll());
$this->assertFalse($cache1->contains('key1'));
$this->assertFalse($cache1->contains('key2'));
$this->assertFalse($cache2->contains('key1'));
$this->assertFalse($cache2->contains('key2'));
/* Inserting a new entry will use the same, incremented namespace
* version, and it will be visible to both providers.
*/
$this->assertTrue($cache1->save('key1', 1));
$this->assertTrue($cache1->contains('key1'));
$this->assertTrue($cache2->contains('key1'));
/* A new cache provider will be initialized with the original namespace
* version and not share any visibility with the first two providers.
*/
$cache3 = $this->_getCacheDriver();
$this->assertFalse($cache3->contains('key1'));
$this->assertFalse($cache3->contains('key2'));
$this->assertTrue($cache3->save('key3', 3));
$this->assertTrue($cache3->contains('key3'));
}
public function testNamespace()
{
$cache = $this->_getCacheDriver();
$cache->setNamespace('ns1_');
$this->assertTrue($cache->save('key1', 1));
$this->assertTrue($cache->contains('key1'));
$cache->setNamespace('ns2_');
$this->assertFalse($cache->contains('key1'));
}
public function testDeleteAllNamespace()
{
$cache = $this->_getCacheDriver();
$cache->setNamespace('ns1');
$this->assertFalse($cache->contains('key1'));
$cache->save('key1', 'test');
$this->assertTrue($cache->contains('key1'));
$cache->setNamespace('ns2');
$this->assertFalse($cache->contains('key1'));
$cache->save('key1', 'test');
$this->assertTrue($cache->contains('key1'));
$cache->setNamespace('ns1');
$this->assertTrue($cache->contains('key1'));
$cache->deleteAll();
$this->assertFalse($cache->contains('key1'));
$cache->setNamespace('ns2');
$this->assertTrue($cache->contains('key1'));
$cache->deleteAll();
$this->assertFalse($cache->contains('key1'));
}
/**
* @group DCOM-43
*/
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertArrayHasKey(Cache::STATS_HITS, $stats);
$this->assertArrayHasKey(Cache::STATS_MISSES, $stats);
$this->assertArrayHasKey(Cache::STATS_UPTIME, $stats);
$this->assertArrayHasKey(Cache::STATS_MEMORY_USAGE, $stats);
$this->assertArrayHasKey(Cache::STATS_MEMORY_AVAILABLE, $stats);
}
public function testSaveReturnsTrueWithAndWithoutTTlSet()
{
$cache = $this->_getCacheDriver();
$cache->deleteAll();
$this->assertTrue($cache->save('without_ttl', 'without_ttl'));
$this->assertTrue($cache->save('with_ttl', 'with_ttl', 3600));
}
public function testValueThatIsFalseBooleanIsProperlyRetrieved()
{
$cache = $this->_getCacheDriver();
$cache->deleteAll();
$this->assertTrue($cache->save('key1', false));
$this->assertTrue($cache->contains('key1'));
$this->assertFalse($cache->fetch('key1'));
}
/**
* Return whether multiple cache providers share the same storage.
*
* This is used for skipping certain tests for shared storage behavior.
*
* @return bool
*/
protected function isSharedStorage()
{
return true;
}
/**
* @return \Doctrine\Common\Cache\CacheProvider
*/
abstract protected function _getCacheDriver();
}

View File

@@ -0,0 +1,99 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\ApcCache;
use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Cache\ChainCache;
class ChainCacheTest extends CacheTest
{
protected function _getCacheDriver()
{
return new ChainCache(array(new ArrayCache()));
}
public function testLifetime()
{
$this->markTestSkipped('The ChainCache test uses ArrayCache which does not implement TTL currently.');
}
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertInternalType('array', $stats);
}
public function testOnlyFetchFirstOne()
{
$cache1 = new ArrayCache();
$cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
$cache2->expects($this->never())->method('doFetch');
$chainCache = new ChainCache(array($cache1, $cache2));
$chainCache->save('id', 'bar');
$this->assertEquals('bar', $chainCache->fetch('id'));
}
public function testFetchPropagateToFastestCache()
{
$cache1 = new ArrayCache();
$cache2 = new ArrayCache();
$cache2->save('bar', 'value');
$chainCache = new ChainCache(array($cache1, $cache2));
$this->assertFalse($cache1->contains('bar'));
$result = $chainCache->fetch('bar');
$this->assertEquals('value', $result);
$this->assertTrue($cache2->contains('bar'));
}
public function testNamespaceIsPropagatedToAllProviders()
{
$cache1 = new ArrayCache();
$cache2 = new ArrayCache();
$chainCache = new ChainCache(array($cache1, $cache2));
$chainCache->setNamespace('bar');
$this->assertEquals('bar', $cache1->getNamespace());
$this->assertEquals('bar', $cache2->getNamespace());
}
public function testDeleteToAllProviders()
{
$cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
$cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
$cache1->expects($this->once())->method('doDelete');
$cache2->expects($this->once())->method('doDelete');
$chainCache = new ChainCache(array($cache1, $cache2));
$chainCache->delete('bar');
}
public function testFlushToAllProviders()
{
$cache1 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
$cache2 = $this->getMockForAbstractClass('Doctrine\Common\Cache\CacheProvider');
$cache1->expects($this->once())->method('doFlush');
$cache2->expects($this->once())->method('doFlush');
$chainCache = new ChainCache(array($cache1, $cache2));
$chainCache->flushAll();
}
protected function isSharedStorage()
{
return false;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Couchbase;
use Doctrine\Common\Cache\CouchbaseCache;
/**
* @requires extension couchbase
*/
class CouchbaseCacheTest extends CacheTest
{
private $couchbase;
protected function setUp()
{
try {
$this->couchbase = new Couchbase('127.0.0.1', 'Administrator', 'password', 'default');
} catch(Exception $ex) {
$this->markTestSkipped('Could not instantiate the Couchbase cache because of: ' . $ex);
}
}
protected function _getCacheDriver()
{
$driver = new CouchbaseCache();
$driver->setCouchbase($this->couchbase);
return $driver;
}
}

View File

@@ -0,0 +1,268 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
/**
* @group DCOM-101
*/
class FileCacheTest extends \Doctrine\Tests\DoctrineTestCase
{
/**
* @var \Doctrine\Common\Cache\FileCache
*/
private $driver;
protected function setUp()
{
$this->driver = $this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array(), '', false
);
}
public function testFilenameShouldCreateThePathWithOneSubDirectory()
{
$cache = $this->driver;
$method = new \ReflectionMethod($cache, 'getFilename');
$key = 'item-key';
$expectedDir = array(
'84',
);
$expectedDir = implode(DIRECTORY_SEPARATOR, $expectedDir);
$method->setAccessible(true);
$path = $method->invoke($cache, $key);
$dirname = pathinfo($path, PATHINFO_DIRNAME);
$this->assertEquals(DIRECTORY_SEPARATOR . $expectedDir, $dirname);
}
public function testFileExtensionCorrectlyEscaped()
{
$driver1 = $this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array(__DIR__, '.*')
);
$driver2 = $this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array(__DIR__, '.php')
);
$doGetStats = new \ReflectionMethod($driver1, 'doGetStats');
$doGetStats->setAccessible(true);
$stats1 = $doGetStats->invoke($driver1);
$stats2 = $doGetStats->invoke($driver2);
$this->assertSame(0, $stats1[Cache::STATS_MEMORY_USAGE]);
$this->assertGreaterThan(0, $stats2[Cache::STATS_MEMORY_USAGE]);
}
/**
* @group DCOM-266
*/
public function testFileExtensionSlashCorrectlyEscaped()
{
$driver = $this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array(__DIR__ . '/../', DIRECTORY_SEPARATOR . basename(__FILE__))
);
$doGetStats = new \ReflectionMethod($driver, 'doGetStats');
$doGetStats->setAccessible(true);
$stats = $doGetStats->invoke($driver);
$this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_USAGE]);
}
public function testNonIntUmaskThrowsInvalidArgumentException()
{
$this->setExpectedException('InvalidArgumentException');
$this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array('', '', 'invalid')
);
}
public function testGetDirectoryReturnsRealpathDirectoryString()
{
$directory = __DIR__ . '/../';
$driver = $this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array($directory)
);
$doGetDirectory = new \ReflectionMethod($driver, 'getDirectory');
$actualDirectory = $doGetDirectory->invoke($driver);
$expectedDirectory = realpath($directory);
$this->assertEquals($expectedDirectory, $actualDirectory);
}
public function testGetExtensionReturnsExtensionString()
{
$directory = __DIR__ . '/../';
$extension = DIRECTORY_SEPARATOR . basename(__FILE__);
$driver = $this->getMock(
'Doctrine\Common\Cache\FileCache',
array('doFetch', 'doContains', 'doSave'),
array($directory, $extension)
);
$doGetExtension = new \ReflectionMethod($driver, 'getExtension');
$actualExtension = $doGetExtension->invoke($driver);
$this->assertEquals($extension, $actualExtension);
}
const WIN_MAX_PATH_LEN = 258;
public static function getBasePathForWindowsPathLengthTests($pathLength)
{
// Not using __DIR__ because it can get screwed up when xdebug debugger is attached.
$basePath = realpath(sys_get_temp_dir()) . '/' . uniqid('doctrine-cache', true);
/** @noinspection MkdirRaceConditionInspection */
@mkdir($basePath);
$basePath = realpath($basePath);
// Test whether the desired path length is odd or even.
$desiredPathLengthIsOdd = ($pathLength % 2) == 1;
// If the cache key is not too long, the filecache codepath will add
// a slash and bin2hex($key). The length of the added portion will be an odd number.
// len(desired) = len(base path) + len(slash . bin2hex($key))
// odd = even + odd
// even = odd + odd
$basePathLengthShouldBeOdd = !$desiredPathLengthIsOdd;
$basePathLengthIsOdd = (strlen($basePath) % 2) == 1;
// If the base path needs to be odd or even where it is not, we add an odd number of
// characters as a pad. In this case, we're adding '\aa' (or '/aa' depending on platform)
// This is all to make it so that the key we're testing would result in
// a path that is exactly the length we want to test IF the path length limit
// were not in place in FileCache.
if ($basePathLengthIsOdd != $basePathLengthShouldBeOdd) {
$basePath .= DIRECTORY_SEPARATOR . "aa";
}
return $basePath;
}
/**
* @param int $length
* @param string $basePath
*
* @return array
*/
public static function getKeyAndPathFittingLength($length, $basePath)
{
$baseDirLength = strlen($basePath);
$extensionLength = strlen('.doctrine.cache');
$directoryLength = strlen(DIRECTORY_SEPARATOR . 'aa' . DIRECTORY_SEPARATOR);
$keyLength = $length - ($baseDirLength + $extensionLength + $directoryLength); // - 1 because of slash
$key = str_repeat('a', floor($keyLength / 2));
$keyHash = hash('sha256', $key);
$keyPath = $basePath
. DIRECTORY_SEPARATOR
. substr($keyHash, 0, 2)
. DIRECTORY_SEPARATOR
. bin2hex($key)
. '.doctrine.cache';
$hashedKeyPath = $basePath
. DIRECTORY_SEPARATOR
. substr($keyHash, 0, 2)
. DIRECTORY_SEPARATOR
. '_' . $keyHash
. '.doctrine.cache';
return array($key, $keyPath, $hashedKeyPath);
}
public function getPathLengthsToTest()
{
// Windows officially supports 260 bytes including null terminator
// 259 characters is too large due to PHP bug (https://bugs.php.net/bug.php?id=70943)
// 260 characters is too large - null terminator is included in allowable length
return array(
array(257, false),
array(258, false),
array(259, true),
array(260, true)
);
}
/**
* @runInSeparateProcess
* @dataProvider getPathLengthsToTest
*
* @covers \Doctrine\Common\Cache\FileCache::getFilename
*
* @param int $length
* @param bool $pathShouldBeHashed
*/
public function testWindowsPathLengthLimitationsAreCorrectlyRespected($length, $pathShouldBeHashed)
{
if (! defined('PHP_WINDOWS_VERSION_BUILD')) {
define('PHP_WINDOWS_VERSION_BUILD', 'Yes, this is the "usual suspect", with the usual limitations');
}
$basePath = self::getBasePathForWindowsPathLengthTests($length);
$fileCache = $this->getMockForAbstractClass(
'Doctrine\Common\Cache\FileCache',
array($basePath, '.doctrine.cache')
);
list($key, $keyPath, $hashedKeyPath) = self::getKeyAndPathFittingLength($length, $basePath);
$getFileName = new \ReflectionMethod($fileCache, 'getFilename');
$getFileName->setAccessible(true);
$this->assertEquals(
$length,
strlen($keyPath),
sprintf('Path expected to be %d characters long is %d characters long', $length, strlen($keyPath))
);
if ($pathShouldBeHashed) {
$keyPath = $hashedKeyPath;
}
if ($pathShouldBeHashed) {
$this->assertSame(
$hashedKeyPath,
$getFileName->invoke($fileCache, $key),
'Keys should be hashed correctly if they are over the limit.'
);
} else {
$this->assertSame(
$keyPath,
$getFileName->invoke($fileCache, $key),
'Keys below limit of the allowed length are used directly, unhashed'
);
}
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\FilesystemCache;
/**
* @group DCOM-101
*/
class FilesystemCacheTest extends BaseFileCacheTest
{
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNull($stats[Cache::STATS_HITS]);
$this->assertNull($stats[Cache::STATS_MISSES]);
$this->assertNull($stats[Cache::STATS_UPTIME]);
$this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]);
$this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]);
}
public function testCacheInSharedDirectoryIsPerExtension()
{
$cache1 = new FilesystemCache($this->directory, '.foo');
$cache2 = new FilesystemCache($this->directory, '.bar');
$this->assertTrue($cache1->save('key1', 11));
$this->assertTrue($cache1->save('key2', 12));
$this->assertTrue($cache2->save('key1', 21));
$this->assertTrue($cache2->save('key2', 22));
$this->assertSame(11, $cache1->fetch('key1'), 'Cache value must not be influenced by a different cache in the same directory but different extension');
$this->assertSame(12, $cache1->fetch('key2'));
$this->assertTrue($cache1->flushAll());
$this->assertFalse($cache1->fetch('key1'), 'flushAll() must delete all items with the current extension');
$this->assertFalse($cache1->fetch('key2'));
$this->assertSame(21, $cache2->fetch('key1'), 'flushAll() must not remove items with a different extension in a shared directory');
$this->assertSame(22, $cache2->fetch('key2'));
}
public function testFlushAllWithNoExtension()
{
$cache = new FilesystemCache($this->directory, '');
$this->assertTrue($cache->save('key1', 1));
$this->assertTrue($cache->save('key2', 2));
$this->assertTrue($cache->flushAll());
$this->assertFalse($cache->contains('key1'));
$this->assertFalse($cache->contains('key2'));
}
protected function _getCacheDriver()
{
return new FilesystemCache($this->directory);
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\MemcacheCache;
use Memcache;
/**
* @requires extension memcache
*/
class MemcacheCacheTest extends CacheTest
{
private $memcache;
protected function setUp()
{
$this->memcache = new Memcache();
if (@$this->memcache->connect('localhost', 11211) === false) {
unset($this->memcache);
$this->markTestSkipped('Cannot connect to Memcache.');
}
}
protected function tearDown()
{
if ($this->memcache instanceof Memcache) {
$this->memcache->flush();
}
}
/**
* {@inheritdoc}
*
* Memcache does not support " " and null byte as key so we remove them from the tests.
*/
public function provideCacheIds()
{
$ids = parent::provideCacheIds();
unset($ids[21], $ids[22]);
return $ids;
}
public function testGetMemcacheReturnsInstanceOfMemcache()
{
$this->assertInstanceOf('Memcache', $this->_getCacheDriver()->getMemcache());
}
/**
* {@inheritDoc}
*/
protected function _getCacheDriver()
{
$driver = new MemcacheCache();
$driver->setMemcache($this->memcache);
return $driver;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\MemcachedCache;
use Memcached;
/**
* @requires extension memcached
*/
class MemcachedCacheTest extends CacheTest
{
private $memcached;
protected function setUp()
{
$this->memcached = new Memcached();
$this->memcached->setOption(Memcached::OPT_COMPRESSION, false);
$this->memcached->addServer('127.0.0.1', 11211);
if (@fsockopen('127.0.0.1', 11211) === false) {
unset($this->memcached);
$this->markTestSkipped('Cannot connect to Memcached.');
}
}
protected function tearDown()
{
if ($this->memcached instanceof Memcached) {
$this->memcached->flush();
}
}
/**
* {@inheritdoc}
*
* Memcached does not support " ", null byte and very long keys so we remove them from the tests.
*/
public function provideCacheIds()
{
$ids = parent::provideCacheIds();
unset($ids[21], $ids[22], $ids[24]);
return $ids;
}
public function testGetMemcachedReturnsInstanceOfMemcached()
{
$this->assertInstanceOf('Memcached', $this->_getCacheDriver()->getMemcached());
}
/**
* {@inheritDoc}
*/
protected function _getCacheDriver()
{
$driver = new MemcachedCache();
$driver->setMemcached($this->memcached);
return $driver;
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\MongoDBCache;
use MongoClient;
use MongoCollection;
/**
* @requires extension mongo
*/
class MongoDBCacheTest extends CacheTest
{
/**
* @var MongoCollection
*/
private $collection;
protected function setUp()
{
if ( ! version_compare(phpversion('mongo'), '1.3.0', '>=')) {
$this->markTestSkipped('Mongo >= 1.3.0 is required.');
}
$mongo = new MongoClient();
$this->collection = $mongo->selectCollection('doctrine_common_cache', 'test');
}
protected function tearDown()
{
if ($this->collection instanceof MongoCollection) {
$this->collection->drop();
}
}
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNull($stats[Cache::STATS_HITS]);
$this->assertNull($stats[Cache::STATS_MISSES]);
$this->assertGreaterThan(0, $stats[Cache::STATS_UPTIME]);
$this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]);
$this->assertNull($stats[Cache::STATS_MEMORY_AVAILABLE]);
}
/**
* @group 108
*/
public function testMongoCursorExceptionsDoNotBubbleUp()
{
/* @var $collection \MongoCollection|\PHPUnit_Framework_MockObject_MockObject */
$collection = $this->getMock('MongoCollection', array(), array(), '', false);
$collection->expects(self::once())->method('update')->willThrowException(new \MongoCursorException());
$cache = new MongoDBCache($collection);
self::assertFalse($cache->save('foo', 'bar'));
}
protected function _getCacheDriver()
{
return new MongoDBCache($this->collection);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\PhpFileCache;
/**
* @group DCOM-101
*/
class PhpFileCacheTest extends BaseFileCacheTest
{
public function provideDataToCache()
{
$data = parent::provideDataToCache();
unset($data['object'], $data['object_recursive']); // PhpFileCache only allows objects that implement __set_state() and fully support var_export()
if (PHP_VERSION_ID < 70002) {
unset($data['float_zero']); // var_export exports float(0) as int(0): https://bugs.php.net/bug.php?id=66179
}
return $data;
}
public function testImplementsSetState()
{
$cache = $this->_getCacheDriver();
// Test save
$cache->save('test_set_state', new SetStateClass(array(1,2,3)));
//Test __set_state call
$this->assertCount(0, SetStateClass::$values);
// Test fetch
$value = $cache->fetch('test_set_state');
$this->assertInstanceOf('Doctrine\Tests\Common\Cache\SetStateClass', $value);
$this->assertEquals(array(1,2,3), $value->getValue());
//Test __set_state call
$this->assertCount(1, SetStateClass::$values);
// Test contains
$this->assertTrue($cache->contains('test_set_state'));
}
public function testNotImplementsSetState()
{
$cache = $this->_getCacheDriver();
$this->setExpectedException('InvalidArgumentException');
$cache->save('test_not_set_state', new NotSetStateClass(array(1,2,3)));
}
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNull($stats[Cache::STATS_HITS]);
$this->assertNull($stats[Cache::STATS_MISSES]);
$this->assertNull($stats[Cache::STATS_UPTIME]);
$this->assertEquals(0, $stats[Cache::STATS_MEMORY_USAGE]);
$this->assertGreaterThan(0, $stats[Cache::STATS_MEMORY_AVAILABLE]);
}
protected function _getCacheDriver()
{
return new PhpFileCache($this->directory);
}
}
class NotSetStateClass
{
private $value;
public function __construct($value)
{
$this->value = $value;
}
public function getValue()
{
return $this->value;
}
}
class SetStateClass extends NotSetStateClass
{
public static $values = array();
public static function __set_state($data)
{
self::$values = $data;
return new self($data['value']);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\PredisCache;
use Predis\Client;
use Predis\Connection\ConnectionException;
class PredisCacheTest extends CacheTest
{
private $client;
protected function setUp()
{
if (!class_exists('Predis\Client')) {
$this->markTestSkipped('Predis\Client is missing. Make sure to "composer install" to have all dev dependencies.');
}
$this->client = new Client();
try {
$this->client->connect();
} catch (ConnectionException $e) {
$this->markTestSkipped('Cannot connect to Redis because of: ' . $e);
}
}
public function testHitMissesStatsAreProvided()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNotNull($stats[Cache::STATS_HITS]);
$this->assertNotNull($stats[Cache::STATS_MISSES]);
}
/**
* @return PredisCache
*/
protected function _getCacheDriver()
{
return new PredisCache($this->client);
}
/**
* {@inheritDoc}
*
* @dataProvider provideDataToCache
*/
public function testSetContainsFetchDelete($value)
{
if (array() === $value) {
$this->markTestIncomplete(
'Predis currently doesn\'t support saving empty array values. '
. 'See https://github.com/nrk/predis/issues/241'
);
}
parent::testSetContainsFetchDelete($value);
}
/**
* {@inheritDoc}
*
* @dataProvider provideDataToCache
*/
public function testUpdateExistingEntry($value)
{
if (array() === $value) {
$this->markTestIncomplete(
'Predis currently doesn\'t support saving empty array values. '
. 'See https://github.com/nrk/predis/issues/241'
);
}
parent::testUpdateExistingEntry($value);
}
public function testAllowsGenericPredisClient()
{
/* @var $predisClient \Predis\ClientInterface */
$predisClient = $this->getMock('Predis\\ClientInterface');
$this->assertInstanceOf('Doctrine\\Common\\Cache\\PredisCache', new PredisCache($predisClient));
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\RedisCache;
use Doctrine\Common\Cache\Cache;
/**
* @requires extension redis
*/
class RedisCacheTest extends CacheTest
{
private $_redis;
protected function setUp()
{
$this->_redis = new \Redis();
$ok = @$this->_redis->connect('127.0.0.1');
if (!$ok) {
$this->markTestSkipped('Cannot connect to Redis.');
}
}
public function testHitMissesStatsAreProvided()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNotNull($stats[Cache::STATS_HITS]);
$this->assertNotNull($stats[Cache::STATS_MISSES]);
}
public function testGetRedisReturnsInstanceOfRedis()
{
$this->assertInstanceOf('Redis', $this->_getCacheDriver()->getRedis());
}
public function testSerializerOptionWithOutIgbinaryExtension()
{
if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
$this->markTestSkipped('Extension igbinary is loaded.');
}
$this->assertEquals(
\Redis::SERIALIZER_PHP,
$this->_getCacheDriver()->getRedis()->getOption(\Redis::OPT_SERIALIZER)
);
}
/**
* {@inheritDoc}
*/
protected function _getCacheDriver()
{
$driver = new RedisCache();
$driver->setRedis($this->_redis);
return $driver;
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Riak\Bucket;
use Riak\Connection;
use Riak\Exception;
use Doctrine\Common\Cache\RiakCache;
/**
* RiakCache test
*
* @group Riak
* @requires extension riak
*/
class RiakCacheTest extends CacheTest
{
/**
* @var \Riak\Connection
*/
private $connection;
/**
* @var \Riak\Bucket
*/
private $bucket;
protected function setUp()
{
try {
$this->connection = new Connection('127.0.0.1', 8087);
$this->bucket = new Bucket($this->connection, 'test');
} catch (Exception\RiakException $e) {
$this->markTestSkipped('Cannot connect to Riak.');
}
}
/**
* {@inheritdoc}
*/
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNull($stats);
}
/**
* Retrieve RiakCache instance.
*
* @return \Doctrine\Common\Cache\RiakCache
*/
protected function _getCacheDriver()
{
return new RiakCache($this->bucket);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\SQLite3Cache;
use SQLite3;
/**
* @requires extension sqlite3
*/
class SQLite3Test extends CacheTest
{
private $file;
private $sqlite;
protected function setUp()
{
$this->file = tempnam(null, 'doctrine-cache-test-');
unlink($this->file);
$this->sqlite = new SQLite3($this->file);
}
protected function tearDown()
{
$this->sqlite = null; // DB must be closed before
unlink($this->file);
}
public function testGetStats()
{
$this->assertNull($this->_getCacheDriver()->getStats());
}
/**
* {@inheritDoc}
*/
protected function _getCacheDriver()
{
return new SQLite3Cache($this->sqlite, 'test_table');
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\VoidCache;
/**
* @covers \Doctrine\Common\Cache\VoidCache
*/
class VoidCacheTest extends \PHPUnit_Framework_TestCase
{
public function testShouldAlwaysReturnFalseOnContains()
{
$cache = new VoidCache();
$this->assertFalse($cache->contains('foo'));
$this->assertFalse($cache->contains('bar'));
}
public function testShouldAlwaysReturnFalseOnFetch()
{
$cache = new VoidCache();
$this->assertFalse($cache->fetch('foo'));
$this->assertFalse($cache->fetch('bar'));
}
public function testShouldAlwaysReturnTrueOnSaveButNotStoreAnything()
{
$cache = new VoidCache();
$this->assertTrue($cache->save('foo', 'fooVal'));
$this->assertFalse($cache->contains('foo'));
$this->assertFalse($cache->fetch('foo'));
}
public function testShouldAlwaysReturnTrueOnDelete()
{
$cache = new VoidCache();
$this->assertTrue($cache->delete('foo'));
}
public function testShouldAlwaysReturnNullOnGetStatus()
{
$cache = new VoidCache();
$this->assertNull($cache->getStats());
}
public function testShouldAlwaysReturnTrueOnFlush()
{
$cache = new VoidCache();
$this->assertTrue($cache->flushAll());
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\WincacheCache;
/**
* @requires extension wincache
*/
class WincacheCacheTest extends CacheTest
{
protected function _getCacheDriver()
{
return new WincacheCache();
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\XcacheCache;
/**
* @requires extension xcache
*/
class XcacheCacheTest extends CacheTest
{
protected function _getCacheDriver()
{
return new XcacheCache();
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Doctrine\Tests\Common\Cache;
use Doctrine\Common\Cache\ZendDataCache;
/**
* @requires function zend_shm_cache_fetch
*/
class ZendDataCacheTest extends CacheTest
{
protected function setUp()
{
if ('apache2handler' !== php_sapi_name()) {
$this->markTestSkipped('Zend Data Cache only works in apache2handler SAPI.');
}
}
public function testGetStats()
{
$cache = $this->_getCacheDriver();
$stats = $cache->getStats();
$this->assertNull($stats);
}
protected function _getCacheDriver()
{
return new ZendDataCache();
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\Tests;
/**
* Base testcase class for all Doctrine testcases.
*/
abstract class DoctrineTestCase extends \PHPUnit_Framework_TestCase
{
}

View File

@@ -0,0 +1,7 @@
extension="mongo.so"
extension="memcache.so"
extension="memcached.so"
extension="redis.so"
apc.enabled=1
apc.enable_cli=1

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="../../vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<logging>
<log type="coverage-clover" target="../../build/logs/clover.xml"/>
</logging>
<testsuites>
<testsuite name="Doctrine Cache Test Suite">
<directory>../Doctrine/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>../../lib/Doctrine/</directory>
</whitelist>
</filter>
<groups>
<exclude>
<group>performance</group>
</exclude>
</groups>
</phpunit>

View File

@@ -0,0 +1,25 @@
# CHANGELOG
## 0.2.0 - 2019-09-16
* Improvement: Tests for expired items without must-revalidate #20
* Improvement: Support for including Vary headers in cache keys #21
* Improvement: Test for the can_cache option #23
* Bugfix: Not adding timezone to dates #24
* Improvement: Add $defaultTtl to CacheStorage constructor #25
* Bugfix: Error caches responses for without vary headers #26
* Bugfix: stale-if-header not being added to max-age #27
* Bugfix: max-age and freshness confusing zero and null #28
* Bugfix: Use date() method to fix missing GMT #29
* Improvement: Tests for stale-if-error behaviour #30
* Improvement: Delete cache entries on both 404 and 410 responses #33
* Refactoring: Minor ValidationSubscriber.php cleanup #35
* Refactoring: Minor ValidationSubscriber.php cleanup #35
* Improvement: Extend caching ttl considerations #56
* Improvement: Add purge method #57
* Improvement: Integration test for the calculation of the "resident_time" #60
* Improvement: Support for PHP 7.0 & 7.1 #73
## 0.1.0 - 2014-10-29
* Initial release.

View File

@@ -0,0 +1,19 @@
Copyright (c) 2014 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,14 @@
all: clean coverage
test:
vendor/bin/phpunit
coverage:
vendor/bin/phpunit --coverage-html=artifacts/coverage
open artifacts/coverage/index.html
view-coverage:
open artifacts/coverage/index.html
clean:
rm -rf artifacts/*

View File

@@ -0,0 +1,138 @@
=======================
Guzzle Cache Subscriber
=======================
.. important::
**This repo has not been updated for Guzzle 6 and only supports Guzzle 5.**
See https://github.com/Kevinrob/guzzle-cache-middleware for a nice Guzzle 6
compatible Cache middleware.
Provides a private transparent proxy cache for caching HTTP responses.
Here's a simple example of how it's used:
.. code-block:: php
use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
$client = new Client(['defaults' => ['debug' => true]]);
// Use the helper method to attach a cache to the client.
CacheSubscriber::attach($client);
// Send the first request
$a = $client->get('http://en.wikipedia.org/wiki/Main_Page');
// Send the second request. This will find a cache hit which must be
// validated. The validation request returns a 304, which yields the original
// cached response.
$b = $client->get('http://en.wikipedia.org/wiki/Main_Page');
Running the above code sample should output verbose cURL information that looks
something like this:
::
> GET /wiki/Main_Page HTTP/1.1
Host: en.wikipedia.org
User-Agent: Guzzle/4.2.1 curl/7.37.0 PHP/5.5.13
Via: 1.1 GuzzleCache/4.2.1
< HTTP/1.1 200 OK
< Server: Apache
< X-Content-Type-Options: nosniff
< Content-language: en
< X-UA-Compatible: IE=Edge
< Vary: Accept-Encoding,Cookie
< Last-Modified: Thu, 21 Aug 2014 01:51:49 GMT
< Content-Type: text/html; charset=UTF-8
< X-Varnish: 2345493325, 1998949714 1994269567
< Via: 1.1 varnish, 1.1 varnish
< Transfer-Encoding: chunked
< Date: Thu, 21 Aug 2014 02:34:12 GMT
< Age: 2541
< Connection: keep-alive
< X-Cache: cp1055 hit (1), cp1068 frontend hit (25353)
< Cache-Control: private, s-maxage=0, max-age=0, must-revalidate
< Set-Cookie: GeoIP=US:Seattle:47.6062:-122.3321:v4; Path=/; Domain=.wikipedia.org
<
* Connection #0 to host en.wikipedia.org left intact
* Re-using existing connection! (#0) with host en.wikipedia.org
> GET /wiki/Main_Page HTTP/1.1
Host: en.wikipedia.org
User-Agent: Guzzle/4.2.1 curl/7.37.0 PHP/5.5.13
Via: 1.1 GuzzleCache/4.2.1, 1.1 GuzzleCache/4.2.1
If-Modified-Since: Thu, 21 Aug 2014 01:51:49 GMT
< HTTP/1.1 304 Not Modified
< Server: Apache
< X-Content-Type-Options: nosniff
< Content-language: en
< X-UA-Compatible: IE=Edge
< Vary: Accept-Encoding,Cookie
< Last-Modified: Thu, 21 Aug 2014 01:51:49 GMT
< Content-Type: text/html; charset=UTF-8
< X-Varnish: 2345493325, 1998950450 1994269567
< Via: 1.1 varnish, 1.1 varnish
< Date: Thu, 21 Aug 2014 02:34:12 GMT
< Age: 2541
< Connection: keep-alive
< X-Cache: cp1055 hit (1), cp1068 frontend hit (25360)
< Cache-Control: private, s-maxage=0, max-age=0, must-revalidate
< Set-Cookie: GeoIP=US:Seattle:47.6062:-122.3321:v4; Path=/; Domain=.wikipedia.org
<
* Connection #0 to host en.wikipedia.org left intact
Installing
----------
Add the following to your composer.json:
.. code-block:: javascript
{
"require": {
"guzzlehttp/cache-subscriber": "0.2.*@dev"
}
}
or
.. code-block:: console
$ composer require guzzlehttp/cache-subscriber
Creating a CacheSubscriber
--------------------------
The easiest way to create a CacheSubscriber is using the ``attach()`` helper
method of ``GuzzleHttp\Subscriber\Cache\CacheSubscriber``. This method accepts
a request or client object and attaches the necessary subscribers used to
perform cache lookups, validation requests, and automatic purging of resources.
The ``attach()`` method accepts the following options:
storage
A ``GuzzleHttp\Subscriber\Cache\CacheStorageInterface`` object used to
store cached responses. If no value is not provided, an in-memory array
cache will be used.
validate
A Boolean value that determines if cached response are ever validated
against the origin server. This setting defaults to ``true`` but can be
disabled by passing ``false``.
purge
A Boolean value that determines if cached responses are purged when
non-idempotent requests are sent to their URI. This setting defaults to
``true`` but can be disabled by passing ``false``.
can_cache
An optional callable used to determine if a request can be cached. The
callable accepts a ``GuzzleHttp\Message\RequestInterface`` and returns a
Boolean value. If no value is provided, the default behavior is utilized.
.. warning::
This is a WIP update for Guzzle 5+. It hasn't been tested and is in
active development. Expect bugs and breaks.

View File

@@ -0,0 +1,30 @@
{
"name": "guzzlehttp/cache-subscriber",
"description": "Guzzle HTTP cache subscriber",
"homepage": "http://guzzlephp.org/",
"keywords": ["cache", "guzzle"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.4.0",
"guzzlehttp/guzzle": "~5.0",
"doctrine/cache": "~1.3"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": { "GuzzleHttp\\Subscriber\\Cache\\": "src" }
},
"extra": {
"branch-alias": {
"dev-master": "0.2-dev"
}
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite>
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -0,0 +1,403 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use Doctrine\Common\Cache\Cache;
use GuzzleHttp\Message\AbstractMessage;
use GuzzleHttp\Message\MessageInterface;
use GuzzleHttp\Message\Request;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\Stream;
use GuzzleHttp\Stream\StreamInterface;
/**
* Default cache storage implementation.
*/
class CacheStorage implements CacheStorageInterface
{
/** @var string */
private $keyPrefix;
/** @var int Default cache TTL */
private $defaultTtl;
/** @var Cache */
private $cache;
/** @var array Headers are excluded from the caching (see RFC 2616:13.5.1) */
private static $noCache = [
'age' => true,
'connection' => true,
'keep-alive' => true,
'proxy-authenticate' => true,
'proxy-authorization' => true,
'te' => true,
'trailers' => true,
'transfer-encoding' => true,
'upgrade' => true,
'set-cookie' => true,
'set-cookie2' => true,
];
/**
* @param Cache $cache Cache backend.
* @param string $keyPrefix (optional) Key prefix to add to each key.
* @param int $defaultTtl (optional) The default TTL to set, in seconds.
*/
public function __construct(Cache $cache, $keyPrefix = null, $defaultTtl = 0)
{
$this->cache = $cache;
$this->keyPrefix = $keyPrefix;
$this->defaultTtl = $defaultTtl;
}
public function cache(
RequestInterface $request,
ResponseInterface $response
) {
$ctime = time();
$ttl = $this->getTtl($response);
$key = $this->getCacheKey($request, $this->normalizeVary($response));
$headers = $this->persistHeaders($request);
$entries = $this->getManifestEntries($key, $ctime, $response, $headers);
$bodyDigest = null;
// Persist the Vary response header.
if ($response->hasHeader('vary')) {
$this->cacheVary($request, $response);
}
// Persist the response body if needed
if ($response->getBody() && $response->getBody()->getSize() > 0) {
$body = $response->getBody();
$bodyDigest = $this->getBodyKey($request->getUrl(), $body);
$this->cache->save($bodyDigest, (string) $body, $ttl);
}
array_unshift($entries, [
$headers,
$this->persistHeaders($response),
$response->getStatusCode(),
$bodyDigest,
$ctime + $ttl
]);
$this->cache->save($key, serialize($entries));
}
public function delete(RequestInterface $request)
{
$vary = $this->fetchVary($request);
$key = $this->getCacheKey($request, $vary);
$entries = $this->cache->fetch($key);
if (!$entries) {
return;
}
// Delete each cached body
foreach (unserialize($entries) as $entry) {
if ($entry[3]) {
$this->cache->delete($entry[3]);
}
}
// Delete any cached Vary header responses.
$this->deleteVary($request);
$this->cache->delete($key);
}
public function purge($url)
{
foreach (['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PURGE'] as $m) {
$this->delete(new Request($m, $url));
}
}
public function fetch(RequestInterface $request)
{
$vary = $this->fetchVary($request);
if ($vary) {
$key = $this->getCacheKey($request, $vary);
} else {
$key = $this->getCacheKey($request);
}
$entries = $this->cache->fetch($key);
if (!$entries) {
return null;
}
$match = $matchIndex = null;
$headers = $this->persistHeaders($request);
$entries = unserialize($entries);
foreach ($entries as $index => $entry) {
$vary = isset($entry[1]['vary']) ? $entry[1]['vary'] : '';
if ($this->requestsMatch($vary, $headers, $entry[0])) {
$match = $entry;
$matchIndex = $index;
break;
}
}
if (!$match) {
return null;
}
// Ensure that the response is not expired
$response = null;
if ($match[4] < time()) {
$response = -1;
} else {
$response = new Response($match[2], $match[1]);
if ($match[3]) {
if ($body = $this->cache->fetch($match[3])) {
$response->setBody(Stream\Utils::create($body));
} else {
// The response is not valid because the body was somehow
// deleted
$response = -1;
}
}
}
if ($response === -1) {
// Remove the entry from the metadata and update the cache
unset($entries[$matchIndex]);
if ($entries) {
$this->cache->save($key, serialize($entries));
} else {
$this->cache->delete($key);
}
return null;
}
return $response;
}
/**
* Hash a request URL into a string that returns cache metadata.
*
* @param RequestInterface $request The Request to generate the cache key
* for.
* @param array $vary (optional) An array of headers to vary
* the cache key by.
*
* @return string
*/
private function getCacheKey(RequestInterface $request, array $vary = [])
{
$key = $request->getMethod() . ' ' . $request->getUrl();
// If Vary headers have been passed in, fetch each header and add it to
// the cache key.
foreach ($vary as $header) {
$key .= " $header: " . $request->getHeader($header);
}
return $this->keyPrefix . md5($key);
}
/**
* Create a cache key for a response's body.
*
* @param string $url URL of the entry
* @param StreamInterface $body Response body
*
* @return string
*/
private function getBodyKey($url, StreamInterface $body)
{
return $this->keyPrefix . md5($url) . Stream\Utils::hash($body, 'md5');
}
/**
* Determines whether two Request HTTP header sets are non-varying.
*
* @param string $vary Response vary header
* @param array $r1 HTTP header array
* @param array $r2 HTTP header array
*
* @return bool
*/
private function requestsMatch($vary, $r1, $r2)
{
if ($vary) {
foreach (explode(',', $vary) as $header) {
$key = trim(strtolower($header));
$v1 = isset($r1[$key]) ? $r1[$key] : null;
$v2 = isset($r2[$key]) ? $r2[$key] : null;
if ($v1 !== $v2) {
return false;
}
}
}
return true;
}
/**
* Creates an array of cacheable and normalized message headers.
*
* @param MessageInterface $message
*
* @return array
*/
private function persistHeaders(MessageInterface $message)
{
// Clone the response to not destroy any necessary headers when caching
$headers = array_diff_key($message->getHeaders(), self::$noCache);
// Cast the headers to a string
foreach ($headers as &$value) {
$value = implode(', ', $value);
}
return $headers;
}
/**
* Return the TTL to use when caching a Response.
*
* @param ResponseInterface $response The response being cached.
*
* @return int The TTL in seconds.
*/
private function getTtl(ResponseInterface $response)
{
$ttl = 0;
if ($cacheControl = $response->getHeader('Cache-Control')) {
$maxAge = Utils::getDirective($response, 'max-age');
if (is_numeric($maxAge)) {
$ttl += $maxAge;
}
// According to RFC5861 stale headers are *in addition* to any
// max-age values.
$stale = Utils::getDirective($response, 'stale-if-error');
if (is_numeric($stale)) {
$ttl += $stale;
}
} elseif ($expires = $response->getHeader('Expires')) {
$ttl += strtotime($expires) - time();
}
return $ttl ?: $this->defaultTtl;
}
private function getManifestEntries(
$key,
$currentTime,
ResponseInterface $response,
$persistedRequest
) {
$entries = [];
$manifest = $this->cache->fetch($key);
if (!$manifest) {
return $entries;
}
// Determine which cache entries should still be in the cache
$vary = $response->getHeader('Vary');
foreach (unserialize($manifest) as $entry) {
// Check if the entry is expired
if ($entry[4] < $currentTime) {
continue;
}
$varyCmp = isset($entry[1]['vary']) ? $entries[1]['vary'] : '';
if ($vary != $varyCmp ||
!$this->requestsMatch($vary, $entry[0], $persistedRequest)
) {
$entries[] = $entry;
}
}
return $entries;
}
/**
* Return a sorted list of Vary headers.
*
* While headers are case-insensitive, header values are not. We can only
* normalize the order of headers to combine cache entries.
*
* @param ResponseInterface $response The Response with Vary headers.
*
* @return array An array of sorted headers.
*/
private function normalizeVary(ResponseInterface $response)
{
$parts = AbstractMessage::normalizeHeader($response, 'vary');
sort($parts);
return $parts;
}
/**
* Cache the Vary headers from a response.
*
* @param RequestInterface $request The Request that generated the Vary
* headers.
* @param ResponseInterface $response The Response with Vary headers.
*/
private function cacheVary(
RequestInterface $request,
ResponseInterface $response
) {
$key = $this->getVaryKey($request);
$this->cache->save($key, $this->normalizeVary($response), $this->getTtl($response));
}
/**
* Fetch the Vary headers associated with a request, if they exist.
*
* Only responses, and not requests, contain Vary headers. However, we need
* to be able to determine what Vary headers were set for a given URL and
* request method on a future request.
*
* @param RequestInterface $request The Request to fetch headers for.
*
* @return array An array of headers.
*/
private function fetchVary(RequestInterface $request)
{
$key = $this->getVaryKey($request);
$varyHeaders = $this->cache->fetch($key);
return is_array($varyHeaders) ? $varyHeaders : [];
}
/**
* Delete the headers associated with a Vary request.
*
* @param RequestInterface $request The Request to delete headers for.
*/
private function deleteVary(RequestInterface $request)
{
$key = $this->getVaryKey($request);
$this->cache->delete($key);
}
/**
* Get the cache key for Vary headers.
*
* @param RequestInterface $request The Request to fetch the key for.
*
* @return string The generated key.
*/
private function getVaryKey(RequestInterface $request)
{
$key = $this->keyPrefix . md5('vary ' . $this->getCacheKey($request));
return $key;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
/**
* Interface used to cache HTTP responses.
*/
interface CacheStorageInterface
{
/**
* Get a Response from the cache for a request.
*
* @param RequestInterface $request
*
* @return null|ResponseInterface
*/
public function fetch(RequestInterface $request);
/**
* Cache an HTTP request.
*
* @param RequestInterface $request Request being cached
* @param ResponseInterface $response Response to cache
*/
public function cache(
RequestInterface $request,
ResponseInterface $response
);
/**
* Deletes cache entries that match a request.
*
* @param RequestInterface $request Request to delete from cache
*/
public function delete(RequestInterface $request);
/**
* Purge all cache entries for a given URL.
*
* @param string $url
*/
public function purge($url);
}

View File

@@ -0,0 +1,275 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use Doctrine\Common\Cache\ArrayCache;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Event\BeforeEvent;
use GuzzleHttp\Event\CompleteEvent;
use GuzzleHttp\Event\ErrorEvent;
use GuzzleHttp\Event\HasEmitterInterface;
use GuzzleHttp\Event\RequestEvents;
use GuzzleHttp\Event\SubscriberInterface;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
/**
* Plugin to enable the caching of GET and HEAD requests.
*
* Caching can be done on all requests passing through this plugin or only
* after retrieving resources with cacheable response headers.
*
* This is a simple implementation of RFC 2616 and should be considered a
* private transparent proxy cache, meaning authorization and private data can
* be cached.
*
* It also implements RFC 5861's `stale-if-error` Cache-Control extension,
* allowing stale cache responses to be used when an error is encountered
* (such as a `500 Internal Server Error` or DNS failure).
*/
class CacheSubscriber implements SubscriberInterface
{
/** @var CacheStorageInterface $cache Object used to cache responses */
private $storage;
/** @var callable Determines if a request is cacheable */
private $canCache;
/**
* @param CacheStorageInterface $cache Cache storage
* @param callable $canCache Callable used to determine if a
* request can be cached. Accepts a
* RequestInterface and returns a
* boolean value.
*/
public function __construct(
CacheStorageInterface $cache,
callable $canCache
) {
$this->storage = $cache;
$this->canCache = $canCache;
}
/**
* Helper method used to easily attach a cache to a request or client.
*
* This method accepts an array of options that are used to control the
* caching behavior:
*
* - storage: An optional GuzzleHttp\Subscriber\Cache\CacheStorageInterface.
* If no value is not provided, an in-memory array cache will be used.
* - validate: Boolean value that determines if cached response are ever
* validated against the origin server. Defaults to true but can be
* disabled by passing false.
* - purge: Boolean value that determines if cached responses are purged
* when non-idempotent requests are sent to their URI. Defaults to true
* but can be disabled by passing false.
* - can_cache: An optional callable used to determine if a request can be
* cached. The callable accepts a RequestInterface and returns a boolean
* value. If no value is provided, the default behavior is utilized.
*
* @param HasEmitterInterface $subject Client or request to attach to,
* @param array $options Options used to control the cache.
*
* @return array Returns an associative array containing a 'subscriber' key
* that holds the created CacheSubscriber, and a 'storage'
* key that contains the cache storage used by the subscriber.
*/
public static function attach(
HasEmitterInterface $subject,
array $options = []
) {
if (!isset($options['storage'])) {
$options['storage'] = new CacheStorage(new ArrayCache());
}
if (!isset($options['can_cache'])) {
$options['can_cache'] = [
'GuzzleHttp\Subscriber\Cache\Utils',
'canCacheRequest',
];
}
$emitter = $subject->getEmitter();
$cache = new self($options['storage'], $options['can_cache']);
$emitter->attach($cache);
if (!isset($options['validate']) || $options['validate'] === true) {
$emitter->attach(new ValidationSubscriber(
$options['storage'],
$options['can_cache'])
);
}
if (!isset($options['purge']) || $options['purge'] === true) {
$emitter->attach(new PurgeSubscriber($options['storage']));
}
return ['subscriber' => $cache, 'storage' => $options['storage']];
}
public function getEvents()
{
return [
'before' => ['onBefore', RequestEvents::LATE],
'complete' => ['onComplete', RequestEvents::EARLY],
'error' => ['onError', RequestEvents::EARLY]
];
}
/**
* Checks if a request can be cached, and if so, intercepts with a cached
* response is available.
*
* @param BeforeEvent $event
*/
public function onBefore(BeforeEvent $event)
{
$request = $event->getRequest();
if (!$this->canCacheRequest($request)) {
$this->cacheMiss($request);
return;
}
if (!($response = $this->storage->fetch($request))) {
$this->cacheMiss($request);
return;
}
$response->setHeader('Age', Utils::getResponseAge($response));
$valid = $this->validate($request, $response);
// Validate that the response satisfies the request
if ($valid) {
$request->getConfig()->set('cache_lookup', 'HIT');
$request->getConfig()->set('cache_hit', true);
$event->intercept($response);
} else {
$this->cacheMiss($request);
}
}
/**
* Checks if the request and response can be cached, and if so, store it.
*
* @param CompleteEvent $event
*/
public function onComplete(CompleteEvent $event)
{
$request = $event->getRequest();
$response = $event->getResponse();
// Cache the response if it can be cached and isn't already
if ($request->getConfig()->get('cache_lookup') === 'MISS'
&& call_user_func($this->canCache, $request)
&& Utils::canCacheResponse($response)
) {
// Store the date when the response was cached
$response->setHeader('X-Guzzle-Cache-Date', gmdate('D, d M Y H:i:s T', time()));
$this->storage->cache($request, $response);
}
$this->addResponseHeaders($request, $response);
}
/**
* If the request failed, then check if a cached response would suffice.
*
* @param ErrorEvent $event
*/
public function onError(ErrorEvent $event)
{
$request = $event->getRequest();
if (!call_user_func($this->canCache, $request)) {
return;
}
$response = $this->storage->fetch($request);
// Intercept the failed response if possible
if ($response && $this->validateFailed($request, $response)) {
$request->getConfig()->set('cache_hit', 'error');
$response->setHeader('Age', Utils::getResponseAge($response));
$event->intercept($response);
}
}
private function cacheMiss(RequestInterface $request)
{
$request->getConfig()->set('cache_lookup', 'MISS');
}
private function validate(
RequestInterface $request,
ResponseInterface $response
) {
// Validation is handled in another subscriber and can be optionally
// enabled/disabled.
if (Utils::getDirective($response, 'must-revalidate')) {
return true;
}
return Utils::isResponseValid($request, $response);
}
private function validateFailed(
RequestInterface $request,
ResponseInterface $response
) {
$req = Utils::getDirective($request, 'stale-if-error');
$res = Utils::getDirective($response, 'stale-if-error');
if (!$req && !$res) {
return false;
}
$responseAge = Utils::getResponseAge($response);
$maxAge = Utils::getMaxAge($response);
if (($req && $responseAge - $maxAge > $req) ||
($responseAge - $maxAge > $res)
) {
return false;
}
return true;
}
private function canCacheRequest(RequestInterface $request)
{
return !$request->getConfig()->get('cache.disable')
&& call_user_func($this->canCache, $request);
}
private function addResponseHeaders(
RequestInterface $request,
ResponseInterface $response
) {
$params = $request->getConfig();
$lookup = $params['cache_lookup'] . ' from GuzzleCache';
$response->addHeader('X-Cache-Lookup', $lookup);
if ($params['cache_hit'] === true) {
$response->addHeader('X-Cache', 'HIT from GuzzleCache');
} elseif ($params['cache_hit'] == 'error') {
$response->addHeader('X-Cache', 'HIT_ERROR from GuzzleCache');
} else {
$response->addHeader('X-Cache', 'MISS from GuzzleCache');
}
$freshness = Utils::getFreshness($response);
// Only add a Warning header if we are returning a stale response.
if ($params['cache_hit'] && $freshness !== null && $freshness <= 0) {
$response->addHeader(
'Warning',
sprintf(
'%d GuzzleCache/' . ClientInterface::VERSION . ' "%s"',
110,
'Response is stale'
)
);
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use GuzzleHttp\Event\BeforeEvent;
use GuzzleHttp\Event\RequestEvents;
use GuzzleHttp\Event\SubscriberInterface;
use GuzzleHttp\Message\Response;
/**
* Automatically purges a URL when a non-idempotent request is made to it.
*/
class PurgeSubscriber implements SubscriberInterface
{
/** @var CacheStorageInterface */
private $storage;
/** @var array */
private static $purgeMethods = [
'PUT' => true,
'POST' => true,
'DELETE' => true,
'PATCH' => true,
'PURGE' => true,
];
/**
* @param CacheStorageInterface $storage Storage to modify if purging
*/
public function __construct($storage)
{
$this->storage = $storage;
}
public function getEvents()
{
return ['before' => ['onBefore', RequestEvents::LATE]];
}
public function onBefore(BeforeEvent $event)
{
$request = $event->getRequest();
if (isset(self::$purgeMethods[$request->getMethod()])) {
$this->storage->purge($request->getUrl());
if ('PURGE' === $request->getMethod()) {
$event->intercept(new Response(204));
}
}
}
}

View File

@@ -0,0 +1,202 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use GuzzleHttp\Message\AbstractMessage;
use GuzzleHttp\Message\MessageInterface;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
/**
* Cache utility functions.
*/
class Utils
{
/**
* Get a cache control directive from a message.
*
* @param MessageInterface $message Message to retrieve
* @param string $part Cache directive to retrieve
*
* @return mixed|bool|null
*/
public static function getDirective(MessageInterface $message, $part)
{
$parts = AbstractMessage::parseHeader($message, 'Cache-Control');
foreach ($parts as $line) {
if (isset($line[$part])) {
return $line[$part];
} elseif (in_array($part, $line)) {
return true;
}
}
return null;
}
/**
* Gets the age of a response in seconds.
*
* @param ResponseInterface $response
*
* @return int
*/
public static function getResponseAge(ResponseInterface $response)
{
if ($response->hasHeader('Age')) {
return (int) $response->getHeader('Age');
}
$date = strtotime($response->getHeader('Date') ?: 'now');
return time() - $date;
}
/**
* Gets the number of seconds from the current time in which a response
* is still considered fresh.
*
* @param ResponseInterface $response
*
* @return int|null Returns the number of seconds
*/
public static function getMaxAge(ResponseInterface $response)
{
$smaxage = Utils::getDirective($response, 's-maxage');
if (is_numeric($smaxage)) {
return (int) $smaxage;
}
$maxage = Utils::getDirective($response, 'max-age');
if (is_numeric($maxage)) {
return (int) $maxage;
}
if ($response->hasHeader('Expires')) {
return strtotime($response->getHeader('Expires')) - time();
}
return null;
}
/**
* Get the freshness of a response by returning the difference of the
* maximum lifetime of the response and the age of the response.
*
* Freshness values less than 0 mean that the response is no longer fresh
* and is ABS(freshness) seconds expired. Freshness values of greater than
* zero is the number of seconds until the response is no longer fresh.
* A NULL result means that no freshness information is available.
*
* @param ResponseInterface $response Response to get freshness of
*
* @return int|null
*/
public static function getFreshness(ResponseInterface $response)
{
$maxAge = self::getMaxAge($response);
$age = self::getResponseAge($response);
return is_int($maxAge) && is_int($age) ? ($maxAge - $age) : null;
}
/**
* Default function used to determine if a request can be cached.
*
* @param RequestInterface $request Request to check
*
* @return bool
*/
public static function canCacheRequest(RequestInterface $request)
{
$method = $request->getMethod();
// Only GET and HEAD requests can be cached
if ($method !== 'GET' && $method !== 'HEAD') {
return false;
}
// Don't fool with Range requests for now
if ($request->hasHeader('Range')) {
return false;
}
return self::getDirective($request, 'no-store') === null;
}
/**
* Determines if a response can be cached.
*
* @param ResponseInterface $response Response to check
*
* @return bool
*/
public static function canCacheResponse(ResponseInterface $response)
{
static $cacheCodes = [200, 203, 300, 301, 410];
// Check if the response is cacheable based on the code
if (!in_array((int) $response->getStatusCode(), $cacheCodes)) {
return false;
}
// Make sure a valid body was returned and can be cached
$body = $response->getBody();
if ($body && (!$body->isReadable() || !$body->isSeekable())) {
return false;
}
// Never cache no-store resources (this is a private cache, so private
// can be cached)
if (self::getDirective($response, 'no-store')) {
return false;
}
// Don't fool with Content-Range requests for now
if ($response->hasHeader('Content-Range')) {
return false;
}
$freshness = self::getFreshness($response);
return $freshness === null // No freshness info.
|| $freshness >= 0 // It's fresh
|| $response->hasHeader('ETag') // Can validate
|| $response->hasHeader('Last-Modified'); // Can validate
}
public static function isResponseValid(
RequestInterface $request,
ResponseInterface $response
) {
$responseAge = Utils::getResponseAge($response);
$maxAge = Utils::getDirective($response, 'max-age');
// Increment the age based on the X-Guzzle-Cache-Date
if ($cacheDate = $response->getHeader('X-Guzzle-Cache-Date')) {
$responseAge += (time() - strtotime($cacheDate));
$response->setHeader('Age', $responseAge);
}
// Check the request's max-age header against the age of the response
if ($maxAge !== null && $responseAge > $maxAge) {
return false;
}
// Check the response's max-age header against the freshness level
$freshness = Utils::getFreshness($response);
if ($freshness !== null) {
$maxStale = Utils::getDirective($request, 'max-stale');
if ($maxStale !== null) {
if ($freshness < (-1 * $maxStale)) {
return false;
}
} elseif ($maxAge !== null && $responseAge > $maxAge) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,207 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use GuzzleHttp\Event\CompleteEvent;
use GuzzleHttp\Event\RequestEvents;
use GuzzleHttp\Event\SubscriberInterface;
use GuzzleHttp\Exception\BadResponseException;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
/**
* Validates cached responses as needed.
*
* @Link http://tools.ietf.org/html/rfc7234#section-4.3
*/
class ValidationSubscriber implements SubscriberInterface
{
/** @var CacheStorageInterface Cache object storing cache data */
private $storage;
/** @var callable */
private $canCache;
/** @var array */
private static $gone = [404 => true, 410 => true];
/** @var array */
private static $replaceHeaders = [
'Date',
'Expires',
'Cache-Control',
'ETag',
'Last-Modified',
];
/**
* @param CacheStorageInterface $cache Cache storage
* @param callable $canCache Callable used to determine if a
* request can be cached. Accepts a
* RequestInterface and returns a
* boolean value.
*/
public function __construct(
CacheStorageInterface $cache,
callable $canCache
) {
$this->storage = $cache;
$this->canCache = $canCache;
}
public function getEvents()
{
return ['complete' => ['onComplete', RequestEvents::EARLY]];
}
public function onComplete(CompleteEvent $e)
{
$lookup = $e->getRequest()->getConfig()->get('cache_lookup');
if ($lookup == 'HIT' &&
$this->shouldvalidate($e->getRequest(), $e->getResponse())
) {
$this->validate($e->getRequest(), $e->getResponse(), $e);
}
}
private function validate(
RequestInterface $request,
ResponseInterface $response,
CompleteEvent $event
) {
try {
$validate = $this->createRevalidationRequest($request, $response);
$validated = $event->getClient()->send($validate);
} catch (BadResponseException $e) {
$this->handleBadResponse($e);
}
if ($validated->getStatusCode() == 200) {
$this->handle200Response($request, $validated, $event);
} elseif ($validated->getStatusCode() == 304) {
$this->handle304Response($request, $response, $validated, $event);
}
}
private function shouldValidate(
RequestInterface $request,
ResponseInterface $response
) {
if ($request->getMethod() != 'GET'
|| $request->getConfig()->get('cache.disable')
) {
return false;
}
$validate = Utils::getDirective($request, 'Pragma') === 'no-cache'
|| Utils::getDirective($response, 'Pragma') === 'no-cache'
|| Utils::getDirective($request, 'must-revalidate')
|| Utils::getDirective($response, 'must-revalidate')
|| Utils::getDirective($request, 'no-cache')
|| Utils::getDirective($response, 'no-cache')
|| Utils::getDirective($response, 'max-age') === '0'
|| Utils::getDirective($response, 's-maxage') === '0';
// Use the strong ETag validator if available and the response contains
// no Cache-Control directive
if (!$validate
&& !$response->hasHeader('Cache-Control')
&& $response->hasHeader('ETag')
) {
$validate = true;
}
return $validate;
}
/**
* Handles a bad response when attempting to validate.
*
* If the resource no longer exists, then remove from the cache.
*
* @param BadResponseException $e Exception encountered
*
* @throws BadResponseException
*/
private function handleBadResponse(BadResponseException $e)
{
if (isset(self::$gone[$e->getResponse()->getStatusCode()])) {
$this->storage->delete($e->getRequest());
}
throw $e;
}
/**
* Creates a request to use for revalidation.
*
* @param RequestInterface $request Request
* @param ResponseInterface $response Response to validate
*
* @return RequestInterface returns a revalidation request
*/
private function createRevalidationRequest(
RequestInterface $request,
ResponseInterface $response
) {
$validate = clone $request;
$validate->getConfig()->set('cache.disable', true);
$validate->removeHeader('Pragma');
$validate->removeHeader('Cache-Control');
$responseDate = $response->getHeader('Last-Modified')
?: $response->getHeader('Date');
$validate->setHeader('If-Modified-Since', $responseDate);
if ($etag = $response->getHeader('ETag')) {
$validate->setHeader('If-None-Match', $etag);
}
return $validate;
}
private function handle200Response(
RequestInterface $request,
ResponseInterface $validateResponse,
CompleteEvent $event
) {
// Store the 200 response in the cache if possible
if (Utils::canCacheResponse($validateResponse)) {
$this->storage->cache($request, $validateResponse);
}
$event->intercept($validateResponse);
}
private function handle304Response(
RequestInterface $request,
ResponseInterface $response,
ResponseInterface $validated,
CompleteEvent $event
) {
// Make sure that this response has the same ETag
if ($validated->getHeader('ETag') !== $response->getHeader('ETag')) {
// Revalidation failed, so remove from cache and retry.
$this->storage->delete($request);
$event->intercept($event->getClient()->send($request));
return;
}
// Replace cached headers with any of these headers from the
// origin server that might be more up to date
$modified = false;
foreach (self::$replaceHeaders as $name) {
if ($validated->hasHeader($name)
&& $validated->getHeader($name) != $response->getHeader($name)
) {
$modified = true;
$response->setHeader($name, $validated->getHeader($name));
}
}
// Store the updated response in cache
if ($modified) {
$this->storage->cache($request, $response);
}
}
}

View File

@@ -0,0 +1,138 @@
<?php
namespace GuzzleHttp\Tests\Subscriber\Cache;
use Doctrine\Common\Cache\ArrayCache;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Subscriber\Cache\CacheStorage;
/**
* Test the CacheStorage class.
*
* @class CacheStorageTest
*/
class CacheStorageTest extends \PHPUnit_Framework_TestCase
{
/**
* Test that a Response's max-age returns the correct TTL.
*/
public function testGetTtlMaxAge()
{
$response = new Response(200, [
'Cache-control' => 'max-age=10',
]);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache());
$ttl = $getTtl->invokeArgs($cache, [$response]);
$this->assertEquals(10, $ttl);
}
/**
* Test that the default TTL for cachable responses with no max-age headers
* is zero.
*/
public function testGetTtlDefault()
{
$response = new Response(200);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache());
$ttl = $getTtl->invokeArgs($cache, [$response]);
// assertSame() here to be specific about null / false returns.
$this->assertSame(0, $ttl);
}
/**
* Test setting the default TTL.
*/
public function testSetTtlDefault()
{
$response = new Response(200);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache(), null, 10);
$ttl = $getTtl->invokeArgs($cache, [$response]);
$this->assertEquals(10, $ttl);
}
/**
* Test that stale-if-error is added to the max-age header.
*/
public function testGetTtlMaxAgeStaleIfError()
{
$response = new Response(200, [
'Cache-control' => 'max-age=10, stale-if-error=10',
]);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache());
$ttl = $getTtl->invokeArgs($cache, [$response]);
$this->assertEquals(20, $ttl);
}
/**
* Test that stale-if-error works without a max-age header.
*/
public function testGetTtlStaleIfErrorAlone()
{
$response = new Response(200, [
'Cache-control' => 'stale-if-error=10',
]);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache());
$ttl = $getTtl->invokeArgs($cache, [$response]);
$this->assertEquals(10, $ttl);
}
/**
* Test that expires is considered when cache-control is not available.
*/
public function testGetTtlExpires()
{
$expires = new \DateTime('+100 seconds');
$response = new Response(200, [
'Expires' => $expires->format(DATE_RFC1123),
]);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache());
$ttl = $getTtl->invokeArgs($cache, [$response]);
$this->assertEquals(100, $ttl);
}
/**
* Test that cache-control is considered before expires.
*/
public function testGetTtlCacheControlExpires()
{
$expires = new \DateTime('+100 seconds');
$response = new Response(200, [
'Expires' => $expires->format(DATE_RFC1123),
'Cache-control' => 'max-age=10',
]);
$getTtl = $this->getMethod('getTtl');
$cache = new CacheStorage(new ArrayCache());
$ttl = $getTtl->invokeArgs($cache, [$response]);
$this->assertEquals(10, $ttl);
}
/**
* Return a protected or private method.
*
* @param string $name The name of the method.
*
* @return \ReflectionMethod A method object.
*/
protected static function getMethod($name)
{
$class = new \ReflectionClass('GuzzleHttp\Subscriber\Cache\CacheStorage');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace GuzzleHttp\Tests\Subscriber\Cache;
use GuzzleHttp\Client;
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
class CacheSubscriberTest extends \PHPUnit_Framework_TestCase
{
public function testCreatesAndAttachedDefaultSubscriber()
{
$client = new Client();
$cache = CacheSubscriber::attach($client);
$this->assertArrayHasKey('subscriber', $cache);
$this->assertArrayHasKey('storage', $cache);
$this->assertInstanceOf(
'GuzzleHttp\Subscriber\Cache\CacheStorage',
$cache['storage']
);
$this->assertInstanceOf(
'GuzzleHttp\Subscriber\Cache\CacheSubscriber',
$cache['subscriber']
);
$this->assertTrue($client->getEmitter()->hasListeners('error'));
}
}

View File

@@ -0,0 +1,768 @@
<?php
namespace GuzzleHttp\Tests\Subscriber\Cache;
require_once __DIR__ . '/../vendor/guzzlehttp/ringphp/tests/Client/Server.php';
require_once __DIR__ . '/../vendor/guzzlehttp/guzzle/tests/Server.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\Response;
use GuzzleHttp\Stream\Stream;
use GuzzleHttp\Subscriber\Cache\CacheSubscriber;
use GuzzleHttp\Subscriber\History;
use GuzzleHttp\Tests\Server;
class IntegrationTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
Server::start();
}
protected function tearDown()
{
Server::stop();
}
public function testCachesResponses()
{
Server::enqueue([
new Response(200, [
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
'Age' => '1277'
]),
new Response(304, [
'Content-Type' => 'text/html; charset=UTF-8',
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
'Date' => 'Wed, 29 Oct 2014 20:52:16 GMT',
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
'Age' => '1278'
]),
new Response(200, [
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
'Cache-Control' => 'private, s-maxage=0, max-age=0',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
'Age' => '1277'
]),
new Response(200, [
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
'Date' => 'Wed, 29 Oct 2014 20:53:15 GMT',
'Cache-Control' => 'private, s-maxage=0, max-age=0',
'Last-Modified' => 'Wed, 29 Oct 2014 20:53:00 GMT',
'Age' => '1277'
]),
]);
$history = new History();
$client = $this->setupClient($history);
$response1 = $client->get('/foo');
$this->assertEquals(200, $response1->getStatusCode());
$response2 = $client->get('/foo');
$this->assertEquals(200, $response2->getStatusCode());
$last = $history->getLastResponse();
$this->assertEquals('HIT from GuzzleCache', $last->getHeader('X-Cache-Lookup'));
$this->assertEquals('HIT from GuzzleCache', $last->getHeader('X-Cache'));
// Validate that expired requests without must-revalidate expire.
$response3 = $client->get('/foo');
$this->assertEquals(200, $response3->getStatusCode());
$response4 = $client->get('/foo');
$this->assertEquals(200, $response4->getStatusCode());
$last = $history->getLastResponse();
$this->assertEquals('MISS from GuzzleCache', $last->getHeader('X-Cache-Lookup'));
$this->assertEquals('MISS from GuzzleCache', $last->getHeader('X-Cache'));
// Validate that all of our requests were received.
$this->assertCount(4, Server::received());
}
/**
* Test that Warning headers aren't added to cache misses.
*/
public function testCacheMissNoWarning()
{
Server::enqueue([
new Response(200, [
'Vary' => 'Accept-Encoding,Cookie,X-Use-HHVM',
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
'Age' => '1277',
]),
]);
$client = $this->setupClient();
$response = $client->get('/foo');
$this->assertFalse($response->hasHeader('warning'));
}
/**
* Test that the Vary header creates unique cache entries.
*
* @throws \Exception
*/
public function testVaryUniqueResponses()
{
$now = $this->date();
Server::enqueue(
[
new Response(
200, [
'Vary' => 'Accept',
'Content-type' => 'text/html',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
'Last-Modified' => $now,
], Stream::factory('It works!')
),
new Response(
200, [
'Vary' => 'Accept',
'Content-type' => 'application/json',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
'Last-Modified' => $now,
], Stream::factory(json_encode(['body' => 'It works!']))
),
]
);
$client = $this->setupClient();
$response1 = $client->get(
'/foo',
['headers' => ['Accept' => 'text/html']]
);
$this->assertEquals('It works!', $this->getResponseBody($response1));
$response2 = $client->get(
'/foo',
['headers' => ['Accept' => 'application/json']]
);
$this->assertEquals(
'MISS from GuzzleCache',
$response2->getHeader('x-cache')
);
$decoded = json_decode($this->getResponseBody($response2));
if (!isset($decoded) || !isset($decoded->body)) {
$this->fail('JSON response could not be decoded.');
} else {
$this->assertEquals('It works!', $decoded->body);
}
}
public function testCachesResponsesForWithoutVaryHeader()
{
$now = $this->date();
Server::enqueue(
[
new Response(
200, [
'Content-type' => 'text/html',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000, must-revalidate',
'Last-Modified' => $now,
], Stream::factory()
),
new Response(
200, [
'Content-type' => 'text/html',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000, must-revalidate',
'Last-Modified' => $now,
], Stream::factory()
),
]
);
$client = $this->setupClient();
$response1 = $client->get('/foo');
$this->assertEquals(200, $response1->getStatusCode());
$response2 = $client->get('/foo');
$this->assertEquals(200, $response2->getStatusCode());
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache'));
}
/**
* Test that requests varying on both Accept and User-Agent properly split
* different User-Agents into different cache items.
*/
public function testVaryUserAgent()
{
$this->setupMultipleVaryResponses();
$client = $this->setupClient();
$response1 = $client->get(
'/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/1.0',
]
]
);
$this->assertEquals(
'Test/1.0 request.',
$this->getResponseBody($response1)
);
$response2 = $client->get(
'/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/2.0',
]
]
);
$this->assertEquals(
'MISS from GuzzleCache',
$response2->getHeader('x-cache')
);
$this->assertEquals(
'Test/2.0 request.',
$this->getResponseBody($response2)
);
// Test that we get cache hits where both Vary headers match.
$response5 = $client->get(
'/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/2.0',
]
]
);
$this->assertEquals(
'HIT from GuzzleCache',
$response5->getHeader('x-cache')
);
$this->assertEquals(
'Test/2.0 request.',
$this->getResponseBody($response5)
);
}
/**
* Test that requests varying on Accept but not User-Agent return different responses.
*/
public function testVaryAccept()
{
$this->setupMultipleVaryResponses();
$client = $this->setupClient();
// Prime the cache.
$client->get(
'/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/1.0',
]
]
);
$client->get(
'/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/2.0',
]
]
);
$response1 = $client->get(
'/foo',
[
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'Testing/1.0',
]
]
);
$this->assertEquals(
'MISS from GuzzleCache',
$response1->getHeader('x-cache')
);
$this->assertEquals(
'Test/1.0 request.',
json_decode($this->getResponseBody($response1))->body
);
$response2 = $client->get(
'/foo',
[
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'Testing/2.0',
]
]
);
$this->assertEquals(
'MISS from GuzzleCache',
$response2->getHeader('x-cache')
);
$this->assertEquals(
'Test/2.0 request.',
json_decode($this->getResponseBody($response2))->body
);
}
/**
* Test that we return cached responses when multiple Vary headers match.
*/
public function testMultipleVaryMatch()
{
$this->setupMultipleVaryResponses();
$client = $this->setupClient();
// Prime the cache.
$client->get('/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/1.0',
]
]
);
$client->get('/foo',
[
'headers' => [
'Accept' => 'text/html',
'User-Agent' => 'Testing/2.0',
]
]
);
$client->get('/foo',
[
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'Testing/1.0',
]
]
);
$client->get('/foo',
[
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'Testing/2.0',
]
]
);
$response = $client->get(
'/foo',
[
'headers' => [
'Accept' => 'application/json',
'User-Agent' => 'Testing/2.0',
]
]
);
$this->assertEquals(
'HIT from GuzzleCache',
$response->getHeader('x-cache')
);
$this->assertEquals(
'Test/2.0 request.',
json_decode($this->getResponseBody($response))->body
);
}
/**
* Test that stale responses are used on errors if allowed.
*/
public function testOnErrorStaleResponse()
{
$now = $this->date();
Server::enqueue([
new Response(200, [
'Date' => $now,
'Cache-Control' => 'private, max-age=0, must-revalidate, stale-if-error=666',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
], Stream::factory('It works!')),
new Response(503, [
'Date' => $now,
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
'Age' => '1277'
]),
]);
$client = $this->setupClient();
// Prime the cache.
$response1 = $client->get('/foo');
$this->assertEquals(200, $response1->getStatusCode());
// This should return the first request.
$response2 = $client->get('/foo');
$this->assertEquals(200, $response2->getStatusCode());
$this->assertEquals('It works!', $this->getResponseBody($response2));
$this->assertEquals('HIT_ERROR from GuzzleCache', $response2->getHeader('x-cache'));
$this->assertCount(2, Server::received());
}
/**
* Test that expired stale responses aren't returned.
*/
public function testOnErrorStaleResponseExpired()
{
// These dates are in the past, so the responses will be expired.
Server::enqueue([
new Response(200, [
'Date' => 'Wed, 29 Oct 2014 20:52:15 GMT',
'Cache-Control' => 'private, max-age=0, must-revalidate, stale-if-error=10',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
]),
new Response(503, [
'Date' => 'Wed, 29 Oct 2014 20:55:15 GMT',
'Cache-Control' => 'private, s-maxage=0, max-age=0, must-revalidate',
'Last-Modified' => 'Wed, 29 Oct 2014 20:30:57 GMT',
]),
]);
$client = $this->setupClient();
// Prime the cache.
$response1 = $client->get('/foo');
$this->assertEquals(200, $response1->getStatusCode());
$this->assertEquals('Wed, 29 Oct 2014 20:52:15 GMT', $response1->getHeader('Date'));
try {
$client->get('/foo');
$this->fail('503 was not thrown with an expired cache entry.');
} catch (ServerException $e) {
$this->assertEquals(503, $e->getCode());
$this->assertEquals('Wed, 29 Oct 2014 20:55:15 GMT', $e->getResponse()->getHeader('Date'));
$this->assertCount(2, Server::received());
}
}
/**
* Test that the can_cache option can modify cache behaviour.
*/
public function testCanCache()
{
$now = $this->date();
// Return an uncacheable response, that is then cached by can_cache
// returning TRUE.
Server::enqueue(
[
new Response(
200, [
'Date' => $now,
'Cache-Control' => 'private, max-age=0, no-cache',
'Last-Modified' => $now,
], Stream::factory('It works!')),
new Response(
304, [
'Date' => $now,
'Cache-Control' => 'private, max-age=0, no-cache',
'Last-Modified' => $now,
'Age' => 0,
]),
]
);
$client = new Client(['base_url' => Server::$url]);
CacheSubscriber::attach(
$client,
[
'can_cache' => function (RequestInterface $request) {
return true;
}
]
);
$response1 = $client->get('/foo');
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
$response2 = $client->get('/foo');
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
$this->assertEquals('It works!', $this->getResponseBody($response2));
}
/**
* Test that PURGE can delete cached responses.
*/
public function testCanPurge()
{
$now = $this->date();
// Return a cached response that is then purged, and requested again
Server::enqueue(
[
new Response(
200, [
'Date' => $now,
'Cache-Control' => 'public, max-age=60',
'Last-Modified' => $now,
], Stream::factory('It is foo!')),
new Response(
200, [
'Date' => $now,
'Cache-Control' => 'public, max-age=60',
'Last-Modified' => $now,
], Stream::factory('It is bar!')),
]
);
$client = $this->setupClient();
$response1 = $client->get('/foo');
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
$this->assertEquals('It is foo!', $this->getResponseBody($response1));
$response2 = $client->get('/foo');
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
$this->assertEquals('It is foo!', $this->getResponseBody($response2));
$response3 = $client->send($client->createRequest('PURGE', '/foo'));
$this->assertEquals(204, $response3->getStatusCode());
$response4 = $client->get('/foo');
$this->assertEquals('MISS from GuzzleCache', $response4->getHeader('X-Cache-Lookup'));
$this->assertEquals('It is bar!', $this->getResponseBody($response4));
}
/**
* Test that cache entries are deleted when a response 404s.
*/
public function test404CacheDelete()
{
$this->fourXXCacheDelete(404);
}
/**
* Test that cache entries are deleted when a response 410s.
*/
public function test410CacheDelete()
{
$this->fourXXCacheDelete(410);
}
/**
* Test the resident_time calculation (RFC7234 4.2.3)
*/
public function testAgeIsIncremented()
{
Server::enqueue([
new Response(200, [
'Date' => $this->date(),
'Cache-Control' => 'public, max-age=60',
'Age' => '59'
], Stream::factory('Age is 59!')),
new Response(200, [
'Date' => $this->date(),
'Cache-Control' => 'public, max-age=60',
'Age' => '0'
], Stream::factory('It works!')),
]);
$client = $this->setupClient();
// First request : the response is cached
$response1 = $client->get('/foo');
$this->assertEquals(200, $response1->getStatusCode());
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
$this->assertEquals('Age is 59!', $this->getResponseBody($response1));
// Second request : cache hit, age is now 60
sleep(1);
$response2 = $client->get('/foo');
$this->assertEquals(200, $response1->getStatusCode());
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
// This request should not be valid anymore : age is 59 + 2 = 61 which is strictly greater than 60
sleep(1);
$response3 = $client->get('/foo');
$this->assertEquals(200, $response3->getStatusCode());
$this->assertEquals('MISS from GuzzleCache', $response3->getHeader('X-Cache-Lookup'));
$this->assertEquals('It works!', $this->getResponseBody($response3));
$this->assertCount(2, Server::received());
}
/**
* Decode a response body from TestServer.
*
* TestServer encodes all responses with base64, so we need to decode them
* before we can do any assert's on them.
*
* @param Response $response The response with a body to decode.
*
* @return string
*/
private function getResponseBody($response)
{
return base64_decode($response->getBody());
}
/**
* Set up responses used by our Vary tests.
*
* @throws \Exception
*/
private function setupMultipleVaryResponses()
{
$now = $this->date();
Server::enqueue(
[
new Response(
200, [
'Vary' => 'Accept, User-Agent',
'Content-type' => 'text/html',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
'Last-Modified' => $now,
], Stream::factory('Test/1.0 request.')
),
new Response(
200,
[
'Vary' => 'Accept, User-Agent',
'Content-type' => 'text/html',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
'Last-Modified' => $now,
],
Stream::factory('Test/2.0 request.')
),
new Response(
200, [
'Vary' => 'Accept, User-Agent',
'Content-type' => 'application/json',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
'Last-Modified' => $now,
], Stream::factory(json_encode(['body' => 'Test/1.0 request.']))
),
new Response(
200, [
'Vary' => 'Accept, User-Agent',
'Content-type' => 'application/json',
'Date' => $now,
'Cache-Control' => 'public, s-maxage=1000, max-age=1000',
'Last-Modified' => $now,
], Stream::factory(json_encode(['body' => 'Test/2.0 request.']))
),
]
);
}
/**
* Setup a Guzzle client for testing.
*
* @param History $history (optional) parameter of a History to track
* requests in.
*
* @return Client A client ready to run test requests against.
*/
private function setupClient(History $history = null)
{
$client = new Client(['base_url' => Server::$url]);
CacheSubscriber::attach($client);
if ($history) {
$client->getEmitter()->attach($history);
}
return $client;
}
/**
* Return a date string suitable for using in an HTTP header.
*
* @param int $timestamp (optional) A Unix timestamp to generate the date.
*
* @return string The generated date string.
*/
private function date($timestamp = null)
{
if (!$timestamp) {
$timestamp = time();
}
return gmdate("D, d M Y H:i:s", $timestamp) . ' GMT';
}
/**
* Helper to test that a 400 response deletes cache entries.
*
* @param int $errorCode The error code to test, such as 404 or 410.
*
* @throws \Exception
*/
private function fourXXCacheDelete($errorCode)
{
$now = $this->date();
Server::enqueue(
[
new Response(
200, [
'Date' => $now,
'Cache-Control' => 'public, max-age=1000, must-revalidate',
'Last-Modified' => $now,
]
),
new Response(
304, [
'Date' => $now,
'Cache-Control' => 'public, max-age=1000, must-revalidate',
'Last-Modified' => $now,
'Age' => 0,
]
),
new Response(
$errorCode, [
'Date' => $now,
'Cache-Control' => 'public, max-age=1000, must-revalidate',
'Last-Modified' => $now,
]
),
new Response(
200, [
'Date' => $now,
'Cache-Control' => 'public, max-age=1000, must-revalidate',
'Last-Modified' => $now,
]
),
]
);
$client = $this->setupClient();
$response1 = $client->get('/foo');
$this->assertEquals('MISS from GuzzleCache', $response1->getHeader('X-Cache-Lookup'));
$response2 = $client->get('/foo');
$this->assertEquals('HIT from GuzzleCache', $response2->getHeader('X-Cache-Lookup'));
try {
$client->get('/foo');
$this->fail($errorCode . ' was not thrown.');
} catch (RequestException $e) {
$response3 = $e->getResponse();
$this->assertEquals($errorCode, $response3->getStatusCode());
$this->assertEquals('MISS from GuzzleCache', $response3->getHeader('X-Cache-Lookup'));
}
$response4 = $client->get('/foo');
$this->assertEquals('MISS from GuzzleCache', $response4->getHeader('X-Cache-Lookup'));
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace GuzzleHttp\Subscriber\Cache;
use GuzzleHttp\Message\MessageFactory;
use PHPUnit_Framework_TestCase;
/**
* Test the Utils class.
*
* @class UtilsTest
*/
class UtilsTest extends PHPUnit_Framework_TestCase
{
/**
* Test that a max-age of zero isn't returned as null.
*/
public function testGetMaxAgeZero()
{
$messageFactory = new MessageFactory();
$response = $messageFactory->createResponse(200, ['Cache-Control' => 's-maxage=0']);
$this->assertSame(0, Utils::getMaxAge($response));
$response = $messageFactory->createResponse(200, ['Cache-Control' => 'max-age=0']);
$this->assertSame(0, Utils::getMaxAge($response));
$response = $messageFactory->createResponse(200, ['Expires' => gmdate('D, d M Y H:i:s') . ' GMT']);
$this->assertLessThanOrEqual(0, Utils::getMaxAge($response));
}
/**
* Test that a response with no max-age information returns null.
*/
public function testGetMaxAgeNull()
{
$messageFactory = new MessageFactory();
$response = $messageFactory->createResponse(200);
$this->assertSame(null, Utils::getMaxAge($response));
}
/**
* Test that a response that is zero fresh returns zero and not null.
*/
public function testGetFreshnessZero()
{
$messageFactory = new MessageFactory();
$response = $messageFactory->createResponse(200,
[
'Cache-Control' => 'max-age=0',
'Age' => 0,
]);
$this->assertSame(0, Utils::getFreshness($response));
}
/**
* Test that responses that can't have freshness determined return null.
*/
public function testGetFreshnessNull()
{
$messageFactory = new MessageFactory();
$response = $messageFactory->createResponse(200);
$this->assertSame(null, Utils::getFreshness($response));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
Copyright (c) 2011-2015 Michael Dowling, https://github.com/mtdowling <mtdowling@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,70 @@
Guzzle, PHP HTTP client and webservice framework
================================================
[![Build Status](https://secure.travis-ci.org/guzzle/guzzle.svg?branch=master)](http://travis-ci.org/guzzle/guzzle)
Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and
trivial to integrate with web services.
- Manages things like persistent connections, represents query strings as
collections, simplifies sending streaming POST requests with fields and
files, and abstracts away the underlying HTTP transport layer.
- Can send both synchronous and asynchronous requests using the same interface
without requiring a dependency on a specific event loop.
- Pluggable HTTP adapters allows Guzzle to integrate with any method you choose
for sending HTTP requests over the wire (e.g., cURL, sockets, PHP's stream
wrapper, non-blocking event loops like ReactPHP.
- Guzzle makes it so that you no longer need to fool around with cURL options,
stream contexts, or sockets.
```php
$client = new GuzzleHttp\Client();
$response = $client->get('http://guzzlephp.org');
$res = $client->get('https://api.github.com/user', ['auth' => ['user', 'pass']]);
echo $res->getStatusCode();
// "200"
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
var_export($res->json());
// Outputs the JSON decoded data
// Send an asynchronous request.
$req = $client->createRequest('GET', 'http://httpbin.org', ['future' => true]);
$client->send($req)->then(function ($response) {
echo 'I completed! ' . $response;
});
```
Get more information and answers with the
[Documentation](http://guzzlephp.org/),
[Forums](https://groups.google.com/forum/?hl=en#!forum/guzzle),
and [Gitter](https://gitter.im/guzzle/guzzle).
### Installing via Composer
The recommended way to install Guzzle is through
[Composer](http://getcomposer.org).
```bash
# Install Composer
curl -sS https://getcomposer.org/installer | php
```
Next, run the Composer command to install the latest stable version of Guzzle:
```bash
composer.phar require guzzlehttp/guzzle
```
After installing, you need to require Composer's autoloader:
```php
require 'vendor/autoload.php';
```
### Documentation
More information can be found in the online documentation at
http://guzzlephp.org/.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
{
"name": "guzzlehttp/guzzle",
"type": "library",
"description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
"keywords": ["framework", "http", "rest", "web service", "curl", "client", "HTTP client"],
"homepage": "http://guzzlephp.org/",
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.4.0",
"guzzlehttp/ringphp": "^1.1",
"react/promise": "^2.2"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "^4.0"
},
"autoload": {
"psr-4": {
"GuzzleHttp\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"GuzzleHttp\\Tests\\": "tests/"
}
},
"scripts": {
"test": "make test",
"test-ci": "make coverage"
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace GuzzleHttp;
/**
* Represents the result of a batch operation. This result container is
* iterable, countable, and you can can get a result by value using the
* getResult function.
*
* Successful results are anything other than exceptions. Failure results are
* exceptions.
*
* @package GuzzleHttp
*/
class BatchResults implements \Countable, \IteratorAggregate, \ArrayAccess
{
private $hash;
/**
* @param \SplObjectStorage $hash Hash of key objects to result values.
*/
public function __construct(\SplObjectStorage $hash)
{
$this->hash = $hash;
}
/**
* Get the keys that are available on the batch result.
*
* @return array
*/
public function getKeys()
{
return iterator_to_array($this->hash);
}
/**
* Gets a result from the container for the given object. When getting
* results for a batch of requests, provide the request object.
*
* @param object $forObject Object to retrieve the result for.
*
* @return mixed|null
*/
public function getResult($forObject)
{
return isset($this->hash[$forObject]) ? $this->hash[$forObject] : null;
}
/**
* Get an array of successful results.
*
* @return array
*/
public function getSuccessful()
{
$results = [];
foreach ($this->hash as $key) {
if (!($this->hash[$key] instanceof \Exception)) {
$results[] = $this->hash[$key];
}
}
return $results;
}
/**
* Get an array of failed results.
*
* @return array
*/
public function getFailures()
{
$results = [];
foreach ($this->hash as $key) {
if ($this->hash[$key] instanceof \Exception) {
$results[] = $this->hash[$key];
}
}
return $results;
}
/**
* Allows iteration over all batch result values.
*
* @return \ArrayIterator
*/
public function getIterator()
{
$results = [];
foreach ($this->hash as $key) {
$results[] = $this->hash[$key];
}
return new \ArrayIterator($results);
}
/**
* Counts the number of elements in the batch result.
*
* @return int
*/
public function count()
{
return count($this->hash);
}
/**
* Checks if the batch contains a specific numerical array index.
*
* @param int $key Index to access
*
* @return bool
*/
public function offsetExists($key)
{
return $key < count($this->hash);
}
/**
* Allows access of the batch using a numerical array index.
*
* @param int $key Index to access.
*
* @return mixed|null
*/
public function offsetGet($key)
{
$i = -1;
foreach ($this->hash as $obj) {
if ($key === ++$i) {
return $this->hash[$obj];
}
}
return null;
}
public function offsetUnset($key)
{
throw new \RuntimeException('Not implemented');
}
public function offsetSet($key, $value)
{
throw new \RuntimeException('Not implemented');
}
}

View File

@@ -0,0 +1,362 @@
<?php
namespace GuzzleHttp;
use GuzzleHttp\Event\HasEmitterTrait;
use GuzzleHttp\Message\MessageFactory;
use GuzzleHttp\Message\MessageFactoryInterface;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\FutureResponse;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Future\FutureInterface;
use GuzzleHttp\Exception\RequestException;
use React\Promise\FulfilledPromise;
use React\Promise\RejectedPromise;
/**
* HTTP client
*/
class Client implements ClientInterface
{
use HasEmitterTrait;
/** @var MessageFactoryInterface Request factory used by the client */
private $messageFactory;
/** @var Url Base URL of the client */
private $baseUrl;
/** @var array Default request options */
private $defaults;
/** @var callable Request state machine */
private $fsm;
/**
* Clients accept an array of constructor parameters.
*
* Here's an example of creating a client using an URI template for the
* client's base_url and an array of default request options to apply
* to each request:
*
* $client = new Client([
* 'base_url' => [
* 'http://www.foo.com/{version}/',
* ['version' => '123']
* ],
* 'defaults' => [
* 'timeout' => 10,
* 'allow_redirects' => false,
* 'proxy' => '192.168.16.1:10'
* ]
* ]);
*
* @param array $config Client configuration settings
* - base_url: Base URL of the client that is merged into relative URLs.
* Can be a string or an array that contains a URI template followed
* by an associative array of expansion variables to inject into the
* URI template.
* - handler: callable RingPHP handler used to transfer requests
* - message_factory: Factory used to create request and response object
* - defaults: Default request options to apply to each request
* - emitter: Event emitter used for request events
* - fsm: (internal use only) The request finite state machine. A
* function that accepts a transaction and optional final state. The
* function is responsible for transitioning a request through its
* lifecycle events.
*/
public function __construct(array $config = [])
{
$this->configureBaseUrl($config);
$this->configureDefaults($config);
if (isset($config['emitter'])) {
$this->emitter = $config['emitter'];
}
$this->messageFactory = isset($config['message_factory'])
? $config['message_factory']
: new MessageFactory();
if (isset($config['fsm'])) {
$this->fsm = $config['fsm'];
} else {
if (isset($config['handler'])) {
$handler = $config['handler'];
} elseif (isset($config['adapter'])) {
$handler = $config['adapter'];
} else {
$handler = Utils::getDefaultHandler();
}
$this->fsm = new RequestFsm($handler, $this->messageFactory);
}
}
public function getDefaultOption($keyOrPath = null)
{
return $keyOrPath === null
? $this->defaults
: Utils::getPath($this->defaults, $keyOrPath);
}
public function setDefaultOption($keyOrPath, $value)
{
Utils::setPath($this->defaults, $keyOrPath, $value);
}
public function getBaseUrl()
{
return (string) $this->baseUrl;
}
public function createRequest($method, $url = null, array $options = [])
{
$options = $this->mergeDefaults($options);
// Use a clone of the client's emitter
$options['config']['emitter'] = clone $this->getEmitter();
$url = $url || (is_string($url) && strlen($url))
? $this->buildUrl($url)
: (string) $this->baseUrl;
return $this->messageFactory->createRequest($method, $url, $options);
}
public function get($url = null, $options = [])
{
return $this->send($this->createRequest('GET', $url, $options));
}
public function head($url = null, array $options = [])
{
return $this->send($this->createRequest('HEAD', $url, $options));
}
public function delete($url = null, array $options = [])
{
return $this->send($this->createRequest('DELETE', $url, $options));
}
public function put($url = null, array $options = [])
{
return $this->send($this->createRequest('PUT', $url, $options));
}
public function patch($url = null, array $options = [])
{
return $this->send($this->createRequest('PATCH', $url, $options));
}
public function post($url = null, array $options = [])
{
return $this->send($this->createRequest('POST', $url, $options));
}
public function options($url = null, array $options = [])
{
return $this->send($this->createRequest('OPTIONS', $url, $options));
}
public function send(RequestInterface $request)
{
$isFuture = $request->getConfig()->get('future');
$trans = new Transaction($this, $request, $isFuture);
$fn = $this->fsm;
try {
$fn($trans);
if ($isFuture) {
// Turn the normal response into a future if needed.
return $trans->response instanceof FutureInterface
? $trans->response
: new FutureResponse(new FulfilledPromise($trans->response));
}
// Resolve deep futures if this is not a future
// transaction. This accounts for things like retries
// that do not have an immediate side-effect.
while ($trans->response instanceof FutureInterface) {
$trans->response = $trans->response->wait();
}
return $trans->response;
} catch (\Exception $e) {
if ($isFuture) {
// Wrap the exception in a promise
return new FutureResponse(new RejectedPromise($e));
}
throw RequestException::wrapException($trans->request, $e);
} catch (\TypeError $error) {
$exception = new \Exception($error->getMessage(), $error->getCode(), $error);
if ($isFuture) {
// Wrap the exception in a promise
return new FutureResponse(new RejectedPromise($exception));
}
throw RequestException::wrapException($trans->request, $exception);
}
}
/**
* Get an array of default options to apply to the client
*
* @return array
*/
protected function getDefaultOptions()
{
$settings = [
'allow_redirects' => true,
'exceptions' => true,
'decode_content' => true,
'verify' => true
];
// Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set.
// We can only trust the HTTP_PROXY environment variable in a CLI
// process due to the fact that PHP has no reliable mechanism to
// get environment variables that start with "HTTP_".
if (php_sapi_name() == 'cli' && getenv('HTTP_PROXY')) {
$settings['proxy']['http'] = getenv('HTTP_PROXY');
}
if ($proxy = getenv('HTTPS_PROXY')) {
$settings['proxy']['https'] = $proxy;
}
return $settings;
}
/**
* Expand a URI template and inherit from the base URL if it's relative
*
* @param string|array $url URL or an array of the URI template to expand
* followed by a hash of template varnames.
* @return string
* @throws \InvalidArgumentException
*/
private function buildUrl($url)
{
// URI template (absolute or relative)
if (!is_array($url)) {
return strpos($url, '://')
? (string) $url
: (string) $this->baseUrl->combine($url);
}
if (!isset($url[1])) {
throw new \InvalidArgumentException('You must provide a hash of '
. 'varname options in the second element of a URL array.');
}
// Absolute URL
if (strpos($url[0], '://')) {
return Utils::uriTemplate($url[0], $url[1]);
}
// Combine the relative URL with the base URL
return (string) $this->baseUrl->combine(
Utils::uriTemplate($url[0], $url[1])
);
}
private function configureBaseUrl(&$config)
{
if (!isset($config['base_url'])) {
$this->baseUrl = new Url('', '');
} elseif (!is_array($config['base_url'])) {
$this->baseUrl = Url::fromString($config['base_url']);
} elseif (count($config['base_url']) < 2) {
throw new \InvalidArgumentException('You must provide a hash of '
. 'varname options in the second element of a base_url array.');
} else {
$this->baseUrl = Url::fromString(
Utils::uriTemplate(
$config['base_url'][0],
$config['base_url'][1]
)
);
$config['base_url'] = (string) $this->baseUrl;
}
}
private function configureDefaults($config)
{
if (!isset($config['defaults'])) {
$this->defaults = $this->getDefaultOptions();
} else {
$this->defaults = array_replace(
$this->getDefaultOptions(),
$config['defaults']
);
}
// Add the default user-agent header
if (!isset($this->defaults['headers'])) {
$this->defaults['headers'] = [
'User-Agent' => Utils::getDefaultUserAgent()
];
} elseif (!Core::hasHeader($this->defaults, 'User-Agent')) {
// Add the User-Agent header if one was not already set
$this->defaults['headers']['User-Agent'] = Utils::getDefaultUserAgent();
}
}
/**
* Merges default options into the array passed by reference.
*
* @param array $options Options to modify by reference
*
* @return array
*/
private function mergeDefaults($options)
{
$defaults = $this->defaults;
// Case-insensitively merge in default headers if both defaults and
// options have headers specified.
if (!empty($defaults['headers']) && !empty($options['headers'])) {
// Create a set of lowercased keys that are present.
$lkeys = [];
foreach (array_keys($options['headers']) as $k) {
$lkeys[strtolower($k)] = true;
}
// Merge in lowercase default keys when not present in above set.
foreach ($defaults['headers'] as $key => $value) {
if (!isset($lkeys[strtolower($key)])) {
$options['headers'][$key] = $value;
}
}
// No longer need to merge in headers.
unset($defaults['headers']);
}
$result = array_replace_recursive($defaults, $options);
foreach ($options as $k => $v) {
if ($v === null) {
unset($result[$k]);
}
}
return $result;
}
/**
* @deprecated Use {@see GuzzleHttp\Pool} instead.
* @see GuzzleHttp\Pool
*/
public function sendAll($requests, array $options = [])
{
Pool::send($this, $requests, $options);
}
/**
* @deprecated Use GuzzleHttp\Utils::getDefaultHandler
*/
public static function getDefaultHandler()
{
return Utils::getDefaultHandler();
}
/**
* @deprecated Use GuzzleHttp\Utils::getDefaultUserAgent
*/
public static function getDefaultUserAgent()
{
return Utils::getDefaultUserAgent();
}
}

View File

@@ -0,0 +1,150 @@
<?php
namespace GuzzleHttp;
use GuzzleHttp\Event\HasEmitterInterface;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
/**
* Client interface for sending HTTP requests
*/
interface ClientInterface extends HasEmitterInterface
{
const VERSION = '5.3.1';
/**
* Create and return a new {@see RequestInterface} object.
*
* Use an absolute path to override the base path of the client, or a
* relative path to append to the base path of the client. The URL can
* contain the query string as well. Use an array to provide a URL
* template and additional variables to use in the URL template expansion.
*
* @param string $method HTTP method
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return RequestInterface
*/
public function createRequest($method, $url = null, array $options = []);
/**
* Send a GET request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function get($url = null, $options = []);
/**
* Send a HEAD request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function head($url = null, array $options = []);
/**
* Send a DELETE request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function delete($url = null, array $options = []);
/**
* Send a PUT request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function put($url = null, array $options = []);
/**
* Send a PATCH request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function patch($url = null, array $options = []);
/**
* Send a POST request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function post($url = null, array $options = []);
/**
* Send an OPTIONS request
*
* @param string|array|Url $url URL or URI template
* @param array $options Array of request options to apply.
*
* @return ResponseInterface
* @throws RequestException When an error is encountered
*/
public function options($url = null, array $options = []);
/**
* Sends a single request
*
* @param RequestInterface $request Request to send
*
* @return \GuzzleHttp\Message\ResponseInterface
* @throws \LogicException When the handler does not populate a response
* @throws RequestException When an error is encountered
*/
public function send(RequestInterface $request);
/**
* Get default request options of the client.
*
* @param string|null $keyOrPath The Path to a particular default request
* option to retrieve or pass null to retrieve all default request
* options. The syntax uses "/" to denote a path through nested PHP
* arrays. For example, "headers/content-type".
*
* @return mixed
*/
public function getDefaultOption($keyOrPath = null);
/**
* Set a default request option on the client so that any request created
* by the client will use the provided default value unless overridden
* explicitly when creating a request.
*
* @param string|null $keyOrPath The Path to a particular configuration
* value to set. The syntax uses a path notation that allows you to
* specify nested configuration values (e.g., 'headers/content-type').
* @param mixed $value Default request option value to set
*/
public function setDefaultOption($keyOrPath, $value);
/**
* Get the base URL of the client.
*
* @return string Returns the base URL if present
*/
public function getBaseUrl();
}

View File

@@ -0,0 +1,236 @@
<?php
namespace GuzzleHttp;
/**
* Key value pair collection object
*/
class Collection implements
\ArrayAccess,
\IteratorAggregate,
\Countable,
ToArrayInterface
{
use HasDataTrait;
/**
* @param array $data Associative array of data to set
*/
public function __construct(array $data = [])
{
$this->data = $data;
}
/**
* Create a new collection from an array, validate the keys, and add default
* values where missing
*
* @param array $config Configuration values to apply.
* @param array $defaults Default parameters
* @param array $required Required parameter names
*
* @return self
* @throws \InvalidArgumentException if a parameter is missing
*/
public static function fromConfig(
array $config = [],
array $defaults = [],
array $required = []
) {
$data = $config + $defaults;
if ($missing = array_diff($required, array_keys($data))) {
throw new \InvalidArgumentException(
'Config is missing the following keys: ' .
implode(', ', $missing));
}
return new self($data);
}
/**
* Removes all key value pairs
*/
public function clear()
{
$this->data = [];
}
/**
* Get a specific key value.
*
* @param string $key Key to retrieve.
*
* @return mixed|null Value of the key or NULL
*/
public function get($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
/**
* Set a key value pair
*
* @param string $key Key to set
* @param mixed $value Value to set
*/
public function set($key, $value)
{
$this->data[$key] = $value;
}
/**
* Add a value to a key. If a key of the same name has already been added,
* the key value will be converted into an array and the new value will be
* pushed to the end of the array.
*
* @param string $key Key to add
* @param mixed $value Value to add to the key
*/
public function add($key, $value)
{
if (!array_key_exists($key, $this->data)) {
$this->data[$key] = $value;
} elseif (is_array($this->data[$key])) {
$this->data[$key][] = $value;
} else {
$this->data[$key] = array($this->data[$key], $value);
}
}
/**
* Remove a specific key value pair
*
* @param string $key A key to remove
*/
public function remove($key)
{
unset($this->data[$key]);
}
/**
* Get all keys in the collection
*
* @return array
*/
public function getKeys()
{
return array_keys($this->data);
}
/**
* Returns whether or not the specified key is present.
*
* @param string $key The key for which to check the existence.
*
* @return bool
*/
public function hasKey($key)
{
return array_key_exists($key, $this->data);
}
/**
* Checks if any keys contains a certain value
*
* @param string $value Value to search for
*
* @return mixed Returns the key if the value was found FALSE if the value
* was not found.
*/
public function hasValue($value)
{
return array_search($value, $this->data, true);
}
/**
* Replace the data of the object with the value of an array
*
* @param array $data Associative array of data
*/
public function replace(array $data)
{
$this->data = $data;
}
/**
* Add and merge in a Collection or array of key value pair data.
*
* @param Collection|array $data Associative array of key value pair data
*/
public function merge($data)
{
foreach ($data as $key => $value) {
$this->add($key, $value);
}
}
/**
* Overwrite key value pairs in this collection with all of the data from
* an array or collection.
*
* @param array|\Traversable $data Values to override over this config
*/
public function overwriteWith($data)
{
if (is_array($data)) {
$this->data = $data + $this->data;
} elseif ($data instanceof Collection) {
$this->data = $data->toArray() + $this->data;
} else {
foreach ($data as $key => $value) {
$this->data[$key] = $value;
}
}
}
/**
* Returns a Collection containing all the elements of the collection after
* applying the callback function to each one.
*
* The callable should accept three arguments:
* - (string) $key
* - (string) $value
* - (array) $context
*
* The callable must return a the altered or unaltered value.
*
* @param callable $closure Map function to apply
* @param array $context Context to pass to the callable
*
* @return Collection
*/
public function map(callable $closure, array $context = [])
{
$collection = new static();
foreach ($this as $key => $value) {
$collection[$key] = $closure($key, $value, $context);
}
return $collection;
}
/**
* Iterates over each key value pair in the collection passing them to the
* callable. If the callable returns true, the current value from input is
* returned into the result Collection.
*
* The callable must accept two arguments:
* - (string) $key
* - (string) $value
*
* @param callable $closure Evaluation function
*
* @return Collection
*/
public function filter(callable $closure)
{
$collection = new static();
foreach ($this->data as $key => $value) {
if ($closure($key, $value)) {
$collection[$key] = $value;
}
}
return $collection;
}
}

View File

@@ -0,0 +1,248 @@
<?php
namespace GuzzleHttp\Cookie;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
use GuzzleHttp\ToArrayInterface;
/**
* Cookie jar that stores cookies an an array
*/
class CookieJar implements CookieJarInterface, ToArrayInterface
{
/** @var SetCookie[] Loaded cookie data */
private $cookies = [];
/** @var bool */
private $strictMode;
/**
* @param bool $strictMode Set to true to throw exceptions when invalid
* cookies are added to the cookie jar.
* @param array $cookieArray Array of SetCookie objects or a hash of arrays
* that can be used with the SetCookie constructor
*/
public function __construct($strictMode = false, $cookieArray = [])
{
$this->strictMode = $strictMode;
foreach ($cookieArray as $cookie) {
if (!($cookie instanceof SetCookie)) {
$cookie = new SetCookie($cookie);
}
$this->setCookie($cookie);
}
}
/**
* Create a new Cookie jar from an associative array and domain.
*
* @param array $cookies Cookies to create the jar from
* @param string $domain Domain to set the cookies to
*
* @return self
*/
public static function fromArray(array $cookies, $domain)
{
$cookieJar = new self();
foreach ($cookies as $name => $value) {
$cookieJar->setCookie(new SetCookie([
'Domain' => $domain,
'Name' => $name,
'Value' => $value,
'Discard' => true
]));
}
return $cookieJar;
}
/**
* Quote the cookie value if it is not already quoted and it contains
* problematic characters.
*
* @param string $value Value that may or may not need to be quoted
*
* @return string
*/
public static function getCookieValue($value)
{
if (substr($value, 0, 1) !== '"' &&
substr($value, -1, 1) !== '"' &&
strpbrk($value, ';,')
) {
$value = '"' . $value . '"';
}
return $value;
}
public function toArray()
{
return array_map(function (SetCookie $cookie) {
return $cookie->toArray();
}, $this->getIterator()->getArrayCopy());
}
public function clear($domain = null, $path = null, $name = null)
{
if (!$domain) {
$this->cookies = [];
return;
} elseif (!$path) {
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) use ($path, $domain) {
return !$cookie->matchesDomain($domain);
}
);
} elseif (!$name) {
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) use ($path, $domain) {
return !($cookie->matchesPath($path) &&
$cookie->matchesDomain($domain));
}
);
} else {
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) use ($path, $domain, $name) {
return !($cookie->getName() == $name &&
$cookie->matchesPath($path) &&
$cookie->matchesDomain($domain));
}
);
}
}
public function clearSessionCookies()
{
$this->cookies = array_filter(
$this->cookies,
function (SetCookie $cookie) {
return !$cookie->getDiscard() && $cookie->getExpires();
}
);
}
public function setCookie(SetCookie $cookie)
{
// Only allow cookies with set and valid domain, name, value
$result = $cookie->validate();
if ($result !== true) {
if ($this->strictMode) {
throw new \RuntimeException('Invalid cookie: ' . $result);
} else {
$this->removeCookieIfEmpty($cookie);
return false;
}
}
// Resolve conflicts with previously set cookies
foreach ($this->cookies as $i => $c) {
// Two cookies are identical, when their path, and domain are
// identical.
if ($c->getPath() != $cookie->getPath() ||
$c->getDomain() != $cookie->getDomain() ||
$c->getName() != $cookie->getName()
) {
continue;
}
// The previously set cookie is a discard cookie and this one is
// not so allow the new cookie to be set
if (!$cookie->getDiscard() && $c->getDiscard()) {
unset($this->cookies[$i]);
continue;
}
// If the new cookie's expiration is further into the future, then
// replace the old cookie
if ($cookie->getExpires() > $c->getExpires()) {
unset($this->cookies[$i]);
continue;
}
// If the value has changed, we better change it
if ($cookie->getValue() !== $c->getValue()) {
unset($this->cookies[$i]);
continue;
}
// The cookie exists, so no need to continue
return false;
}
$this->cookies[] = $cookie;
return true;
}
public function count()
{
return count($this->cookies);
}
public function getIterator()
{
return new \ArrayIterator(array_values($this->cookies));
}
public function extractCookies(
RequestInterface $request,
ResponseInterface $response
) {
if ($cookieHeader = $response->getHeaderAsArray('Set-Cookie')) {
foreach ($cookieHeader as $cookie) {
$sc = SetCookie::fromString($cookie);
if (!$sc->getDomain()) {
$sc->setDomain($request->getHost());
}
$this->setCookie($sc);
}
}
}
public function addCookieHeader(RequestInterface $request)
{
$values = [];
$scheme = $request->getScheme();
$host = $request->getHost();
$path = $request->getPath();
foreach ($this->cookies as $cookie) {
if ($cookie->matchesPath($path) &&
$cookie->matchesDomain($host) &&
!$cookie->isExpired() &&
(!$cookie->getSecure() || $scheme == 'https')
) {
$values[] = $cookie->getName() . '='
. self::getCookieValue($cookie->getValue());
}
}
if ($values) {
$request->setHeader('Cookie', implode('; ', $values));
}
}
/**
* If a cookie already exists and the server asks to set it again with a
* null value, the cookie must be deleted.
*
* @param SetCookie $cookie
*/
private function removeCookieIfEmpty(SetCookie $cookie)
{
$cookieValue = $cookie->getValue();
if ($cookieValue === null || $cookieValue === '') {
$this->clear(
$cookie->getDomain(),
$cookie->getPath(),
$cookie->getName()
);
}
}
}

View File

@@ -0,0 +1,75 @@
<?php
namespace GuzzleHttp\Cookie;
use GuzzleHttp\Message\RequestInterface;
use GuzzleHttp\Message\ResponseInterface;
/**
* Stores HTTP cookies.
*
* It extracts cookies from HTTP requests, and returns them in HTTP responses.
* CookieJarInterface instances automatically expire contained cookies when
* necessary. Subclasses are also responsible for storing and retrieving
* cookies from a file, database, etc.
*
* @link http://docs.python.org/2/library/cookielib.html Inspiration
*/
interface CookieJarInterface extends \Countable, \IteratorAggregate
{
/**
* Add a Cookie header to a request.
*
* If no matching cookies are found in the cookie jar, then no Cookie
* header is added to the request.
*
* @param RequestInterface $request Request object to update
*/
public function addCookieHeader(RequestInterface $request);
/**
* Extract cookies from an HTTP response and store them in the CookieJar.
*
* @param RequestInterface $request Request that was sent
* @param ResponseInterface $response Response that was received
*/
public function extractCookies(
RequestInterface $request,
ResponseInterface $response
);
/**
* Sets a cookie in the cookie jar.
*
* @param SetCookie $cookie Cookie to set.
*
* @return bool Returns true on success or false on failure
*/
public function setCookie(SetCookie $cookie);
/**
* Remove cookies currently held in the cookie jar.
*
* Invoking this method without arguments will empty the whole cookie jar.
* If given a $domain argument only cookies belonging to that domain will
* be removed. If given a $domain and $path argument, cookies belonging to
* the specified path within that domain are removed. If given all three
* arguments, then the cookie with the specified name, path and domain is
* removed.
*
* @param string $domain Clears cookies matching a domain
* @param string $path Clears cookies matching a domain and path
* @param string $name Clears cookies matching a domain, path, and name
*
* @return CookieJarInterface
*/
public function clear($domain = null, $path = null, $name = null);
/**
* Discard all sessions cookies.
*
* Removes cookies that don't have an expire field or a have a discard
* field set to true. To be called when the user agent shuts down according
* to RFC 2965.
*/
public function clearSessionCookies();
}

View File

@@ -0,0 +1,86 @@
<?php
namespace GuzzleHttp\Cookie;
use GuzzleHttp\Utils;
/**
* Persists non-session cookies using a JSON formatted file
*/
class FileCookieJar extends CookieJar
{
/** @var string filename */
private $filename;
/**
* Create a new FileCookieJar object
*
* @param string $cookieFile File to store the cookie data
*
* @throws \RuntimeException if the file cannot be found or created
*/
public function __construct($cookieFile)
{
$this->filename = $cookieFile;
if (file_exists($cookieFile)) {
$this->load($cookieFile);
}
}
/**
* Saves the file when shutting down
*/
public function __destruct()
{
$this->save($this->filename);
}
/**
* Saves the cookies to a file.
*
* @param string $filename File to save
* @throws \RuntimeException if the file cannot be found or created
*/
public function save($filename)
{
$json = [];
foreach ($this as $cookie) {
if ($cookie->getExpires() && !$cookie->getDiscard()) {
$json[] = $cookie->toArray();
}
}
if (false === file_put_contents($filename, json_encode($json), LOCK_EX)) {
// @codeCoverageIgnoreStart
throw new \RuntimeException("Unable to save file {$filename}");
// @codeCoverageIgnoreEnd
}
}
/**
* Load cookies from a JSON formatted file.
*
* Old cookies are kept unless overwritten by newly loaded ones.
*
* @param string $filename Cookie file to load.
* @throws \RuntimeException if the file cannot be loaded.
*/
public function load($filename)
{
$json = file_get_contents($filename);
if (false === $json) {
// @codeCoverageIgnoreStart
throw new \RuntimeException("Unable to load file {$filename}");
// @codeCoverageIgnoreEnd
}
$data = Utils::jsonDecode($json, true);
if (is_array($data)) {
foreach (Utils::jsonDecode($json, true) as $cookie) {
$this->setCookie(new SetCookie($cookie));
}
} elseif (strlen($data)) {
throw new \RuntimeException("Invalid cookie file: {$filename}");
}
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace GuzzleHttp\Cookie;
use GuzzleHttp\Utils;
/**
* Persists cookies in the client session
*/
class SessionCookieJar extends CookieJar
{
/** @var string session key */
private $sessionKey;
/**
* Create a new SessionCookieJar object
*
* @param string $sessionKey Session key name to store the cookie data in session
*/
public function __construct($sessionKey)
{
$this->sessionKey = $sessionKey;
$this->load();
}
/**
* Saves cookies to session when shutting down
*/
public function __destruct()
{
$this->save();
}
/**
* Save cookies to the client session
*/
public function save()
{
$json = [];
foreach ($this as $cookie) {
if ($cookie->getExpires() && !$cookie->getDiscard()) {
$json[] = $cookie->toArray();
}
}
$_SESSION[$this->sessionKey] = json_encode($json);
}
/**
* Load the contents of the client session into the data array
*/
protected function load()
{
$cookieJar = isset($_SESSION[$this->sessionKey])
? $_SESSION[$this->sessionKey]
: null;
$data = Utils::jsonDecode($cookieJar, true);
if (is_array($data)) {
foreach ($data as $cookie) {
$this->setCookie(new SetCookie($cookie));
}
} elseif (strlen($data)) {
throw new \RuntimeException("Invalid cookie data");
}
}
}

View File

@@ -0,0 +1,373 @@
<?php
namespace GuzzleHttp\Cookie;
use GuzzleHttp\ToArrayInterface;
/**
* Set-Cookie object
*/
class SetCookie implements ToArrayInterface
{
/** @var array */
private static $defaults = [
'Name' => null,
'Value' => null,
'Domain' => null,
'Path' => '/',
'Max-Age' => null,
'Expires' => null,
'Secure' => false,
'Discard' => false,
'HttpOnly' => false
];
/** @var array Cookie data */
private $data;
/**
* Create a new SetCookie object from a string
*
* @param string $cookie Set-Cookie header string
*
* @return self
*/
public static function fromString($cookie)
{
// Create the default return array
$data = self::$defaults;
// Explode the cookie string using a series of semicolons
$pieces = array_filter(array_map('trim', explode(';', $cookie)));
// The name of the cookie (first kvp) must include an equal sign.
if (empty($pieces) || !strpos($pieces[0], '=')) {
return new self($data);
}
// Add the cookie pieces into the parsed data array
foreach ($pieces as $part) {
$cookieParts = explode('=', $part, 2);
$key = trim($cookieParts[0]);
$value = isset($cookieParts[1])
? trim($cookieParts[1], " \n\r\t\0\x0B\"")
: true;
// Only check for non-cookies when cookies have been found
if (empty($data['Name'])) {
$data['Name'] = $key;
$data['Value'] = $value;
} else {
foreach (array_keys(self::$defaults) as $search) {
if (!strcasecmp($search, $key)) {
$data[$search] = $value;
continue 2;
}
}
$data[$key] = $value;
}
}
return new self($data);
}
/**
* @param array $data Array of cookie data provided by a Cookie parser
*/
public function __construct(array $data = [])
{
$this->data = array_replace(self::$defaults, $data);
// Extract the Expires value and turn it into a UNIX timestamp if needed
if (!$this->getExpires() && $this->getMaxAge()) {
// Calculate the Expires date
$this->setExpires(time() + $this->getMaxAge());
} elseif ($this->getExpires() && !is_numeric($this->getExpires())) {
$this->setExpires($this->getExpires());
}
}
public function __toString()
{
$str = $this->data['Name'] . '=' . $this->data['Value'] . '; ';
foreach ($this->data as $k => $v) {
if ($k != 'Name' && $k != 'Value' && $v !== null && $v !== false) {
if ($k == 'Expires') {
$str .= 'Expires=' . gmdate('D, d M Y H:i:s \G\M\T', $v) . '; ';
} else {
$str .= ($v === true ? $k : "{$k}={$v}") . '; ';
}
}
}
return rtrim($str, '; ');
}
public function toArray()
{
return $this->data;
}
/**
* Get the cookie name
*
* @return string
*/
public function getName()
{
return $this->data['Name'];
}
/**
* Set the cookie name
*
* @param string $name Cookie name
*/
public function setName($name)
{
$this->data['Name'] = $name;
}
/**
* Get the cookie value
*
* @return string
*/
public function getValue()
{
return $this->data['Value'];
}
/**
* Set the cookie value
*
* @param string $value Cookie value
*/
public function setValue($value)
{
$this->data['Value'] = $value;
}
/**
* Get the domain
*
* @return string|null
*/
public function getDomain()
{
return $this->data['Domain'];
}
/**
* Set the domain of the cookie
*
* @param string $domain
*/
public function setDomain($domain)
{
$this->data['Domain'] = $domain;
}
/**
* Get the path
*
* @return string
*/
public function getPath()
{
return $this->data['Path'];
}
/**
* Set the path of the cookie
*
* @param string $path Path of the cookie
*/
public function setPath($path)
{
$this->data['Path'] = $path;
}
/**
* Maximum lifetime of the cookie in seconds
*
* @return int|null
*/
public function getMaxAge()
{
return $this->data['Max-Age'];
}
/**
* Set the max-age of the cookie
*
* @param int $maxAge Max age of the cookie in seconds
*/
public function setMaxAge($maxAge)
{
$this->data['Max-Age'] = $maxAge;
}
/**
* The UNIX timestamp when the cookie Expires
*
* @return mixed
*/
public function getExpires()
{
return $this->data['Expires'];
}
/**
* Set the unix timestamp for which the cookie will expire
*
* @param int $timestamp Unix timestamp
*/
public function setExpires($timestamp)
{
$this->data['Expires'] = is_numeric($timestamp)
? (int) $timestamp
: strtotime($timestamp);
}
/**
* Get whether or not this is a secure cookie
*
* @return null|bool
*/
public function getSecure()
{
return $this->data['Secure'];
}
/**
* Set whether or not the cookie is secure
*
* @param bool $secure Set to true or false if secure
*/
public function setSecure($secure)
{
$this->data['Secure'] = $secure;
}
/**
* Get whether or not this is a session cookie
*
* @return null|bool
*/
public function getDiscard()
{
return $this->data['Discard'];
}
/**
* Set whether or not this is a session cookie
*
* @param bool $discard Set to true or false if this is a session cookie
*/
public function setDiscard($discard)
{
$this->data['Discard'] = $discard;
}
/**
* Get whether or not this is an HTTP only cookie
*
* @return bool
*/
public function getHttpOnly()
{
return $this->data['HttpOnly'];
}
/**
* Set whether or not this is an HTTP only cookie
*
* @param bool $httpOnly Set to true or false if this is HTTP only
*/
public function setHttpOnly($httpOnly)
{
$this->data['HttpOnly'] = $httpOnly;
}
/**
* Check if the cookie matches a path value
*
* @param string $path Path to check against
*
* @return bool
*/
public function matchesPath($path)
{
return !$this->getPath() || 0 === stripos($path, $this->getPath());
}
/**
* Check if the cookie matches a domain value
*
* @param string $domain Domain to check against
*
* @return bool
*/
public function matchesDomain($domain)
{
// Remove the leading '.' as per spec in RFC 6265.
// http://tools.ietf.org/html/rfc6265#section-5.2.3
$cookieDomain = ltrim($this->getDomain(), '.');
// Domain not set or exact match.
if (!$cookieDomain || !strcasecmp($domain, $cookieDomain)) {
return true;
}
// Matching the subdomain according to RFC 6265.
// http://tools.ietf.org/html/rfc6265#section-5.1.3
if (filter_var($domain, FILTER_VALIDATE_IP)) {
return false;
}
return (bool) preg_match('/\.' . preg_quote($cookieDomain) . '$/i', $domain);
}
/**
* Check if the cookie is expired
*
* @return bool
*/
public function isExpired()
{
return $this->getExpires() !== null && time() > $this->getExpires();
}
/**
* Check if the cookie is valid according to RFC 6265
*
* @return bool|string Returns true if valid or an error message if invalid
*/
public function validate()
{
// Names must not be empty, but can be 0
$name = $this->getName();
if (empty($name) && !is_numeric($name)) {
return 'The cookie name must not be empty';
}
// Check if any of the invalid characters are present in the cookie name
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
return "Cookie name must not cannot invalid characters: =,; \\t\\r\\n\\013\\014";
}
// Value must not be empty, but can be 0
$value = $this->getValue();
if (empty($value) && !is_numeric($value)) {
return 'The cookie value must not be empty';
}
// Domains must not be empty, but can be 0
// A "0" is not a valid internet domain, but may be used as server name
// in a private network.
$domain = $this->getDomain();
if (empty($domain) && !is_numeric($domain)) {
return 'The cookie domain must not be empty';
}
return true;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace GuzzleHttp\Event;
/**
* Basic event class that can be extended.
*/
abstract class AbstractEvent implements EventInterface
{
private $propagationStopped = false;
public function isPropagationStopped()
{
return $this->propagationStopped;
}
public function stopPropagation()
{
$this->propagationStopped = true;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace GuzzleHttp\Event;
use GuzzleHttp\Transaction;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Message\RequestInterface;
/**
* Base class for request events, providing a request and client getter.
*/
abstract class AbstractRequestEvent extends AbstractEvent
{
/** @var Transaction */
protected $transaction;
/**
* @param Transaction $transaction
*/
public function __construct(Transaction $transaction)
{
$this->transaction = $transaction;
}
/**
* Get the HTTP client associated with the event.
*
* @return ClientInterface
*/
public function getClient()
{
return $this->transaction->client;
}
/**
* Get the request object
*
* @return RequestInterface
*/
public function getRequest()
{
return $this->transaction->request;
}
/**
* Get the number of transaction retries.
*
* @return int
*/
public function getRetryCount()
{
return $this->transaction->retries;
}
/**
* @return Transaction
*/
public function getTransaction()
{
return $this->transaction;
}
}

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