8000 Create OpCache adapter to use OPCache in recent versions of PHP · symfony/symfony@06c84df · GitHub
[go: up one dir, main page]

Skip to content

Commit 06c84df

Browse files
committed
Create OpCache adapter to use OPCache in recent versions of PHP
1 parent 1ceb61e commit 06c84df

File tree

7 files changed

+677
-1
lines changed

7 files changed

+677
-1
lines changed
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Adapter;
13+
14+
use Psr\Cache\CacheItemInterface;
15+
use Psr\Log\LoggerAwareInterface;
16+
use Psr\Log\LoggerAwareTrait;
17+
use Symfony\Component\Cache\CacheItem;
18+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
19+
20+
/**
21+
* Adapter building static PHP files that will be cached by OPCache.
22+
* This Adapter is in read-only if you use AdapterInterface methods.
23+
* You can use the method "store" to build the cache file.
24+
*
25+
* @author Titouan Galopin <galopintitouan@gmail.com>
26+
*/
27+
class OpCacheAdapter implements AdapterInterface, LoggerAwareInterface
28+
{
29+
use LoggerAwareTrait;
30+
31+
private $file;
32+
private $storeSerialized;
33+
private $values;
34+
private $createCacheItem;
35+
36+
public function __construct($file, $storeSerialized = true)
37+
{
38+
if (file_exists($file)) {
39+
if (!is_file($file)) {
40+
throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s', $file));
41+
}
42+
43+
if (!is_writable($file)) {
44+
throw new InvalidArgumentException(sprintf('Cache file is not writable: %s', $file));
45+
}
46+
}
47+
48+
$directory = dirname($< 67ED /span>file);
49+
50+
if (!file_exists($directory)) {
51+
@mkdir($directory, 0777, true);
52+
}
53+
54+
if (!file_exists($directory)) {
55+
throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s', $directory));
56+
}
57+
58+
if (!is_writable($directory)) {
59+
throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s', $directory));
60+
}
61+
62+
$this->file = $file;
63+
$this->storeSerialized = $storeSerialized;
64+
65+
$this->createCacheItem = \Closure::bind(
66+
function ($key, $value, $isHit) {
67+
$item = new CacheItem();
68+
$item->key = $key;
69+
$item->value = $value;
70+
$item->isHit = $isHit;
71+
$item->defaultLifetime = 0;
72+
73+
return $item;
74+
},
75+
$this,
76+
CacheItem::class
77+
);
78+
}
79+
80+
public function store(array $values = array())
81+
{
82+
// On PHP 5.6, OPcache is able to keep statically declared arrays in shared memory,
83+
// but only up to 32767 elements (See https://bugs.php.net/68057).
84+
// On PHP 7.0+, all static arrays are kept in shared memory, statically declared or not.
85+
$static = count($values) > 32767 ? '' : 'static ';
86+
87+
if ($this->storeSerialized) {
88+
foreach ($values as $id => $value) {
89+
$values[$this->validateKey($id)] = serialize($value);
90+
}
91+
}
92+
93+
$exported = var_export($values, true);
94+
95+
$dump = <<<EOF
96+
// This file has been auto-generated by the Symfony Cache Component.
97+
98+
$static\$staticArray = $exported;
99+
100+
\$array =& \$staticArray;
101+
unset(\$staticArray);
102+
103+
return \$array;
104+
EOF;
105+
106+
// Validates that the dumped code is valid (ie. no __set_state())
107+
$this->values = eval($dump);
108+
109+
$dump = "<?php\n\n" . $dump;
110+
111+
$newFile = tempnam(dirname($this->file), 'sf_opcache_adapter');
112+
113+
file_put_contents($newFile, $dump);
114+
115+
if ('\\' === DIRECTORY_SEPARATOR) {
116+
// Windows does not support overwrite during rename
117+
unlink($this->file);
118+
}
119+
120+
rename($newFile, $this->file);
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
public function getItem($key)
127+
{
128+
if (!$isHit = $this->hasItem($key)) {
129+
$value = null;
130+
} elseif ($this->storeSerialized) {
131+
$value = unserialize($this->values[$key]);
132+
} else {
133+
$value = $this->values[$key];
134+
}
135+
136+
$f = $this->createCacheItem;
137+
138+
return $f($key, $value, $isHit);
139+
}
140+
141+
/**
142+
* {@inheritdoc}
143+
*/
144+
public function getItems(array $keys = array())
145+
{
146+
if (null === $this->values) {
147+
$this->initialize();
148+
}
149+
150+
foreach ($keys as $key) {
151+
$this->validateKey($key);
152+
}
153+
154+
return $this->generateItems($keys);
155+
}
156+
157+
/**
158+
* {@inheritdoc}
159+
*/
160+
public function hasItem($key)
161+
{
162+
$this->validateKey($key);
163+
164+
if (null === $this->values) {
165+
$this->initialize();
166+
}
167+
168+
return isset($this->values[$key]) || (!$this->storeSerialized && array_key_exists($key, $this->values));
169+
}
170+
171+
/**
172+
* {@inheritdoc}
173+
*/
174+
public function clear()
175+
{
176+
CacheItem::log($this->logger, 'Failed to clear as OpCacheAdapter is read-only');
177+
178+
return false;
179+
}
180+
181+
/**
182+
* {@inheritdoc}
183+
*/
184+
public function deleteItem($key)
185+
{
186+
$this->validateKey($key);
187+
188+
CacheItem::log($this->logger, 'Failed to delete as OpCacheAdapter is read-only');
189+
190+
return false;
191+
}
192+
193+
/**
194+
* {@inheritdoc}
195+
*/
196+
public function deleteItems(array $keys)
197+
{
198+
$this->validateKey($keys);
199+
200+
CacheItem::log($this->logger, 'Failed to delete as OpCacheAdapter is read-only');
201+
202+
return false;
203+
}
204+
205+
/**
206+
* {@inheritdoc}
207+
*/
208+
public function save(CacheItemInterface $item)
209+
{
210+
CacheItem::log($this->logger, 'Failed to save as OpCacheAdapter is read-only');
211+
212+
return false;
213+
}
214+
215+
/**
216+
* {@inheritdoc}
217+
*/
218+
public function saveDeferred(CacheItemInterface $item)
219+
{
220+
CacheItem::log($this->logger, 'Failed to save deferred as OpCacheAdapter is read-only');
221+
222+
return false;
223+
}
224+
225+
/**
226+
* {@inheritdoc}
227+
*/
228+
public function commit()
229+
{
230+
CacheItem::log($this->logger, 'Failed to commit as OpCacheAdapter is read-only');
231+
232+
return false;
233+
}
234+
235+
/**
236+
* Load the cache file.
237+
*/
238+
private function initialize()
239+
{
240+
$this->values = (@include $this->file) ?: array();
241+
}
242+
243+
/**
244+
* Validate a key.
245+
*
246+
* @param string $key
247+
*
248+
* @return string
249+
*/
250+
private function validateKey($key)
251+
{
252+
if (!is_string($key)) {
253+
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', is_object($key) ? get_class($key) : gettype($key)));
254+
}
255+
if (!isset($key[0])) {
256+
throw new InvalidArgumentException('Cache key length must be greater than zero');
257+
}
258+
if (isset($key[strcspn($key, '{}()/\@:')])) {
259+
throw new InvalidArgumentException('Cache key contains reserved characters {}()/\@:');
260+
}
261+
262+
return $key;
263+
}
264+
265+
/**
266+
* Generator for items.
267+
*
268+
* @param array $ids
269+
*
270+
* @return \Generator
271+
*/
272+
private function generateItems(array $ids)
273+
{
274+
$f = $this->createCacheItem;
275+
276+
foreach ($ids as $id) {
277+
if (!$isHit = $this->hasItem($id)) {
278+
$value = null;
279+
} elseif ($this->storeSerialized) {
280+
$value = unserialize($this->values[$id]);
281+
} else {
282+
$value = $this->values[$id];
283+
}
284+
285+
yield $id => $f($id, $value, $isHit);
286+
}
287+
}
288+
}

src/Symfony/Component/Cache/DoctrineProvider.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function __construct(CacheItemPoolInterface $pool)
2525
{
2626
$this->pool = $pool;
2727
}
28-
28+
2929
/**
3030
* {@inheritdoc}
3131
*/

0 commit comments

Comments
 (0)
0