Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

LegacyPdoSessionHandler.php

Zuletzt modifiziert: 09.10.2024, 12:58 - Dateigröße: 10.01 KiB


001  <?php
002   
003  /*
004   * This file is part of the Symfony package.
005   *
006   * (c) Fabien Potencier <fabien@symfony.com>
007   *
008   * For the full copyright and license information, please view the LICENSE
009   * file that was distributed with this source code.
010   */
011   
012  namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
013   
014  @trigger_error('The '.__NAMESPACE__.'\LegacyPdoSessionHandler class is deprecated since version 2.6 and will be removed in 3.0. Use the Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler class instead.', E_USER_DEPRECATED);
015   
016  /**
017   * Session handler using a PDO connection to read and write data.
018   *
019   * Session data is a binary string that can contain non-printable characters like the null byte.
020   * For this reason this handler base64 encodes the data to be able to save it in a character column.
021   *
022   * This version of the PdoSessionHandler does NOT implement locking. So concurrent requests to the
023   * same session can result in data loss due to race conditions.
024   *
025   * @author Fabien Potencier <fabien@symfony.com>
026   * @author Michael Williams <michael.williams@funsational.com>
027   * @author Tobias Schultze <http://tobion.de>
028   *
029   * @deprecated since version 2.6, to be removed in 3.0. Use
030   *             {@link PdoSessionHandler} instead.
031   */
032  class LegacyPdoSessionHandler implements \SessionHandlerInterface
033  {
034      /**
035       * @var \PDO PDO instance
036       */
037      private $pdo;
038   
039      /**
040       * @var string Table name
041       */
042      private $table;
043   
044      /**
045       * @var string Column for session id
046       */
047      private $idCol;
048   
049      /**
050       * @var string Column for session data
051       */
052      private $dataCol;
053   
054      /**
055       * @var string Column for timestamp
056       */
057      private $timeCol;
058   
059      /**
060       * Constructor.
061       *
062       * List of available options:
063       *  * db_table: The name of the table [required]
064       *  * db_id_col: The column where to store the session id [default: sess_id]
065       *  * db_data_col: The column where to store the session data [default: sess_data]
066       *  * db_time_col: The column where to store the timestamp [default: sess_time]
067       *
068       * @param \PDO  $pdo       A \PDO instance
069       * @param array $dbOptions An associative array of DB options
070       *
071       * @throws \InvalidArgumentException When "db_table" option is not provided
072       */
073      public function __construct(\PDO $pdo, array $dbOptions = array())
074      {
075          if (!array_key_exists('db_table', $dbOptions)) {
076              throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
077          }
078          if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) {
079              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__));
080          }
081   
082          $this->pdo = $pdo;
083          $dbOptions = array_merge(array(
084              'db_id_col' => 'sess_id',
085              'db_data_col' => 'sess_data',
086              'db_time_col' => 'sess_time',
087          ), $dbOptions);
088   
089          $this->table = $dbOptions['db_table'];
090          $this->idCol = $dbOptions['db_id_col'];
091          $this->dataCol = $dbOptions['db_data_col'];
092          $this->timeCol = $dbOptions['db_time_col'];
093      }
094   
095      /**
096       * {@inheritdoc}
097       */
098      public function open($savePath, $sessionName)
099      {
100          return true;
101      }
102   
103      /**
104       * {@inheritdoc}
105       */
106      public function close()
107      {
108          return true;
109      }
110   
111      /**
112       * {@inheritdoc}
113       */
114      public function destroy($sessionId)
115      {
116          // delete the record associated with this id
117          $sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
118   
119          try {
120              $stmt = $this->pdo->prepare($sql);
121              $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
122              $stmt->execute();
123          } catch (\PDOException $e) {
124              throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e);
125          }
126   
127          return true;
128      }
129   
130      /**
131       * {@inheritdoc}
132       */
133      public function gc($maxlifetime)
134      {
135          // delete the session records that have expired
136          $sql = "DELETE FROM $this->table WHERE $this->timeCol < :time";
137   
138          try {
139              $stmt = $this->pdo->prepare($sql);
140              $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
141              $stmt->execute();
142          } catch (\PDOException $e) {
143              throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e);
144          }
145   
146          return true;
147      }
148   
149      /**
150       * {@inheritdoc}
151       */
152      public function read($sessionId)
153      {
154          $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id";
155   
156          try {
157              $stmt = $this->pdo->prepare($sql);
158              $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
159              $stmt->execute();
160   
161              // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed
162              $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
163   
164              if ($sessionRows) {
165                  return base64_decode($sessionRows[0][0]);
166              }
167   
168              return '';
169          } catch (\PDOException $e) {
170              throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
171          }
172      }
173   
174      /**
175       * {@inheritdoc}
176       */
177      public function write($sessionId, $data)
178      {
179          $encoded = base64_encode($data);
180   
181          try {
182              // We use a single MERGE SQL query when supported by the database.
183              $mergeSql = $this->getMergeSql();
184   
185              if (null !== $mergeSql) {
186                  $mergeStmt = $this->pdo->prepare($mergeSql);
187                  $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
188                  $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
189                  $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
190                  $mergeStmt->execute();
191   
192                  return true;
193              }
194   
195              $updateStmt = $this->pdo->prepare(
196                  "UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id"
197              );
198              $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
199              $updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
200              $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
201              $updateStmt->execute();
202   
203              // When MERGE is not supported, like in Postgres, we have to use this approach that can result in
204              // duplicate key errors when the same session is written simultaneously. We can just catch such an
205              // error and re-execute the update. This is similar to a serializable transaction with retry logic
206              // on serialization failures but without the overhead and without possible false positives due to
207              // longer gap locking.
208              if (!$updateStmt->rowCount()) {
209                  try {
210                      $insertStmt = $this->pdo->prepare(
211                          "INSERT INTO $this->table ($this->idCol$this->dataCol$this->timeCol) VALUES (:id, :data, :time)"
212                      );
213                      $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
214                      $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
215                      $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
216                      $insertStmt->execute();
217                  } catch (\PDOException $e) {
218                      // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
219                      if (0 === strpos($e->getCode(), '23')) {
220                          $updateStmt->execute();
221                      } else {
222                          throw $e;
223                      }
224                  }
225              }
226          } catch (\PDOException $e) {
227              throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
228          }
229   
230          return true;
231      }
232   
233      /**
234       * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database.
235       *
236       * @return string|null The SQL string or null when not supported
237       */
238      private function getMergeSql()
239      {
240          $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
241   
242          switch ($driver) {
243              case 'mysql':
244                  return "INSERT INTO $this->table ($this->idCol$this->dataCol$this->timeCol) VALUES (:id, :data, :time) ".
245                  "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
246              case 'oci':
247                  // DUAL is Oracle specific dummy table
248                  return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) ".
249                  "WHEN NOT MATCHED THEN INSERT ($this->idCol$this->dataCol$this->timeCol) VALUES (:id, :data, :time) ".
250                  "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time";
251              case 'sqlsrv' === $driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
252                  // MERGE is only available since SQL Server 2008 and must be terminated by semicolon
253                  // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
254                  return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = :id) ".
255                  "WHEN NOT MATCHED THEN INSERT ($this->idCol$this->dataCol$this->timeCol) VALUES (:id, :data, :time) ".
256                  "WHEN MATCHED THEN UPDATE SET $this->dataCol = :data, $this->timeCol = :time;";
257              case 'sqlite':
258                  return "INSERT OR REPLACE INTO $this->table ($this->idCol$this->dataCol$this->timeCol) VALUES (:id, :data, :time)";
259          }
260      }
261   
262      /**
263       * Return a PDO instance.
264       *
265       * @return \PDO
266       */
267      protected function getConnection()
268      {
269          return $this->pdo;
270      }
271  }
272