diff options
| author | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
|---|---|---|
| committer | polo <ordipolo@gmx.fr> | 2024-08-13 23:45:21 +0200 |
| commit | bf6655a534a6775d30cafa67bd801276bda1d98d (patch) | |
| tree | c6381e3f6c81c33eab72508f410b165ba05f7e9c /vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php | |
| parent | 94d67a4b51f8e62e7d518cce26a526ae1ec48278 (diff) | |
| download | AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.gz AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.tar.bz2 AppliGestionPHP-bf6655a534a6775d30cafa67bd801276bda1d98d.zip | |
VERSION 0.2 doctrine ORM et entités
Diffstat (limited to 'vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php')
| -rw-r--r-- | vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php b/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php new file mode 100644 index 0000000..ae2bea7 --- /dev/null +++ b/vendor/symfony/cache/Adapter/DoctrineDbalAdapter.php | |||
| @@ -0,0 +1,383 @@ | |||
| 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 Doctrine\DBAL\ArrayParameterType; | ||
| 15 | use Doctrine\DBAL\Configuration; | ||
| 16 | use Doctrine\DBAL\Connection; | ||
| 17 | use Doctrine\DBAL\DriverManager; | ||
| 18 | use Doctrine\DBAL\Exception as DBALException; | ||
| 19 | use Doctrine\DBAL\Exception\TableNotFoundException; | ||
| 20 | use Doctrine\DBAL\ParameterType; | ||
| 21 | use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; | ||
| 22 | use Doctrine\DBAL\Schema\Schema; | ||
| 23 | use Doctrine\DBAL\Tools\DsnParser; | ||
| 24 | use Symfony\Component\Cache\Exception\InvalidArgumentException; | ||
| 25 | use Symfony\Component\Cache\Marshaller\DefaultMarshaller; | ||
| 26 | use Symfony\Component\Cache\Marshaller\MarshallerInterface; | ||
| 27 | use Symfony\Component\Cache\PruneableInterface; | ||
| 28 | |||
| 29 | class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface | ||
| 30 | { | ||
| 31 | private const MAX_KEY_LENGTH = 255; | ||
| 32 | |||
| 33 | private MarshallerInterface $marshaller; | ||
| 34 | private Connection $conn; | ||
| 35 | private string $platformName; | ||
| 36 | private string $table = 'cache_items'; | ||
| 37 | private string $idCol = 'item_id'; | ||
| 38 | private string $dataCol = 'item_data'; | ||
| 39 | private string $lifetimeCol = 'item_lifetime'; | ||
| 40 | private string $timeCol = 'item_time'; | ||
| 41 | |||
| 42 | /** | ||
| 43 | * You can either pass an existing database Doctrine DBAL Connection or | ||
| 44 | * a DSN string that will be used to connect to the database. | ||
| 45 | * | ||
| 46 | * The cache table is created automatically when possible. | ||
| 47 | * Otherwise, use the createTable() method. | ||
| 48 | * | ||
| 49 | * List of available options: | ||
| 50 | * * db_table: The name of the table [default: cache_items] | ||
| 51 | * * db_id_col: The column where to store the cache id [default: item_id] | ||
| 52 | * * db_data_col: The column where to store the cache data [default: item_data] | ||
| 53 | * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] | ||
| 54 | * * db_time_col: The column where to store the timestamp [default: item_time] | ||
| 55 | * | ||
| 56 | * @throws InvalidArgumentException When namespace contains invalid characters | ||
| 57 | */ | ||
| 58 | public function __construct( | ||
| 59 | Connection|string $connOrDsn, | ||
| 60 | private string $namespace = '', | ||
| 61 | int $defaultLifetime = 0, | ||
| 62 | array $options = [], | ||
| 63 | ?MarshallerInterface $marshaller = null, | ||
| 64 | ) { | ||
| 65 | if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { | ||
| 66 | throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); | ||
| 67 | } | ||
| 68 | |||
| 69 | if ($connOrDsn instanceof Connection) { | ||
| 70 | $this->conn = $connOrDsn; | ||
| 71 | } else { | ||
| 72 | if (!class_exists(DriverManager::class)) { | ||
| 73 | throw new InvalidArgumentException('Failed to parse DSN. Try running "composer require doctrine/dbal".'); | ||
| 74 | } | ||
| 75 | $params = (new DsnParser([ | ||
| 76 | 'db2' => 'ibm_db2', | ||
| 77 | 'mssql' => 'pdo_sqlsrv', | ||
| 78 | 'mysql' => 'pdo_mysql', | ||
| 79 | 'mysql2' => 'pdo_mysql', | ||
| 80 | 'postgres' => 'pdo_pgsql', | ||
| 81 | 'postgresql' => 'pdo_pgsql', | ||
| 82 | 'pgsql' => 'pdo_pgsql', | ||
| 83 | 'sqlite' => 'pdo_sqlite', | ||
| 84 | 'sqlite3' => 'pdo_sqlite', | ||
| 85 | ]))->parse($connOrDsn); | ||
| 86 | |||
| 87 | $config = new Configuration(); | ||
| 88 | $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); | ||
| 89 | |||
| 90 | $this->conn = DriverManager::getConnection($params, $config); | ||
| 91 | } | ||
| 92 | |||
| 93 | $this->maxIdLength = self::MAX_KEY_LENGTH; | ||
| 94 | $this->table = $options['db_table'] ?? $this->table; | ||
| 95 | $this->idCol = $options['db_id_col'] ?? $this->idCol; | ||
| 96 | $this->dataCol = $options['db_data_col'] ?? $this->dataCol; | ||
| 97 | $this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol; | ||
| 98 | $this->timeCol = $options['db_time_col'] ?? $this->timeCol; | ||
| 99 | $this->marshaller = $marshaller ?? new DefaultMarshaller(); | ||
| 100 | |||
| 101 | parent::__construct($namespace, $defaultLifetime); | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * Creates the table to store cache items which can be called once for setup. | ||
| 106 | * | ||
| 107 | * Cache ID are saved in a column of maximum length 255. Cache data is | ||
| 108 | * saved in a BLOB. | ||
| 109 | * | ||
| 110 | * @throws DBALException When the table already exists | ||
| 111 | */ | ||
| 112 | public function createTable(): void | ||
| 113 | { | ||
| 114 | $schema = new Schema(); | ||
| 115 | $this->addTableToSchema($schema); | ||
| 116 | |||
| 117 | foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { | ||
| 118 | $this->conn->executeStatement($sql); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | public function configureSchema(Schema $schema, Connection $forConnection, \Closure $isSameDatabase): void | ||
| 123 | { | ||
| 124 | if ($schema->hasTable($this->table)) { | ||
| 125 | return; | ||
| 126 | } | ||
| 127 | |||
| 128 | if ($forConnection !== $this->conn && !$isSameDatabase($this->conn->executeStatement(...))) { | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | |||
| 132 | $this->addTableToSchema($schema); | ||
| 133 | } | ||
| 134 | |||
| 135 | public function prune(): bool | ||
| 136 | { | ||
| 137 | $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ?"; | ||
| 138 | $params = [time()]; | ||
| 139 | $paramTypes = [ParameterType::INTEGER]; | ||
| 140 | |||
| 141 | if ('' !== $this->namespace) { | ||
| 142 | $deleteSql .= " AND $this->idCol LIKE ?"; | ||
| 143 | $params[] = sprintf('%s%%', $this->namespace); | ||
| 144 | $paramTypes[] = ParameterType::STRING; | ||
| 145 | } | ||
| 146 | |||
| 147 | try { | ||
| 148 | $this->conn->executeStatement($deleteSql, $params, $paramTypes); | ||
| 149 | } catch (TableNotFoundException) { | ||
| 150 | } | ||
| 151 | |||
| 152 | return true; | ||
| 153 | } | ||
| 154 | |||
| 155 | protected function doFetch(array $ids): iterable | ||
| 156 | { | ||
| 157 | $now = time(); | ||
| 158 | $expired = []; | ||
| 159 | |||
| 160 | $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN (?)"; | ||
| 161 | $result = $this->conn->executeQuery($sql, [ | ||
| 162 | $now, | ||
| 163 | $ids, | ||
| 164 | ], [ | ||
| 165 | ParameterType::INTEGER, | ||
| 166 | ArrayParameterType::STRING, | ||
| 167 | ])->iterateNumeric(); | ||
| 168 | |||
| 169 | foreach ($result as $row) { | ||
| 170 | if (null === $row[1]) { | ||
| 171 | $expired[] = $row[0]; | ||
| 172 | } else { | ||
| 173 | yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); | ||
| 174 | } | ||
| 175 | } | ||
| 176 | |||
| 177 | if ($expired) { | ||
| 178 | $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN (?)"; | ||
| 179 | $this->conn->executeStatement($sql, [ | ||
| 180 | $now, | ||
| 181 | $expired, | ||
| 182 | ], [ | ||
| 183 | ParameterType::INTEGER, | ||
| 184 | ArrayParameterType::STRING, | ||
| 185 | ]); | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | protected function doHave(string $id): bool | ||
| 190 | { | ||
| 191 | $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = ? AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ?)"; | ||
| 192 | $result = $this->conn->executeQuery($sql, [ | ||
| 193 | $id, | ||
| 194 | time(), | ||
| 195 | ], [ | ||
| 196 | ParameterType::STRING, | ||
| 197 | ParameterType::INTEGER, | ||
| 198 | ]); | ||
| 199 | |||
| 200 | return (bool) $result->fetchOne(); | ||
| 201 | } | ||
| 202 | |||
| 203 | protected function doClear(string $namespace): bool | ||
| 204 | { | ||
| 205 | if ('' === $namespace) { | ||
| 206 | $sql = $this->conn->getDatabasePlatform()->getTruncateTableSQL($this->table); | ||
| 207 | } else { | ||
| 208 | $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'"; | ||
| 209 | } | ||
| 210 | |||
| 211 | try { | ||
| 212 | $this->conn->executeStatement($sql); | ||
| 213 | } catch (TableNotFoundException) { | ||
| 214 | } | ||
| 215 | |||
| 216 | return true; | ||
| 217 | } | ||
| 218 | |||
| 219 | protected function doDelete(array $ids): bool | ||
| 220 | { | ||
| 221 | $sql = "DELETE FROM $this->table WHERE $this->idCol IN (?)"; | ||
| 222 | try { | ||
| 223 | $this->conn->executeStatement($sql, [array_values($ids)], [ArrayParameterType::STRING]); | ||
| 224 | } catch (TableNotFoundException) { | ||
| 225 | } | ||
| 226 | |||
| 227 | return true; | ||
| 228 | } | ||
| 229 | |||
| 230 | protected function doSave(array $values, int $lifetime): array|bool | ||
| 231 | { | ||
| 232 | if (!$values = $this->marshaller->marshall($values, $failed)) { | ||
| 233 | return $failed; | ||
| 234 | } | ||
| 235 | |||
| 236 | $platformName = $this->getPlatformName(); | ||
| 237 | $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?)"; | ||
| 238 | |||
| 239 | switch ($platformName) { | ||
| 240 | case 'mysql': | ||
| 241 | $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; | ||
| 242 | break; | ||
| 243 | case 'oci': | ||
| 244 | // DUAL is Oracle specific dummy table | ||
| 245 | $sql = "MERGE INTO $this->table USING DUAL ON ($this->idCol = ?) ". | ||
| 246 | "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ". | ||
| 247 | "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?"; | ||
| 248 | break; | ||
| 249 | case 'sqlsrv': | ||
| 250 | // MERGE is only available since SQL Server 2008 and must be terminated by semicolon | ||
| 251 | // It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx | ||
| 252 | $sql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ". | ||
| 253 | "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ". | ||
| 254 | "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;"; | ||
| 255 | break; | ||
| 256 | case 'sqlite': | ||
| 257 | $sql = 'INSERT OR REPLACE'.substr($insertSql, 6); | ||
| 258 | break; | ||
| 259 | case 'pgsql': | ||
| 260 | $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; | ||
| 261 | break; | ||
| 262 | default: | ||
| 263 | $platformName = null; | ||
| 264 | $sql = "UPDATE $this->table SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ? WHERE $this->idCol = ?"; | ||
| 265 | break; | ||
| 266 | } | ||
| 267 | |||
| 268 | $now = time(); | ||
| 269 | $lifetime = $lifetime ?: null; | ||
| 270 | try { | ||
| 271 | $stmt = $this->conn->prepare($sql); | ||
| 272 | } catch (TableNotFoundException) { | ||
| 273 | if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], true)) { | ||
| 274 | $this->createTable(); | ||
| 275 | } | ||
| 276 | $stmt = $this->conn->prepare($sql); | ||
| 277 | } | ||
| 278 | |||
| 279 | if ('sqlsrv' === $platformName || 'oci' === $platformName) { | ||
| 280 | $bind = static function ($id, $data) use ($stmt) { | ||
| 281 | $stmt->bindValue(1, $id); | ||
| 282 | $stmt->bindValue(2, $id); | ||
| 283 | $stmt->bindValue(3, $data, ParameterType::LARGE_OBJECT); | ||
| 284 | $stmt->bindValue(6, $data, ParameterType::LARGE_OBJECT); | ||
| 285 | }; | ||
| 286 | $stmt->bindValue(4, $lifetime, ParameterType::INTEGER); | ||
| 287 | $stmt->bindValue(5, $now, ParameterType::INTEGER); | ||
| 288 | $stmt->bindValue(7, $lifetime, ParameterType::INTEGER); | ||
| 289 | $stmt->bindValue(8, $now, ParameterType::INTEGER); | ||
| 290 | } elseif (null !== $platformName) { | ||
| 291 | $bind = static function ($id, $data) use ($stmt) { | ||
| 292 | $stmt->bindValue(1, $id); | ||
| 293 | $stmt->bindValue(2, $data, ParameterType::LARGE_OBJECT); | ||
| 294 | }; | ||
| 295 | $stmt->bindValue(3, $lifetime, ParameterType::INTEGER); | ||
| 296 | $stmt->bindValue(4, $now, ParameterType::INTEGER); | ||
| 297 | } else { | ||
| 298 | $stmt->bindValue(2, $lifetime, ParameterType::INTEGER); | ||
| 299 | $stmt->bindValue(3, $now, ParameterType::INTEGER); | ||
| 300 | |||
| 301 | $insertStmt = $this->conn->prepare($insertSql); | ||
| 302 | $insertStmt->bindValue(3, $lifetime, ParameterType::INTEGER); | ||
| 303 | $insertStmt->bindValue(4, $now, ParameterType::INTEGER); | ||
| 304 | |||
| 305 | $bind = static function ($id, $data) use ($stmt, $insertStmt) { | ||
| 306 | $stmt->bindValue(1, $data, ParameterType::LARGE_OBJECT); | ||
| 307 | $stmt->bindValue(4, $id); | ||
| 308 | $insertStmt->bindValue(1, $id); | ||
| 309 | $insertStmt->bindValue(2, $data, ParameterType::LARGE_OBJECT); | ||
| 310 | }; | ||
| 311 | } | ||
| 312 | |||
| 313 | foreach ($values as $id => $data) { | ||
| 314 | $bind($id, $data); | ||
| 315 | try { | ||
| 316 | $rowCount = $stmt->executeStatement(); | ||
| 317 | } catch (TableNotFoundException) { | ||
| 318 | if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], true)) { | ||
| 319 | $this->createTable(); | ||
| 320 | } | ||
| 321 | $rowCount = $stmt->executeStatement(); | ||
| 322 | } | ||
| 323 | if (null === $platformName && 0 === $rowCount) { | ||
| 324 | try { | ||
| 325 | $insertStmt->executeStatement(); | ||
| 326 | } catch (DBALException) { | ||
| 327 | // A concurrent write won, let it be | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | return $failed; | ||
| 333 | } | ||
| 334 | |||
| 335 | /** | ||
| 336 | * @internal | ||
| 337 | */ | ||
| 338 | protected function getId(mixed $key): string | ||
| 339 | { | ||
| 340 | if ('pgsql' !== $this->platformName ??= $this->getPlatformName()) { | ||
| 341 | return parent::getId($key); | ||
| 342 | } | ||
| 343 | |||
| 344 | if (str_contains($key, "\0") || str_contains($key, '%') || !preg_match('//u', $key)) { | ||
| 345 | $key = rawurlencode($key); | ||
| 346 | } | ||
| 347 | |||
| 348 | return parent::getId($key); | ||
| 349 | } | ||
| 350 | |||
| 351 | private function getPlatformName(): string | ||
| 352 | { | ||
| 353 | if (isset($this->platformName)) { | ||
| 354 | return $this->platformName; | ||
| 355 | } | ||
| 356 | |||
| 357 | $platform = $this->conn->getDatabasePlatform(); | ||
| 358 | |||
| 359 | return $this->platformName = match (true) { | ||
| 360 | $platform instanceof \Doctrine\DBAL\Platforms\AbstractMySQLPlatform => 'mysql', | ||
| 361 | $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform => 'sqlite', | ||
| 362 | $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform => 'pgsql', | ||
| 363 | $platform instanceof \Doctrine\DBAL\Platforms\OraclePlatform => 'oci', | ||
| 364 | $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform => 'sqlsrv', | ||
| 365 | default => $platform::class, | ||
| 366 | }; | ||
| 367 | } | ||
| 368 | |||
| 369 | private function addTableToSchema(Schema $schema): void | ||
| 370 | { | ||
| 371 | $types = [ | ||
| 372 | 'mysql' => 'binary', | ||
| 373 | 'sqlite' => 'text', | ||
| 374 | ]; | ||
| 375 | |||
| 376 | $table = $schema->createTable($this->table); | ||
| 377 | $table->addColumn($this->idCol, $types[$this->getPlatformName()] ?? 'string', ['length' => 255]); | ||
| 378 | $table->addColumn($this->dataCol, 'blob', ['length' => 16777215]); | ||
| 379 | $table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => true, 'notnull' => false]); | ||
| 380 | $table->addColumn($this->timeCol, 'integer', ['unsigned' => true]); | ||
| 381 | $table->setPrimaryKey([$this->idCol]); | ||
| 382 | } | ||
| 383 | } | ||
