[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
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\HttpFoundation\Session\Storage\Handler; 13 14 @trigger_error('The '.__NAMESPACE__.'\LegacyPdoSessionHandler class is deprecated since Symfony 2.6 and will be removed in 3.0. Use the Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler class instead.', E_USER_DEPRECATED); 15 16 /** 17 * Session handler using a PDO connection to read and write data. 18 * 19 * Session data is a binary string that can contain non-printable characters like the null byte. 20 * For this reason this handler base64 encodes the data to be able to save it in a character column. 21 * 22 * This version of the PdoSessionHandler does NOT implement locking. So concurrent requests to the 23 * same session can result in data loss due to race conditions. 24 * 25 * @author Fabien Potencier <fabien@symfony.com> 26 * @author Michael Williams <michael.williams@funsational.com> 27 * @author Tobias Schultze <http://tobion.de> 28 * 29 * @deprecated since version 2.6, to be removed in 3.0. Use 30 * {@link PdoSessionHandler} instead. 31 */ 32 class LegacyPdoSessionHandler implements \SessionHandlerInterface 33 { 34 private $pdo; 35 36 /** 37 * @var string Table name 38 */ 39 private $table; 40 41 /** 42 * @var string Column for session id 43 */ 44 private $idCol; 45 46 /** 47 * @var string Column for session data 48 */ 49 private $dataCol; 50 51 /** 52 * @var string Column for timestamp 53 */ 54 private $timeCol; 55 56 /** 57 * Constructor. 58 * 59 * List of available options: 60 * * db_table: The name of the table [required] 61 * * db_id_col: The column where to store the session id [default: sess_id] 62 * * db_data_col: The column where to store the session data [default: sess_data] 63 * * db_time_col: The column where to store the timestamp [default: sess_time] 64 * 65 * @param \PDO $pdo A \PDO instance 66 * @param array $dbOptions An associative array of DB options 67 * 68 * @throws \InvalidArgumentException When "db_table" option is not provided 69 */ 70 public function __construct(\PDO $pdo, array $dbOptions = array()) 71 { 72 if (!array_key_exists('db_table', $dbOptions)) { 73 throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.'); 74 } 75 if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) { 76 throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__)); 77 } 78 79 $this->pdo = $pdo; 80 $dbOptions = array_merge(array( 81 'db_id_col' => 'sess_id', 82 'db_data_col' => 'sess_data', 83 'db_time_col' => 'sess_time', 84 ), $dbOptions); 85 86 $this->table = $dbOptions['db_table']; 87 $this->idCol = $dbOptions['db_id_col']; 88 $this->dataCol = $dbOptions['db_data_col']; 89 $this->timeCol = $dbOptions['db_time_col']; 90 } 91 92 /** 93 * {@inheritdoc} 94 */ 95 public function open($savePath, $sessionName) 96 { 97 return true; 98 } 99 100 /** 101 * {@inheritdoc} 102 */ 103 public function close() 104 { 105 return true; 106 } 107 108 /** 109 * {@inheritdoc} 110 */ 111 public function destroy($sessionId) 112 { 113 // delete the record associated with this id 114 $sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; 115 116 try { 117 $stmt = $this->pdo->prepare($sql); 118 $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); 119 $stmt->execute(); 120 } catch (\PDOException $e) { 121 throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e); 122 } 123 124 return true; 125 } 126 127 /** 128 * {@inheritdoc} 129 */ 130 public function gc($maxlifetime) 131 { 132 // delete the session records that have expired 133 $sql = "DELETE FROM $this->table WHERE $this->timeCol < :time"; 134 135 try { 136 $stmt = $this->pdo->prepare($sql); 137 $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT); 138 $stmt->execute(); 139 } catch (\PDOException $e) { 140 throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e); 141 } 142 143 return true; 144 } 145 146 /** 147 * {@inheritdoc} 148 */ 149 public function read($sessionId) 150 { 151 $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id"; 152 153 try { 154 $stmt = $this->pdo->prepare($sql); 155 $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); 156 $stmt->execute(); 157 158 // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed 159 $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); 160 161 if ($sessionRows) { 162 return base64_decode($sessionRows[0][0]); 163 } 164 165 return ''; 166 } catch (\PDOException $e) { 167 throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); 168 } 169 } 170 171 /** 172 * {@inheritdoc} 173 */ 174 public function write($sessionId, $data) 175 { 176 $encoded = base64_encode($data); 177 178 try { 179 // We use a single MERGE SQL query when supported by the database. 180 $mergeSql = $this->getMergeSql(); 181 182 if (null !== $mergeSql) { 183 $mergeStmt = $this->pdo->prepare($mergeSql); 184 $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); 185 $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); 186 $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); 187 $mergeStmt->execute(); 188 189 return true; 190 } 191 192 $updateStmt = $this->pdo->prepare( 193 "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id" 194 ); 195 $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); 196 $updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); 197 $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT); 198 $updateStmt->execute(); 199 200 // When MERGE is not supported, like in Postgres, we have to use this approach that can result in 201 // duplicate key errors when the same session is written simultaneously. We can just catch such an 202 // error and re-execute the update. This is similar to a serializable transaction with retry logic 203 // on serialization failures but without the overhead and without possible false positives due to 204 // longer gap locking. 205 if (!$updateStmt->rowCount()) { 206 try { 207 $insertStmt = $this->pdo->prepare( 208 "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" 209 ); 210 $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR); 211 $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); 212 $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); 213 $insertStmt->execute(); 214 } catch (\PDOException $e) { 215 // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys 216 if (0 === strpos($e->getCode(), '23')) { 217 $updateStmt->execute(); 218 } else { 219 throw $e; 220 } 221 } 222 } 223 } catch (\PDOException $e) { 224 throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); 225 } 226 227 return true; 228 } 229 230 /** 231 * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database. 232 * 233 * @return string|null The SQL string or null when not supported 234 */ 235 private function getMergeSql() 236 { 237 $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME); 238 239 switch ($driver) { 240 case 'mysql': 241 return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". 242 "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)"; 243 case 'oci': 244 // DUAL is Oracle specific dummy table 245 return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ". 246 "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". 247 "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time"; 248 case 'sqlsrv' === $driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='): 249 // MERGE is only available since SQL Server 2008 and must be terminated by semicolon 250 // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx 251 return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ". 252 "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ". 253 "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;"; 254 case 'sqlite': 255 return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"; 256 } 257 } 258 259 /** 260 * Return a PDO instance. 261 * 262 * @return \PDO 263 */ 264 protected function getConnection() 265 { 266 return $this->pdo; 267 } 268 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |