feature #18894 [Cache] Added PhpFilesAdapter (trakos, nicolas-grekas)
This PR was merged into the 3.2-dev branch. Discussion ---------- [Cache] Added PhpFilesAdapter | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This is taking over #18832. With a warm cache I get these numbers consistently (PhpArrayAdapter being the implem in #18823 ): ``` Fetching randomly 5000 items 10000 times: Symfony\Component\Cache\Adapter\FilesystemAdapter: 0.1367, 2 megabytes Symfony\Component\Cache\Adapter\PhpArrayAdapter: 0.0071, 2 megabytes Symfony\Component\Cache\Adapter\PhpFilesAdapter: 0.0389, 2 megabytes Symfony\Component\Cache\Adapter\ApcuAdapter: 0.0361, 2 megabytes ``` This means that the PhpArrayAdapter should be used first, then ApcuAdapter preferred over PhpFilesAdapter, then FilesystemAdapter. This is what AbstractAdapter does here. Also note that to get the cache working, one should stay within the limits defined by the following ini settings: - memory_limit - apc.shm_size - opcache.memory_consumption - opcache.interned_strings_buffer - opcache.max_accelerated_files Commits -------8983e83
[Cache] Optimize & wire PhpFilesAdapter14bcd79
[Cache] Added PhpFilesAdapter
This commit is contained in:
commit
6f833284cb
@ -52,6 +52,7 @@ before_install:
|
|||||||
- if [[ ! $PHP = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi
|
- if [[ ! $PHP = hhvm* ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi
|
||||||
- if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi
|
- if [[ ! $skip ]]; then echo memory_limit = -1 >> $INI_FILE; fi
|
||||||
- if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi
|
- if [[ ! $skip ]]; then echo session.gc_probability = 0 >> $INI_FILE; fi
|
||||||
|
- if [[ ! $skip ]]; then echo opcache.enable_cli = 1 >> $INI_FILE; fi
|
||||||
- if [[ ! $skip && $PHP = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi
|
- if [[ ! $skip && $PHP = 5.* ]]; then echo extension = mongo.so >> $INI_FILE; fi
|
||||||
- if [[ ! $skip && $PHP = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi
|
- if [[ ! $skip && $PHP = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi
|
||||||
- if [[ ! $skip && $PHP = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi
|
- if [[ ! $skip && $PHP = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi
|
||||||
|
@ -35,6 +35,8 @@ install:
|
|||||||
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini-min
|
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini-min
|
||||||
- IF %PHP%==1 echo extension_dir=ext >> php.ini-min
|
- IF %PHP%==1 echo extension_dir=ext >> php.ini-min
|
||||||
- IF %PHP%==1 copy /Y php.ini-min php.ini-max
|
- IF %PHP%==1 copy /Y php.ini-min php.ini-max
|
||||||
|
- IF %PHP%==1 echo zend_extension=php_opcache.dll >> php.ini-max
|
||||||
|
- IF %PHP%==1 echo opcache.enable_cli=1 >> php.ini-max
|
||||||
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini-max
|
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini-max
|
||||||
- IF %PHP%==1 echo extension=php_apcu.dll >> php.ini-max
|
- IF %PHP%==1 echo extension=php_apcu.dll >> php.ini-max
|
||||||
- IF %PHP%==1 echo apc.enable_cli=1 >> php.ini-max
|
- IF %PHP%==1 echo apc.enable_cli=1 >> php.ini-max
|
||||||
|
@ -70,6 +70,15 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
|
|||||||
|
|
||||||
public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
|
public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null)
|
||||||
{
|
{
|
||||||
|
if (!ApcuAdapter::isSupported() && PhpFilesAdapter::isSupported()) {
|
||||||
|
$opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory);
|
||||||
|
if (null !== $logger) {
|
||||||
|
$opcache->setLogger($logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $opcache;
|
||||||
|
}
|
||||||
|
|
||||||
$fs = new FilesystemAdapter($namespace, $defaultLifetime, $directory);
|
$fs = new FilesystemAdapter($namespace, $defaultLifetime, $directory);
|
||||||
if (null !== $logger) {
|
if (null !== $logger) {
|
||||||
$fs->setLogger($logger);
|
$fs->setLogger($logger);
|
||||||
|
@ -11,43 +11,17 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Cache\Adapter;
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Nicolas Grekas <p@tchwork.com>
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
*/
|
*/
|
||||||
class FilesystemAdapter extends AbstractAdapter
|
class FilesystemAdapter extends AbstractAdapter
|
||||||
{
|
{
|
||||||
private $directory;
|
use FilesystemAdapterTrait;
|
||||||
|
|
||||||
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
|
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
|
||||||
{
|
{
|
||||||
parent::__construct('', $defaultLifetime);
|
parent::__construct('', $defaultLifetime);
|
||||||
|
$this->init($namespace, $directory);
|
||||||
if (!isset($directory[0])) {
|
|
||||||
$directory = sys_get_temp_dir().'/symfony-cache';
|
|
||||||
}
|
|
||||||
if (isset($namespace[0])) {
|
|
||||||
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('FilesystemAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
|
||||||
}
|
|
||||||
$directory .= '/'.$namespace;
|
|
||||||
}
|
|
||||||
if (!file_exists($dir = $directory.'/.')) {
|
|
||||||
@mkdir($directory, 0777, true);
|
|
||||||
}
|
|
||||||
if (false === $dir = realpath($dir)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory));
|
|
||||||
}
|
|
||||||
if (!is_writable($dir .= DIRECTORY_SEPARATOR)) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
|
|
||||||
}
|
|
||||||
// On Windows the whole path is limited to 258 chars
|
|
||||||
if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) > 234) {
|
|
||||||
throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->directory = $dir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,35 +65,6 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id)));
|
return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function doClear($namespace)
|
|
||||||
{
|
|
||||||
$ok = true;
|
|
||||||
|
|
||||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
|
|
||||||
$ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
protected function doDelete(array $ids)
|
|
||||||
{
|
|
||||||
$ok = true;
|
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
|
||||||
$file = $this->getFile($id);
|
|
||||||
$ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -127,32 +72,11 @@ class FilesystemAdapter extends AbstractAdapter
|
|||||||
{
|
{
|
||||||
$ok = true;
|
$ok = true;
|
||||||
$expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX;
|
$expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX;
|
||||||
$tmp = $this->directory.uniqid('', true);
|
|
||||||
|
|
||||||
foreach ($values as $id => $value) {
|
foreach ($values as $id => $value) {
|
||||||
$file = $this->getFile($id, true);
|
$ok = $this->write($this->getFile($id, true), $expiresAt."\n".rawurlencode($id)."\n".serialize($value), $expiresAt) && $ok;
|
||||||
|
|
||||||
$value = $expiresAt."\n".rawurlencode($id)."\n".serialize($value);
|
|
||||||
if (false !== @file_put_contents($tmp, $value)) {
|
|
||||||
@touch($tmp, $expiresAt);
|
|
||||||
$ok = @rename($tmp, $file) && $ok;
|
|
||||||
} else {
|
|
||||||
$ok = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getFile($id, $mkdir = false)
|
|
||||||
{
|
|
||||||
$hash = str_replace('/', '-', base64_encode(md5($id, true)));
|
|
||||||
$dir = $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR;
|
|
||||||
|
|
||||||
if ($mkdir && !file_exists($dir)) {
|
|
||||||
@mkdir($dir, 0777, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $dir.substr($hash, 2, -2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
110
src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php
Normal file
110
src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
trait FilesystemAdapterTrait
|
||||||
|
{
|
||||||
|
private $directory;
|
||||||
|
private $tmp;
|
||||||
|
|
||||||
|
private function init($namespace, $directory)
|
||||||
|
{
|
||||||
|
if (!isset($directory[0])) {
|
||||||
|
$directory = sys_get_temp_dir().'/symfony-cache';
|
||||||
|
}
|
||||||
|
if (isset($namespace[0])) {
|
||||||
|
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
||||||
|
}
|
||||||
|
$directory .= '/'.$namespace;
|
||||||
|
}
|
||||||
|
if (!file_exists($dir = $directory.'/.')) {
|
||||||
|
@mkdir($directory, 0777, true);
|
||||||
|
}
|
||||||
|
if (false === $dir = realpath($dir)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory));
|
||||||
|
}
|
||||||
|
if (!is_writable($dir .= DIRECTORY_SEPARATOR)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
|
||||||
|
}
|
||||||
|
// On Windows the whole path is limited to 258 chars
|
||||||
|
if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) > 234) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->directory = $dir;
|
||||||
|
$this->tmp = $this->directory.uniqid('', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doClear($namespace)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
|
||||||
|
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
|
||||||
|
$ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doDelete(array $ids)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$file = $this->getFile($id);
|
||||||
|
$ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function write($file, $data, $expiresAt = null)
|
||||||
|
{
|
||||||
|
if (false === @file_put_contents($this->tmp, $data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (null !== $expiresAt) {
|
||||||
|
@touch($this->tmp, $expiresAt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (@rename($this->tmp, $file)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@unlink($this->tmp);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFile($id, $mkdir = false)
|
||||||
|
{
|
||||||
|
$hash = str_replace('/', '-', base64_encode(md5(static::class.$id, true)));
|
||||||
|
$dir = $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR;
|
||||||
|
|
||||||
|
if ($mkdir && !file_exists($dir)) {
|
||||||
|
@mkdir($dir, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dir.substr($hash, 2, -2);
|
||||||
|
}
|
||||||
|
}
|
123
src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
Normal file
123
src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Adapter;
|
||||||
|
|
||||||
|
use Symfony\Component\Cache\Exception\CacheException;
|
||||||
|
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Piotr Stankowski <git@trakos.pl>
|
||||||
|
* @author Nicolas Grekas <p@tchwork.com>
|
||||||
|
*/
|
||||||
|
class PhpFilesAdapter extends AbstractAdapter
|
||||||
|
{
|
||||||
|
use FilesystemAdapterTrait;
|
||||||
|
|
||||||
|
private $includeHandler;
|
||||||
|
|
||||||
|
public static function isSupported()
|
||||||
|
{
|
||||||
|
return function_exists('opcache_compile_file') && ini_get('opcache.enable');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
|
||||||
|
{
|
||||||
|
if (!static::isSupported()) {
|
||||||
|
throw new CacheException('OPcache is not enabled');
|
||||||
|
}
|
||||||
|
parent::__construct('', $defaultLifetime);
|
||||||
|
$this->init($namespace, $directory);
|
||||||
|
|
||||||
|
$e = new \Exception();
|
||||||
|
$this->includeHandler = function () use ($e) { throw $e; };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doFetch(array $ids)
|
||||||
|
{
|
||||||
|
$values = array();
|
||||||
|
$now = time();
|
||||||
|
|
||||||
|
set_error_handler($this->includeHandler);
|
||||||
|
try {
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
try {
|
||||||
|
$file = $this->getFile($id);
|
||||||
|
list($expiresAt, $values[$id]) = include $file;
|
||||||
|
if ($now >= $expiresAt) {
|
||||||
|
unset($values[$id]);
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($values as $id => $value) {
|
||||||
|
if ('N;' === $value) {
|
||||||
|
$values[$id] = null;
|
||||||
|
} elseif (is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||||
|
$values[$id] = unserialize($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doHave($id)
|
||||||
|
{
|
||||||
|
return (bool) $this->doFetch(array($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
protected function doSave(array $values, $lifetime)
|
||||||
|
{
|
||||||
|
$ok = true;
|
||||||
|
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
|
||||||
|
|
||||||
|
foreach ($values as $id => $value) {
|
||||||
|
if (null === $value || is_object($value)) {
|
||||||
|
$value = serialize($value);
|
||||||
|
} elseif (is_array($value)) {
|
||||||
|
$serialized = serialize($value);
|
||||||
|
$unserialized = unserialize($serialized);
|
||||||
|
// Store arrays serialized if they contain any objects or references
|
||||||
|
if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) {
|
||||||
|
$value = $serialized;
|
||||||
|
}
|
||||||
|
} elseif (is_string($value)) {
|
||||||
|
// Serialize strings if they could be confused with serialized objects or arrays
|
||||||
|
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
|
||||||
|
$value = serialize($value);
|
||||||
|
}
|
||||||
|
} elseif (!is_scalar($value)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Value of type "%s" is not serializable', $key, gettype($value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data[1] = $value;
|
||||||
|
$file = $this->getFile($id, true);
|
||||||
|
$ok = $this->write($file, '<?php return '.var_export($data, true).';') && $ok;
|
||||||
|
@opcache_compile_file($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ok;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Symfony package.
|
||||||
|
*
|
||||||
|
* (c) Fabien Potencier <fabien@symfony.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||||
|
|
||||||
|
use Cache\IntegrationTests\CachePoolTest;
|
||||||
|
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @group time-sensitive
|
||||||
|
*/
|
||||||
|
class PhpFilesAdapterTest extends CachePoolTest
|
||||||
|
{
|
||||||
|
public function createCachePool()
|
||||||
|
{
|
||||||
|
if (defined('HHVM_VERSION')) {
|
||||||
|
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM';
|
||||||
|
}
|
||||||
|
if (!PhpFilesAdapter::isSupported()) {
|
||||||
|
$this->markTestSkipped('OPcache extension is not enabled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PhpFilesAdapter('sf-cache');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass()
|
||||||
|
{
|
||||||
|
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user