Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit edf1bf7

Browse filesBrowse files
committed
Create OpCache adapter to use OPCache in recent versions of PHP
1 parent 1ceb61e commit edf1bf7
Copy full SHA for edf1bf7

File tree

3 files changed

+360
-0
lines changed
Filter options

3 files changed

+360
-0
lines changed
+173Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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 Symfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
/**
17+
* Adapter building static PHP files that will be cached by OPCache.
18+
* This Adapter is in read-only if you use AdapterInterface methods.
19+
* You can use the method "warmUp" to build the cache file.
20+
*
21+
* @author Titouan Galopin <galopintitouan@gmail.com>
22+
*/
23+
class OpCacheAdapter extends AbstractAdapter
24+
{
25+
private $file;
26+
private $storeSerialized;
27+
private $loaded;
28+
private $values;
29+
30+
public function __construct($file, $namespace = '', $defaultLifetime = 0, $storeSerialized = true)
31+
{
32+
parent::__construct($namespace, $defaultLifetime);
33+
34+
$directory = dirname($file);
35+
36+
if (!file_exists($directory)) {
37+
@mkdir($directory, 0777, true);
38+
}
39+
40+
if (!file_exists($directory)) {
41+
throw new InvalidArgumentException(sprintf(
42+
'Cache directory does not exist and cannot be created (%s)',
43+
$directory
44+
));
45+
}
46+
47+
if (!is_writable($directory)) {
48+
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
49+
}
50+
51+
$this->file = $file;
52+
$this->storeSerialized = $storeSerialized;
53+
$this->loaded = false;
54+
$this->values = [];
55+
}
56+
57+
public function warmUp(array $values = [])
58+
{
59+
// On PHP 5.6, OPcache is able to keep statically declared arrays in shared memory,
60+
// but only up to 32767 elements (See https://bugs.php.net/68057).
61+
// On PHP 7.0+, all static arrays are kept in shared memory, statically declared or not.
62+
$static = count($values) > 32767 ? '' : 'static ';
63+
64+
if ($this->storeSerialized) {
65+
foreach ($values as $id => $value) {
66+
$values[$id] = serialize($value);
67+
}
68+
}
69+
70+
$exported = var_export($values, true);
71+
72+
$dump = <<<EOF
73+
<?php
74+
75+
/**
76+
* This file has been auto-generated
77+
* by the Symfony Cache Component.
78+
*/
79+
80+
$static\$staticArray = $exported;
81+
82+
\$array =& \$staticArray;
83+
unset(\$staticArray);
84+
85+
return \$array;
86+
EOF;
87+
88+
file_put_contents($this->file, $dump);
89+
90+
$this->loaded = false;
91+
$this->loadFile();
92+
}
93+
94+
/**
95+
* {@inheritdoc}
96+
*/
97+
protected function doFetch(array $ids)
98+
{
99+
$this->loadFile();
100+
101+
$values = [];
102+
103+
foreach ($ids as $id) {
104+
if (! $this->doHave($id)) {
105+
continue;
106+
}
107+
108+
if ($this->storeSerialized) {
109+
$values[$id] = unserialize($this->values[$id]);
110+
} else {
111+
$values[$id] = $this->values[$id];
112+
}
113+
}
114+
115+
return $values;
116+
}
117+
118+
/**
119+
* {@inheritdoc}
120+
*/
121+
protected function doHave($id)
122+
{
123+
$this->loadFile();
124+
125+
return isset($this->values[$id]);
126+
}
127+
128+
/**
129+
* {@inheritdoc}
130+
*/
131+
protected function doClear($namespace)
132+
{
133+
// This Adapter is read-only
134+
return false;
135+
}
136+
137+
/**
138+
* {@inheritdoc}
139+
*/
140+
protected function doDelete(array $ids)
141+
{
142+
// This Adapter is read-only
143+
return false;
144+
}
145+
146+
/**
147+
* {@inheritdoc}
148+
*/
149+
protected function doSave(array $values, $lifetime)
150+
{
151+
// This Adapter is read-only
152+
return false;
153+
}
154+
155+
/**
156+
* Load the cache file.
157+
*/
158+
private function loadFile()
159+
{
160+
if ($this->loaded) {
161+
return;
162+
}
163+
164+
$this->loaded = true;
165+
166+
if (!file_exists($this->file)) {
167+
$this->values = [];
168+
return;
169+
}
170+
171+
$this->values = require $this->file;
172+
}
173+
}
+158Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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\Tests\Adapter;
13+
14+
use Cache\IntegrationTests\CachePoolTest;
15+
use Psr\Cache\CacheItemInterface;
16+
use Symfony\Component\Cache\Adapter\OpCacheAdapter;
17+
18+
/**
19+
* @group time-sensitive
20+
*/
21+
class OpCacheNotSerializedAdapterTest extends CachePoolTest
22+
{
23+
private $filename;
24+
25+
protected $skippedTests = array(
26+
'testBasicUsage' => 'OpCacheAdpater is read-only.',
27+
'testClear' => 'OpCacheAdpater is read-only.',
28+
'testClearWithDeferredItems' => 'OpCacheAdpater is read-only.',
29+
'testDeleteItem' => 'OpCacheAdpater is read-only.',
30+
'testSave' => 'OpCacheAdpater is read-only.',
31+
'testSaveExpired' => 'OpCacheAdpater is read-only.',
32+
'testSaveWithoutExpire' => 'OpCacheAdpater is read-only.',
33+
'testDeferredSave' => 'OpCacheAdpater is read-only.',
34+
'testDeferredSaveWithoutCommit' => 'OpCacheAdpater is read-only.',
35+
'testDeleteItems' => 'OpCacheAdpater is read-only.',
36+
'testDeleteDeferredItem' => 'OpCacheAdpater is read-only.',
37+
'testCommit' => 'OpCacheAdpater is read-only.',
38+
'testSaveDeferredWhenChangingValues' => 'OpCacheAdpater is read-only.',
39+
'testSaveDeferredOverwrite' => 'OpCacheAdpater is read-only.',
40+
'testSavingObject' => 'OpCacheAdpater is read-only.',
41+
42+
'testDataTypeObject' => 'OpCacheAdpater without serialization does not support objects.',
43+
44+
'testExpiresAt' => 'OpCacheAdpater does not support expiration.',
45+
'testExpiresAtWithNull' => 'OpCacheAdpater does not support expiration.',
46+
'testExpiresAfterWithNull' => 'OpCacheAdpater does not support expiration.',
47+
'testDeferredExpired' => 'OpCacheAdpater does not support expiration.',
48+
'testExpiration' => 'OpCacheAdpater does not support expiration.',
49+
50+
// todo
51+
'testIsHit' => 'OpCacheAdpater is read-only.',
52+
'testIsHitDeferred' => 'OpCacheAdpater is read-only.',
53+
'testDataTypeString' => 'OpCacheAdpater is read-only.',
54+
'testDataTypeInteger' => 'OpCacheAdpater is read-only.',
55+
'testDataTypeNull' => 'OpCacheAdpater is read-only.',
56+
'testDataTypeFloat' => 'OpCacheAdpater is read-only.',
57+
'testDataTypeBoolean' => 'OpCacheAdpater is read-only.',
58+
'testDataTypeArray' => 'OpCacheAdpater is read-only.',
59+
);
60+
61+
public function createCachePool()
62+
{
63+
$this->filename = sys_get_temp_dir() . '/symfony-cache/OpCache/not_serialized.php';
64+
65+
return new OpCacheAdapter($this->filename, '', 0, false);
66+
}
67+
68+
public function testWarmUp()
69+
{
70+
$values = [
71+
'string' => 'string',
72+
'integer' => 42,
73+
'null' => null,
74+
'float' => 42.42,
75+
'boolean' => true,
76+
'array_simple' => [ 'foo', 'bar' ],
77+
'array_associative' => [ 'foo' => 'bar', 'foo2' => 'bar2' ],
78+
];
79+
80+
$adapter = $this->createCachePool();
81+
$adapter->warmUp($values);
82+
83+
$this->assertEquals(
84+
file_get_contents(__DIR__ . '/../Fixtures/OpCache/not_serialized.php'),
85+
file_get_contents($this->filename),
86+
'Warm up should create a PHP file that OPCache can load in memory'
87+
);
88+
}
89+
90+
public function testGetItem()
91+
{
92+
$adapter = $this->createCachePool();
93+
$adapter->warmUp([ 'key' => 'value' ]);
94+
95+
// get existing item
96+
$item = $adapter->getItem('key');
97+
$this->assertEquals('value', $item->get(), 'A stored item must be returned from cached.');
98+
$this->assertEquals('key', $item->getKey(), 'Cache key can not change.');
99+
100+
// get non-existent item
101+
$item = $adapter->getItem('key2');
102+
$this->assertFalse($item->isHit());
103+
$this->assertNull($item->get(), "Item's value must be null when isHit is false.");
104+
}
105+
106+
public function testGetItems()
107+
{
108+
$adapter = $this->createCachePool();
109+
$adapter->warmUp([ 'foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz' ]);
110+
111+
$keys = ['foo', 'bar', 'baz', 'biz'];
112+
113+
/** @type CacheItemInterface[] $items */
114+
$items = $adapter->getItems($keys);
115+
$count = 0;
116+
117+
foreach ($items as $key => $item) {
118+
$itemKey = $item->getKey();
119+
120+
$this->assertEquals($itemKey, $key, 'Keys must be preserved when fetching multiple items');
121+
$this->assertEquals($key !== 'biz', $item->isHit());
122+
$this->assertTrue(in_array($key, $keys), 'Cache key can not change.');
123+
124+
// Remove $key for $keys
125+
foreach ($keys as $k => $v) {
126+
if ($v === $key) {
127+
unset($keys[$k]);
128+
}
129+
}
130+
131+
$count++;
132+
}
133+
134+
$this->assertSame(4, $count);
135+
}
136+
137+
public function testHasItem()
138+
{
139+
$adapter = $this->createCachePool();
140+
$adapter->warmUp([ 'key' => 'foo' ]);
141+
142+
// has existing item
143+
$this->assertTrue($adapter->hasItem('key'));
144+
145+
// has non-existent item
146+
$this->assertFalse($adapter->hasItem('key2'));
147+
}
148+
149+
public function testKeyLength()
150+
{
151+
$key = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.';
152+
153+
$adapter = $this->createCachePool();
154+
$adapter->warmUp([ $key => 'value' ]);
155+
156+
$this->assertTrue($adapter->hasItem($key), 'The implementation does not support a valid cache key');
157+
}
158+
}
+29Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/**
4+
* This file has been auto-generated
5+
* by the Symfony Cache Component.
6+
*/
7+
8+
static $staticArray = array (
9+
'string' => 'string',
10+
'integer' => 42,
11+
'null' => NULL,
12+
'float' => 42.420000000000002,
13+
'boolean' => true,
14+
'array_simple' =>
15+
array (
16+
0 => 'foo',
17+
1 => 'bar',
18+
),
19+
'array_associative' =>
20+
array (
21+
'foo' => 'bar',
22+
'foo2' => 'bar2',
23+
),
24+
);
25+
26+
$array =& $staticArray;
27+
unset($staticArray);
28+
29+
return $array;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.