update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit d885a4baa9
5833 changed files with 418860 additions and 226988 deletions

View file

@ -0,0 +1,172 @@
# Piwik/Cache
This is a PHP caching library based on [Doctrine cache](https://github.com/doctrine/cache) that supports different backends.
At [Piwik](http://piwik.org) we developed this library with the focus on speed as we make heavy use of caching and
sometimes fetch hundreds of entries from the cache in one request.
[![Build Status](https://travis-ci.org/piwik/component-cache.svg?branch=master)](https://travis-ci.org/piwik/component-cache)
[![Coverage Status](https://coveralls.io/repos/piwik/component-cache/badge.png?branch=master)](https://coveralls.io/r/piwik/component-cache?branch=master)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/piwik/component-cache/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/piwik/component-cache/?branch=master)
## Installation
With Composer:
```json
{
"require": {
"piwik/cache": "*"
}
}
```
## Supported backends
* Array (holds cache entries only during one request but is very fast)
* Null (useful for development, won't cache anything)
* File (stores the cache entry on the file system)
* Redis (stores the cache entry on a Redis server, requires [phpredis](https://github.com/nicolasff/phpredis))
* Chained (allows to chain multiple backends to make sure it will read from a fast cache if possible)
Doctrine cache provides support for many more backends and adding one of those is easy. For example:
* APC
* Couchbase
* Memcache
* MongoDB
* Riak
* WinCache
* Xcache
* ZendData
Please send a pull request in case you have added one.
## Different caches
This library comes with three different types of caches. The naming is not optimal right now.
### Lazy
This can be considered as the default cache to use in case you don't know which one to pick. The lazy cache works with
any backend so you can decide whether you want to persist cache entries between requests or not. It does not support
the caching of any objects. Only boolean, numbers, strings and arrays are supported. Whenever you request an entry
from the cache it will fetch the entry from the defined backend again which can cause many reads depending on your
application.
### Eager
This cache stores all its cache entries under one "cache" entry in a configurable backend.
This comes handy for things that you need very often, nearly in every request. Instead of having to read eg.
a hundred cache entries from files it only loads one cache entry which contains the hundred keys. Should be used only
for things that you need very often and only for cache entries that are not too large to keep loading and parsing the
single cache entry fast. This cache is even more useful in case you are using a slow backend such as a file or a database.
Instead of having a hundred stat calls there will be only one. All cache entries it contains have the same life time.
For fast performance it won't validate any cache ids. It is not possible to cache any objects using this cache.
### Transient
This class is used to cache any data during one request. It won't be persisted.
All cache entries will be cached in a simple array meaning it is very fast to save and fetch cache entries. You can
basically achieve the same by using a lazy cache and a backend that does not persist any data such as the array cache
but this one will be a bit faster as it won't validate any cache ids and it allows you to cache any kind of objects.
Compared to the lazy cache it does not support setting any life time as it will be only valid during one request anyway.
Use this one if you read hundreds or thousands of cache entries and if performance really matters to you.
## Usage
### Creating a file backend
```php
$options = array('directory' => '/path/to/cache');
$factory = new \Piwik\Cache\Backend\Factory();
$backend = $factory->buildBackend('file', $options);
```
### Creating a Redis backend
```php
$options = array(
'host' => '127.0.0.1',
'port' => '6379',
'timeout' => 0.0,
'database' => 15, // optional
'password' => 'secret', // optional
);
$factory = new \Piwik\Cache\Backend\Factory();
$backend = $factory->buildBackend('redis', $options);
```
### Creating a chained backend
```php
$options = array(
'backends' => array('array', 'file'),
'file' => array('directory' => '/path/to/cache')
);
$factory = new \Piwik\Cache\Backend\Factory();
$backend = $factory->buildBackend('redis', $options);
```
Whenever you set a cache entry it will save it in the array and in the file cache. Whenever you are trying to read a cache
entry it will first try to get it from the fast array cache. In case it is not available there it will try to fetch
the cache entry from the file system. If the cache entry exists on the file system it will cache the entry automatically
using the array cache so the next read within this request will be fast and won't cause a stat call again. If you delete
a cache entry it will be removed from all configured backends. You can chain any backends. It is recommended to list
faster backends first.
### Creating a lazy cache
[Description lazy cache.](#lazy)
```php
$factory = new \Piwik\Cache\Backend\Factory();
$backend = $factory->buildBackend('file', array('directory' => '/path/to/cache'));
$cache = new \Piwik\Cache\Lazy($backend);
$cache->fetch('myid');
$cache->contains('myid');
$cache->delete('myid');
$cache->save('myid', 'myvalue', $lifeTimeInSeconds = 300);
$cache->flushAll();
```
### Creating an eager cache
[Description eager cache.](#eager)
```php
$cache = new \Piwik\Cache\Eager($backend, $storageId = 'eagercache');
$cache->fetch('myid');
$cache->contains('myid');
$cache->delete('myid');
$cache->save('myid', new \stdClass());
$cache->persistCacheIfNeeded($lifeTimeInSeconds = 300);
$cache->flushAll();
```
It will cache all set cache entries under the cache entry `eagercache`.
### Creating a transient cache
[Description transient cache.](#transient)
```php
$cache = new \Piwik\Cache\Transient();
$cache->fetch('myid');
$cache->contains('myid');
$cache->delete('myid');
$cache->save('myid', new \stdClass());
$cache->flushAll();
```
## License
The Cache component is released under the [LGPL v3.0](http://choosealicense.com/licenses/lgpl-3.0/).
## Changelog
* 0.2.5: updating to doctrine/cache 1.4 which contains our fix
* 0.2.4: do not throw exception when clearing a file cache if the cache dir doesn't exist
* 0.2.3: fixed another race condition in file cache
* 0.2.2: fixed a race condition in file cache
* 0.2.0: Initial release

View file

@ -0,0 +1,31 @@
{
"name": "piwik/cache",
"type": "library",
"license": "LGPL-3.0",
"description": "PHP caching library based on Doctrine cache",
"keywords": ["cache","array","file","redis"],
"authors": [
{
"name": "The Piwik Team",
"email": "hello@piwik.org",
"homepage": "http://piwik.org/the-piwik-team/"
}
],
"autoload": {
"psr-4": {
"Piwik\\Cache\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\Piwik\\Cache\\": "tests/"
}
},
"require": {
"php": ">=5.3.2",
"doctrine/cache": "~1.4"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./vendor/autoload.php">
<testsuites>
<testsuite name="Test suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

View file

@ -0,0 +1,62 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache;
/**
* Backend interface
*/
interface Backend
{
/**
* 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 doFetch($id);
/**
* Tests if an entry exists in the cache.
*
* @param string $id The cache id of the entry to check for.
*
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
public function doContains($id);
/**
* Puts data into the cache.
*
* @param string $id The cache id.
* @param mixed $data The cache entry/data.
* @param int $lifeTime The cache lifetime.
* If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime).
*
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
public function doSave($id, $data, $lifeTime = 0);
/**
* Deletes a cache entry.
*
* @param string $id The cache id.
*
* @return boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
*/
public function doDelete($id);
/**
* Flushes all cache entries from the cache.
*
* @return boolean
*/
public function doFlush();
}

View file

@ -0,0 +1,46 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend;
use Piwik\Cache\Backend;
use Doctrine\Common\Cache\ArrayCache as DoctrineArrayCache;
class ArrayCache extends DoctrineArrayCache implements Backend
{
public function doFetch($id)
{
return parent::doFetch($id);
}
public function doContains($id)
{
return parent::doContains($id);
}
public function doSave($id, $data, $lifeTime = 0)
{
return parent::doSave($id, $data, $lifeTime);
}
public function doDelete($id)
{
if (!$this->doContains($id)) {
return false;
}
return parent::doDelete($id);
}
public function doFlush()
{
return parent::doFlush();
}
}

View file

@ -0,0 +1,103 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend;
use Piwik\Cache\Backend;
/**
* TODO: extend Doctrine ChainCache as soon as available
*/
class Chained implements Backend
{
/**
* @var Backend[]
*/
private $backends = array();
/**
* Initializes the chained backend.
*
* @param Backend[] $backends An array of backends to use. They should be ordered from fastest to slowest.
*/
public function __construct($backends = array())
{
$this->backends = array_values($backends);
}
public function getBackends()
{
return $this->backends;
}
public function doFetch($id)
{
foreach ($this->backends as $key => $backend) {
if ($backend->doContains($id)) {
$value = $backend->doFetch($id);
// EG If chain is ARRAY => REDIS => DB and we find result in DB we will update REDIS and ARRAY
for ($subKey = $key - 1 ; $subKey >= 0 ; $subKey--) {
$this->backends[$subKey]->doSave($id, $value, 300); // TODO we should use the actual TTL here
}
return $value;
}
}
return false;
}
public function doContains($id)
{
foreach ($this->backends as $backend) {
if ($backend->doContains($id)) {
return true;
}
}
return false;
}
public function doSave($id, $data, $lifeTime = 0)
{
$stored = true;
foreach ($this->backends as $backend) {
$stored = $backend->doSave($id, $data, $lifeTime) && $stored;
}
return $stored;
}
// returns true if was deleted from at least one backend, false if it was not present in any of those
public function doDelete($id)
{
$success = false;
foreach ($this->backends as $backend) {
if ($backend->doContains($id)) {
$success = $backend->doDelete($id) || $success;
}
}
return $success;
}
public function doFlush()
{
$flushed = true;
foreach ($this->backends as $backend) {
$flushed = $backend->doFlush() && $flushed;
}
return $flushed;
}
}

View file

@ -0,0 +1,111 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend;
use Piwik\Cache\Backend;
class Factory
{
public function buildArrayCache()
{
return new ArrayCache();
}
public function buildFileCache($options)
{
return new File($options['directory']);
}
public function buildNullCache()
{
return new NullCache();
}
public function buildChainedCache($options)
{
$backends = array();
foreach ($options['backends'] as $backendToBuild) {
$backendOptions = array();
if (array_key_exists($backendToBuild, $options)) {
$backendOptions = $options[$backendToBuild];
}
$backends[] = $this->buildBackend($backendToBuild, $backendOptions);
}
return new Chained($backends);
}
public function buildRedisCache($options)
{
if (empty($options['host']) || empty($options['port'])) {
throw new \InvalidArgumentException('RedisCache is not configured. Please provide at least a host and a port');
}
$timeout = 0.0;
if (array_key_exists('timeout', $options)) {
$timeout = $options['timeout'];
}
$redis = new \Redis();
$redis->connect($options['host'], $options['port'], $timeout);
if (!empty($options['password'])) {
$redis->auth($options['password']);
}
if (array_key_exists('database', $options)) {
$redis->select((int) $options['database']);
}
$redisCache = new Redis();
$redisCache->setRedis($redis);
return $redisCache;
}
/**
* Build a specific backend instance.
*
* @param string $type The type of backend you want to create. Eg 'array', 'file', 'chained', 'null', 'redis'.
* @param array $options An array of options for the backend you want to create.
* @return Backend
* @throws Factory\BackendNotFoundException In case the given type was not found.
*/
public function buildBackend($type, array $options)
{
switch ($type) {
case 'array':
return $this->buildArrayCache();
case 'file':
return $this->buildFileCache($options);
case 'chained':
return $this->buildChainedCache($options);
case 'null':
return $this->buildNullCache();
case 'redis':
return $this->buildRedisCache($options);
default:
throw new Factory\BackendNotFoundException("Cache backend $type not valid");
}
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend\Factory;
use \Exception;
class BackendNotFoundException extends Exception {
}

View file

@ -0,0 +1,155 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend;
use Doctrine\Common\Cache\PhpFileCache;
use Piwik\Cache\Backend;
/**
* This class is used to cache data on the filesystem.
*
* This cache creates one file per id. Every time you try to read the value it will load the cache file again. It will
* try to invalidate the Opcache for a specific cache file if needed.
*/
class File extends PhpFileCache implements Backend
{
// for testing purposes since tests run on both CLI/FPM (changes in CLI can't invalidate
// opcache in FPM, so we have to invalidate before reading)
public static $invalidateOpCacheBeforeRead = false;
/**
* Constructor.
*
* @param string $directory The cache directory.
* @param string|null $extension The cache file extension.
*
* @throws \InvalidArgumentException
*/
public function __construct($directory, $extension = '.php')
{
if (!is_dir($directory)) {
$this->createDirectory($directory);
}
parent::__construct($directory, $extension);
}
public function doFetch($id)
{
if (self::$invalidateOpCacheBeforeRead) {
$this->invalidateCacheFile($id);
}
return parent::doFetch($id);
}
public function doContains($id)
{
return parent::doContains($id);
}
public function doSave($id, $data, $lifeTime = 0)
{
if (!is_dir($this->directory)) {
$this->createDirectory($this->directory);
}
$success = parent::doSave($id, $data, $lifeTime);
if ($success) {
$this->invalidateCacheFile($id);
}
return $success;
}
public function doDelete($id)
{
$this->invalidateCacheFile($id);
$success = parent::doDelete($id);
$this->invalidateCacheFile($id); // in case file was cached by another request between invalidate and doDelete()
return $success;
}
public function doFlush()
{
// if the directory does not exist, do not bother to continue clearing
if (!is_dir($this->directory)) {
return;
}
foreach ($this->getFileIterator() as $name => $file) {
$this->opCacheInvalidate($name);
}
parent::doFlush();
}
private function invalidateCacheFile($id)
{
$filename = $this->getFilename($id);
$this->opCacheInvalidate($filename);
}
/**
* @param string $id
*
* @return string
*/
protected function getFilename($id)
{
$path = $this->directory . DIRECTORY_SEPARATOR;
$id = preg_replace('@[\\\/:"*?<>|]+@', '', $id);
return $path . $id . $this->getExtension();
}
private function opCacheInvalidate($filepath)
{
if (is_file($filepath)) {
if (function_exists('opcache_invalidate')) {
@opcache_invalidate($filepath, $force = true);
}
if (function_exists('apc_delete_file')) {
@apc_delete_file($filepath);
}
}
}
/**
* @return \Iterator
*/
private function getFileIterator()
{
$pattern = '/^.+\\' . $this->getExtension() . '$/i';
$iterator = new \RecursiveDirectoryIterator($this->directory);
$iterator = new \RecursiveIteratorIterator($iterator);
return new \RegexIterator($iterator, $pattern);
}
private function createDirectory($path)
{
if (!is_dir($path)) {
// the mode in mkdir is modified by the current umask
@mkdir($path, 0750, $recursive = true);
}
// try to overcome restrictive umask (mis-)configuration
if (!is_writable($path)) {
@chmod($path, 0755);
if (!is_writable($path)) {
@chmod($path, 0775);
// enough! we're not going to make the directory world-writeable
}
}
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend;
use Piwik\Cache\Backend;
/**
* Can be used in development to prevent caching. Does not cache anything.
*/
class NullCache implements Backend
{
public function doFetch($id)
{
return false;
}
public function doContains($id)
{
return false;
}
public function doSave($id, $data, $lifeTime = 0)
{
return true;
}
public function doDelete($id)
{
return true;
}
public function doFlush()
{
return true;
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache\Backend;
use Doctrine\Common\Cache\RedisCache;
use Piwik\Cache\Backend;
class Redis extends RedisCache implements Backend
{
public function doFetch($id)
{
return parent::doFetch($id);
}
public function doContains($id)
{
return parent::doContains($id);
}
public function doSave($id, $data, $lifeTime = 0)
{
return parent::doSave($id, $data, $lifeTime);
}
public function doDelete($id)
{
return parent::doDelete($id);
}
public function doFlush()
{
return parent::doFlush();
}
}

View file

@ -0,0 +1,55 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache;
interface Cache
{
/**
* Fetches an entry from the cache.
*
* @param string $id The cache id.
* @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.
* @return boolean TRUE if a cache entry exists for the given cache id, FALSE otherwise.
*/
public function contains($id);
/**
* Puts data into the cache.
*
* @param string $id The cache id.
* @param mixed $data The cache entry/data.
* @param int $lifeTime The cache lifetime in seconds.
* If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime).
*
* @return boolean 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 boolean TRUE if the cache entry was successfully deleted, FALSE otherwise.
*/
public function delete($id);
/**
* Flushes all cache entries.
*
* @return boolean TRUE if the cache entries were successfully flushed, FALSE otherwise.
*/
public function flushAll();
}

View file

@ -0,0 +1,138 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache;
use Piwik\Cache\Backend;
/**
* This cache uses one "cache" entry for all cache entries it contains.
*
* This comes handy for things that you need very often, nearly in every request. Instead of having to read eg.
* a hundred caches from file we only load one file which contains the hundred cache ids. Should be used only for things
* that you need very often and only for cache entries that are not too large to keep loading and parsing the single
* cache entry fast.
*
* $cache = new Eager($backend, $storageId = 'eagercache');
* // $cache->fetch('my'id')
* // $cache->save('myid', 'test');
*
* // ... at some point or at the end of the request
* $cache->persistCacheIfNeeded($lifeTime = 43200);
*/
class Eager implements Cache
{
/**
* @var Backend
*/
private $storage;
private $storageId;
private $content = array();
private $isDirty = false;
/**
* Loads the cache entries from the given backend using the given storageId.
*
* @param Backend $storage
* @param $storageId
*/
public function __construct(Backend $storage, $storageId)
{
$this->storage = $storage;
$this->storageId = $storageId;
$content = $storage->doFetch($storageId);
if (is_array($content)) {
$this->content = $content;
}
}
/**
* Fetches an entry from the cache.
*
* Make sure to call the method {@link contains()} to verify whether there is actually any content saved under
* this cache id.
*
* @param string $id The cache id.
* @return int|float|string|boolean|array
*/
public function fetch($id)
{
return $this->content[$id];
}
/**
* {@inheritdoc}
*/
public function contains($id)
{
return array_key_exists($id, $this->content);
}
/**
* Puts data into the cache.
*
* @param string $id The cache id.
* @param int|float|string|boolean|array $content
* @param int $lifeTime Setting a lifetime is not supported by this cache and the parameter will be ignored.
* @return boolean
*/
public function save($id, $content, $lifeTime = 0)
{
if (is_object($content)) {
throw new \InvalidArgumentException('You cannot use this cache to cache an object, only arrays, strings and numbers. Have a look at Transient cache.');
// for performance reasons we do currently not recursively search whether any array contains an object.
}
$this->content[$id] = $content;
$this->isDirty = true;
return true;
}
/**
* {@inheritdoc}
*/
public function delete($id)
{
if ($this->contains($id)) {
$this->isDirty = true;
unset($this->content[$id]);
return true;
}
return false;
}
/**
* {@inheritdoc}
*/
public function flushAll()
{
$this->storage->doDelete($this->storageId);
$this->content = array();
$this->isDirty = false;
return true;
}
/**
* Will persist all previously made changes if there were any.
*
* @param int $lifeTime The cache lifetime in seconds.
* If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime).
*/
public function persistCacheIfNeeded($lifeTime)
{
if ($this->isDirty) {
$this->storage->doSave($this->storageId, $this->content, $lifeTime);
}
}
}

View file

@ -0,0 +1,121 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache;
use Piwik\Cache\Backend;
class Lazy implements Cache
{
private $backend;
/**
* Initializes the cache.
*
* @param Backend $backend Any backend that should be used to store / hold the cache entries.
*/
public function __construct(Backend $backend)
{
$this->backend = $backend;
}
/**
* Fetches an entry from the cache.
*
* @param string $id The cache id.
* @return mixed The cached data or FALSE, if no cache entry exists for the given id.
*/
public function fetch($id)
{
$id = $this->getCompletedCacheIdIfValid($id);
return $this->backend->doFetch($id);
}
/**
* {@inheritdoc}
*/
public function contains($id)
{
$id = $this->getCompletedCacheIdIfValid($id);
return $this->backend->doContains($id);
}
/**
* Puts data into the cache.
*
* @param string $id The cache id.
* @param mixed $data The cache entry/data.
* @param int $lifeTime The cache lifetime in seconds.
* If != 0, sets a specific lifetime for this cache entry (0 => infinite lifeTime).
*
* @return boolean TRUE if the entry was successfully stored in the cache, FALSE otherwise.
*/
public function save($id, $data, $lifeTime = 0)
{
$id = $this->getCompletedCacheIdIfValid($id);
if (is_object($data)) {
throw new \InvalidArgumentException('You cannot use this cache to cache an object, only arrays, strings and numbers. Have a look at Transient cache.');
// for performance reasons we do currently not recursively search whether any array contains an object.
}
return $this->backend->doSave($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
public function delete($id)
{
$id = $this->getCompletedCacheIdIfValid($id);
return $this->backend->doDelete($id);
}
/**
* {@inheritdoc}
*/
public function flushAll()
{
return $this->backend->doFlush();
}
private function getCompletedCacheIdIfValid($id)
{
$this->checkId($id);
return 'piwikcache_' . $id;
}
private function checkId($id)
{
if (empty($id)) {
throw new \InvalidArgumentException('Empty cache id given');
}
if (!$this->isValidId($id)) {
throw new \InvalidArgumentException("Invalid cache id request $id");
}
}
/**
* Returns true if the string is a valid id.
*
* Id that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), and dot(.) will be accepted.
* Id beginning with anything but a-Z or 0-9 will be rejected (including .htaccess for example).
* Id containing anything other than above mentioned will also be rejected (file names with spaces won't be accepted).
*
* @param string $id
* @return bool
*/
private function isValidId($id)
{
return (0 !== preg_match('/(^[a-zA-Z0-9]+([a-zA-Z_0-9.-]*))$/D', $id));
}
}

View file

@ -0,0 +1,89 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL v3 or later
*
*/
namespace Piwik\Cache;
use Piwik\Cache\Backend;
/**
* This class is used to cache data during one request.
*
* Compared to the lazy cache it does not support setting any lifetime. To be a fast cache it does
* not validate any cache id etc.
*/
class Transient implements Cache
{
/**
* @var array $data
*/
private $data = array();
/**
* Fetches an entry from the cache.
*
* Make sure to call the method {@link has()} to verify whether there is actually any content set under this
* cache id.
*
* @param string $id The cache id.
* @return mixed
*/
public function fetch($id)
{
if ($this->contains($id)) {
return $this->data[$id];
}
return false;
}
/**
* {@inheritdoc}
*/
public function contains($id)
{
return isset($this->data[$id]) || array_key_exists($id, $this->data);
}
/**
* Puts data into the cache.
*
* @param string $id The cache id.
* @param mixed $content
* @param int $lifeTime Setting a lifetime is not supported by this cache and the parameter will be ignored.
* @return boolean
*/
public function save($id, $content, $lifeTime = 0)
{
$this->data[$id] = $content;
return true;
}
/**
* {@inheritdoc}
*/
public function delete($id)
{
if (!$this->contains($id)) {
return false;
}
unset($this->data[$id]);
return true;
}
/**
* {@inheritdoc}
*/
public function flushAll()
{
$this->data = array();
return true;
}
}