Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commitbb2dba9

Browse files
[Cache] Add PDO adapter + tag aware adapter
1 parente408b50 commitbb2dba9

File tree

7 files changed

+677
-0
lines changed

7 files changed

+677
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
namespaceSymfony\Component\Cache\Adapter;
13+
14+
class PdoAdapterextends AbstractAdapter
15+
{
16+
use PdoAdapterTrait;
17+
18+
/**
19+
* Constructor.
20+
*
21+
* You can either pass an existing database connection as PDO instance or
22+
* pass a DSN string that will be used to lazy-connect to the database
23+
* when the cache is actually used.
24+
*
25+
* List of available options:
26+
* * db_table: The name of the table [default: cache_items]
27+
* * db_id_col: The column where to store the cache id [default: item_id]
28+
* * db_data_col: The column where to store the cache data [default: item_data]
29+
* * db_expiry_col: The column where to store the expiration timestamp [default: item_expiry]
30+
* * db_username: The username when lazy-connect [default: '']
31+
* * db_password: The password when lazy-connect [default: '']
32+
* * db_connection_options: An array of driver-specific connection options [default: array()]
33+
*
34+
* @param \PDO|string $pdoOrDsn A \PDO instance or DSN string or null
35+
* @param string $namespace
36+
* @param int $defaultLifetime
37+
* @param array $options An associative array of options
38+
*
39+
* @throws InvalidArgumentException When first argument is not PDO nor string
40+
* @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
41+
* @throws InvalidArgumentException When namespace contains invalid characters
42+
*/
43+
publicfunction__construct($pdoOrDsn,$namespace ='',$defaultLifetime =0,array$options =array())
44+
{
45+
$this->init($pdoOrDsn,$namespace,$defaultLifetime,$options);
46+
47+
parent::__construct($namespace,$defaultLifetime);
48+
}
49+
}
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
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+
namespaceSymfony\Component\Cache\Adapter;
13+
14+
useSymfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @internal
18+
*
19+
* @author Nicolas Grekas <p@tchwork.com>
20+
*/
21+
trait PdoAdapterTrait
22+
{
23+
/**
24+
* @var \PDO|null PDO instance or null when not connected yet
25+
*/
26+
private$pdo;
27+
28+
/**
29+
* @var string|null DSN string or null when lazy connection disabled
30+
*/
31+
private$dsn;
32+
33+
/**
34+
* @var string Database driver
35+
*/
36+
private$driver;
37+
38+
/**
39+
* @var string Table name
40+
*/
41+
private$table ='cache_items';
42+
43+
/**
44+
* @var string Column for item id
45+
*/
46+
private$idCol ='item_id';
47+
48+
/**
49+
* @var string Column for item data
50+
*/
51+
private$dataCol ='item_data';
52+
53+
/**
54+
* @var string Column for expiration timestamp
55+
*/
56+
private$expiryCol ='item_expiry';
57+
58+
/**
59+
* @var string Username when lazy-connect
60+
*/
61+
private$username ='';
62+
63+
/**
64+
* @var string Password when lazy-connect
65+
*/
66+
private$password ='';
67+
68+
/**
69+
* @var array Connection options when lazy-connect
70+
*/
71+
private$connectionOptions =array();
72+
73+
privatefunctioninit($pdoOrDsn,$namespace,$defaultLifetime,array$options)
74+
{
75+
if (isset($namespace[0]) &&preg_match('#[^-+.A-Za-z0-9]#',$namespace,$match)) {
76+
thrownewInvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.',$match[0]));
77+
}
78+
79+
if ($pdoOrDsninstanceof \PDO) {
80+
if (\PDO::ERRMODE_EXCEPTION !==$pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
81+
thrownewInvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))',__CLASS__));
82+
}
83+
84+
$this->pdo =$pdoOrDsn;
85+
$this->driver =$this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
86+
}elseif (is_string($pdoOrDsn)) {
87+
$this->dsn =$pdoOrDsn;
88+
}else {
89+
thrownewInvalidArgumentException(sprintf('"%s" requires PDO instance or DSN string as first argument, "%s" given.',__CLASS__,is_object($pdoOrDsn) ?get_class($pdoOrDsn) :gettype($pdoOrDsn)));
90+
}
91+
92+
$this->table =isset($options['db_table']) ?$options['db_table'] :$this->table;
93+
$this->idCol =isset($options['db_id_col']) ?$options['db_id_col'] :$this->idCol;
94+
$this->dataCol =isset($options['db_data_col']) ?$options['db_data_col'] :$this->dataCol;
95+
$this->expiryCol =isset($options['db_expiry_col']) ?$options['db_expiry_col'] :$this->expiryCol;
96+
$this->username =isset($options['db_username']) ?$options['db_username'] :$this->username;
97+
$this->password =isset($options['db_password']) ?$options['db_password'] :$this->password;
98+
$this->connectionOptions =isset($options['db_connection_options']) ?$options['db_connection_options'] :$this->connectionOptions;
99+
}
100+
101+
/**
102+
* Creates the table to store cache items which can be called once for setup.
103+
*
104+
* Cache ID are saved in a column of maximum length 256. Cache data is
105+
* saved in a BLOB.
106+
*
107+
* @throws \PDOException When the table already exists
108+
* @throws \DomainException When an unsupported PDO driver is used
109+
*/
110+
publicfunctioncreateTable()
111+
{
112+
// connect if we are not yet
113+
$this->getConnection();
114+
115+
switch ($this->driver) {
116+
case'mysql':
117+
// We use varbinary for the ID column because it prevents unwanted conversions:
118+
// - character set conversions between server and client
119+
// - trailing space removal
120+
// - case-insensitivity
121+
// - language processing like é == e
122+
$sql ="CREATE TABLE$this->table ($this->idCol VARBINARY(256) NOT NULL PRIMARY KEY,$this->dataCol BLOB NOT NULL,$this->expiryCol INTEGER UNSIGNED) COLLATE utf8_bin, ENGINE = InnoDB";
123+
break;
124+
case'sqlite':
125+
$sql ="CREATE TABLE$this->table ($this->idCol TEXT NOT NULL PRIMARY KEY,$this->dataCol BLOB NOT NULL,$this->expiryCol INTEGER)";
126+
break;
127+
case'pgsql':
128+
$sql ="CREATE TABLE$this->table ($this->idCol VARCHAR(256) NOT NULL PRIMARY KEY,$this->dataCol BYTEA NOT NULL,$this->expiryCol INTEGER)";
129+
break;
130+
case'oci':
131+
$sql ="CREATE TABLE$this->table ($this->idCol VARCHAR2(256) NOT NULL PRIMARY KEY,$this->dataCol BLOB NOT NULL,$this->expiryCol INTEGER)";
132+
break;
133+
case'sqlsrv':
134+
$sql ="CREATE TABLE$this->table ($this->idCol VARCHAR(256) NOT NULL PRIMARY KEY,$this->dataCol VARBINARY(MAX) NOT NULL,$this->expiryCol INTEGER)";
135+
break;
136+
default:
137+
thrownew \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".',$this->driver));
138+
}
139+
140+
$this->pdo->exec($sql);
141+
}
142+
143+
publicfunction__destruct()
144+
{
145+
parent::__destruct();
146+
147+
if (null !==$this->dsn) {
148+
// only close lazy-connection
149+
$this->pdo =null;
150+
}
151+
}
152+
153+
/**
154+
* {@inheritdoc}
155+
*/
156+
protectedfunctiondoFetch(array$ids)
157+
{
158+
$now =time();
159+
$values =array();
160+
$expired =array();
161+
162+
$sql =str_pad('', (count($ids) <<1) -1,'?,');
163+
$sql ="SELECT$this->idCol, CASE WHEN$this->expiryCol IS NULL OR$this->expiryCol > ? THEN$this->dataCol ELSE NULL END FROM$this->table WHERE$this->idCol IN ($sql)";
164+
$stmt =$this->getConnection()->prepare($sql);
165+
$stmt->bindValue($i =1,$now, \PDO::PARAM_INT);
166+
foreach ($idsas$id) {
167+
$stmt->bindValue(++$i,$id);
168+
}
169+
$stmt->execute();
170+
171+
while ($row =$stmt->fetch(\PDO::FETCH_NUM)) {
172+
if (null ===$row[1]) {
173+
$expired[] =$row[0];
174+
}else {
175+
$values[$row[0]] =$row[1];
176+
}
177+
}
178+
179+
if ($expired) {
180+
$sql =str_pad('', (count($expired) <<1) -1,'?,');
181+
$sql ="DELETE FROM$this->table WHERE$this->expiryCol <= ? AND$this->idCol IN ($sql)";
182+
$stmt =$this->getConnection()->prepare($sql);
183+
$stmt->bindValue($i =1,$now, \PDO::PARAM_INT);
184+
foreach ($expiredas$id) {
185+
$stmt->bindValue(++$i,$id);
186+
}
187+
$stmt->execute($expired);
188+
}
189+
190+
returnarray_map('unserialize',$values);
191+
}
192+
193+
/**
194+
* {@inheritdoc}
195+
*/
196+
protectedfunctiondoHave($id)
197+
{
198+
$sql ="SELECT 1 FROM$this->table WHERE$this->idCol = :id AND ($this->expiryCol IS NULL OR$this->expiryCol > :time)";
199+
$stmt =$this->getConnection()->prepare($sql);
200+
201+
$stmt->bindValue(':id',$id);
202+
$stmt->bindValue(':time',time(), \PDO::PARAM_INT);
203+
$stmt->execute();
204+
205+
return (bool)$stmt->fetchColumn();
206+
}
207+
208+
/**
209+
* {@inheritdoc}
210+
*/
211+
protectedfunctiondoClear($namespace)
212+
{
213+
if ('' ===$namespace) {
214+
if ('sqlite' ===$this->driver) {
215+
$sql ="DELETE FROM$this->table";
216+
}else {
217+
$sql ="TRUNCATE TABLE$this->table";
218+
}
219+
}else {
220+
$sql ="DELETE FROM$this->table WHERE$this->idCol LIKE '$namespace%'";
221+
}
222+
223+
$this->getConnection()->exec($sql);
224+
225+
returntrue;
226+
}
227+
228+
/**
229+
* {@inheritdoc}
230+
*/
231+
protectedfunctiondoDelete(array$ids)
232+
{
233+
$sql =str_pad('', (count($ids) <<1) -1,'?,');
234+
$sql ="DELETE FROM$this->table WHERE$this->idCol IN ($sql)";
235+
$stmt =$this->getConnection()->prepare($sql);
236+
$stmt->execute(array_values($ids));
237+
238+
returntrue;
239+
}
240+
241+
/**
242+
* {@inheritdoc}
243+
*/
244+
protectedfunctiondoSave(array$values,$lifetime)
245+
{
246+
$serialized =array();
247+
$failed =array();
248+
249+
foreach ($valuesas$id =>$value) {
250+
try {
251+
$serialized[$id] =serialize($value);
252+
}catch (\Exception$e) {
253+
$failed[] =$id;
254+
}
255+
}
256+
257+
if (!$serialized) {
258+
return$failed;
259+
}
260+
261+
$sql =null;
262+
switch (true) {
263+
case'mysql' ===$this->driver:
264+
$sql ="INSERT INTO$this->table ($this->idCol,$this->dataCol,$this->expiryCol) VALUES (:id, :data, :expiry)".
265+
"ON DUPLICATE KEY UPDATE$this->dataCol = VALUES($this->dataCol),$this->expiry = VALUES($this->expiryCol)";
266+
break;
267+
case'oci' ===$this->driver:
268+
// DUAL is Oracle specific dummy table
269+
$sql ="MERGE INTO$this->table USING DUAL ON ($this->idCol = ?)".
270+
"WHEN NOT MATCHED THEN INSERT ($this->idCol,$this->dataCol,$this->expiryCol) VALUES (?, ?, ?)".
271+
"WHEN MATCHED THEN UPDATE SET$this->dataCol = ?,$this->expiryCol = ?";
272+
break;
273+
case'sqlsrv' ===$this->driver &&version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION),'10','>='):
274+
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
275+
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
276+
$sql ="MERGE INTO$this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?)".
277+
"WHEN NOT MATCHED THEN INSERT ($this->idCol,$this->dataCol,$this->expiryCol) VALUES (?, ?, ?)".
278+
"WHEN MATCHED THEN UPDATE SET$this->dataCol = ?,$this->expiryCol = ?;";
279+
break;
280+
case'sqlite' ===$this->driver:
281+
$sql ="INSERT OR REPLACE INTO$this->table ($this->idCol,$this->dataCol,$this->expiryCol) VALUES (:id, :data, :expiry)";
282+
break;
283+
case'pgsql' ===$this->driver &&version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION),'9.5','>='):
284+
$sql ="INSERT INTO$this->table ($this->idCol,$this->dataCol,$this->expiryCol) VALUES (:id, :data, :expiry)".
285+
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol,$this->expiryCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->expiryCol)";
286+
break;
287+
}
288+
289+
$expiry =$lifetime ?time() +$lifetime :null;
290+
$stmt =$this->getConnection()->prepare($sql);
291+
292+
if ('sqlsrv' ===$this->driver ||'oci' ===$this->driver) {
293+
$stmt->bindParam(1,$id);
294+
$stmt->bindParam(2,$id);
295+
$stmt->bindParam(3,$data, \PDO::PARAM_LOB);
296+
$stmt->bindValue(4,$expiry, \PDO::PARAM_INT);
297+
$stmt->bindParam(5,$data, \PDO::PARAM_LOB);
298+
$stmt->bindValue(6,$expiry, \PDO::PARAM_INT);
299+
}else {
300+
$stmt->bindParam(':id',$id, \PDO::PARAM_STR);
301+
$stmt->bindParam(':data',$data, \PDO::PARAM_LOB);
302+
$stmt->bindValue(':expiry',$expiry, \PDO::PARAM_INT);
303+
}
304+
305+
foreach ($serializedas$id =>$data) {
306+
$stmt->execute();
307+
}
308+
309+
return$failed;
310+
}
311+
312+
/**
313+
* @return \PDO
314+
*/
315+
privatefunctiongetConnection()
316+
{
317+
if (null ===$this->pdo) {
318+
$this->pdo =new \PDO($this->dsn,$this->username,$this->password,$this->connectionOptions);
319+
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
320+
$this->driver =$this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
321+
}
322+
323+
return$this->pdo;
324+
}
325+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp