![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/vendor/symfony/lock/Store/ |
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <[email protected]> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Lock\Store; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Exception\TableNotFoundException; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Schema\DefaultSchemaManagerFactory; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Tools\DsnParser; use Symfony\Component\Lock\Exception\InvalidArgumentException; use Symfony\Component\Lock\Exception\InvalidTtlException; use Symfony\Component\Lock\Exception\LockConflictedException; use Symfony\Component\Lock\Key; use Symfony\Component\Lock\PersistingStoreInterface; /** * DbalStore is a PersistingStoreInterface implementation using a Doctrine DBAL connection. * * Lock metadata are stored in a table. You can use createTable() to initialize * a correctly defined table. * CAUTION: This store relies on all client and server nodes to have * synchronized clocks for lock expiry to occur at the correct time. * To ensure locks don't expire prematurely; the TTLs should be set with enough * extra time to account for any clock drift between nodes. * * @author Jérémy Derussé <[email protected]> */ class DoctrineDbalStore implements PersistingStoreInterface { use DatabaseTableTrait; use ExpiringStoreTrait; private $conn; /** * List of available options: * * db_table: The name of the table [default: lock_keys] * * db_id_col: The column where to store the lock key [default: key_id] * * db_token_col: The column where to store the lock token [default: key_token] * * db_expiration_col: The column where to store the expiration [default: key_expiration]. * * @param Connection|string $connOrUrl A DBAL Connection instance or Doctrine URL * @param array $options An associative array of options * @param float $gcProbability Probability expressed as floating number between 0 and 1 to clean old locks * @param int $initialTtl The expiration delay of locks in seconds * * @throws InvalidArgumentException When namespace contains invalid characters * @throws InvalidArgumentException When the initial ttl is not valid */ public function __construct($connOrUrl, array $options = [], float $gcProbability = 0.01, int $initialTtl = 300) { $this->init($options, $gcProbability, $initialTtl); if ($connOrUrl instanceof Connection) { $this->conn = $connOrUrl; } elseif (\is_string($connOrUrl)) { if (!class_exists(DriverManager::class)) { throw new InvalidArgumentException('Failed to parse the DSN. Try running "composer require doctrine/dbal".'); } if (class_exists(DsnParser::class)) { $params = (new DsnParser([ 'db2' => 'ibm_db2', 'mssql' => 'pdo_sqlsrv', 'mysql' => 'pdo_mysql', 'mysql2' => 'pdo_mysql', 'postgres' => 'pdo_pgsql', 'postgresql' => 'pdo_pgsql', 'pgsql' => 'pdo_pgsql', 'sqlite' => 'pdo_sqlite', 'sqlite3' => 'pdo_sqlite', ]))->parse($connOrUrl); } else { $params = ['url' => $connOrUrl]; } $config = new Configuration(); if (class_exists(DefaultSchemaManagerFactory::class)) { $config->setSchemaManagerFactory(new DefaultSchemaManagerFactory()); } $this->conn = DriverManager::getConnection($params, $config); } else { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be "%s" or string, "%s" given.', Connection::class, __METHOD__, get_debug_type($connOrUrl))); } } /** * {@inheritdoc} */ public function save(Key $key) { $key->reduceLifetime($this->initialTtl); $sql = "INSERT INTO $this->table ($this->idCol, $this->tokenCol, $this->expirationCol) VALUES (?, ?, {$this->getCurrentTimestampStatement()} + $this->initialTtl)"; try { $this->conn->executeStatement($sql, [ $this->getHashedKey($key), $this->getUniqueToken($key), ], [ ParameterType::STRING, ParameterType::STRING, ]); } catch (TableNotFoundException $e) { if (!$this->conn->isTransactionActive() || $this->platformSupportsTableCreationInTransaction()) { $this->createTable(); } try { $this->conn->executeStatement($sql, [ $this->getHashedKey($key), $this->getUniqueToken($key), ], [ ParameterType::STRING, ParameterType::STRING, ]); } catch (DBALException $e) { $this->putOffExpiration($key, $this->initialTtl); } } catch (DBALException $e) { // the lock is already acquired. It could be us. Let's try to put off. $this->putOffExpiration($key, $this->initialTtl); } $this->randomlyPrune(); $this->checkNotExpired($key); } /** * {@inheritdoc} */ public function putOffExpiration(Key $key, $ttl) { if ($ttl < 1) { throw new InvalidTtlException(sprintf('"%s()" expects a TTL greater or equals to 1 second. Got "%s".', __METHOD__, $ttl)); } $key->reduceLifetime($ttl); $sql = "UPDATE $this->table SET $this->expirationCol = {$this->getCurrentTimestampStatement()} + ?, $this->tokenCol = ? WHERE $this->idCol = ? AND ($this->tokenCol = ? OR $this->expirationCol <= {$this->getCurrentTimestampStatement()})"; $uniqueToken = $this->getUniqueToken($key); $result = $this->conn->executeQuery($sql, [ $ttl, $uniqueToken, $this->getHashedKey($key), $uniqueToken, ], [ ParameterType::INTEGER, ParameterType::STRING, ParameterType::STRING, ParameterType::STRING, ]); // If this method is called twice in the same second, the row wouldn't be updated. We have to call exists to know if we are the owner if (!$result->rowCount() && !$this->exists($key)) { throw new LockConflictedException(); } $this->checkNotExpired($key); } /** * {@inheritdoc} */ public function delete(Key $key) { $this->conn->delete($this->table, [ $this->idCol => $this->getHashedKey($key), $this->tokenCol => $this->getUniqueToken($key), ]); } /** * {@inheritdoc} */ public function exists(Key $key) { $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = ? AND $this->tokenCol = ? AND $this->expirationCol > {$this->getCurrentTimestampStatement()}"; $result = $this->conn->fetchOne($sql, [ $this->getHashedKey($key), $this->getUniqueToken($key), ], [ ParameterType::STRING, ParameterType::STRING, ]); return (bool) $result; } /** * Creates the table to store lock keys which can be called once for setup. * * @throws DBALException When the table already exists */ public function createTable(): void { $schema = new Schema(); $this->configureSchema($schema); foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) { $this->conn->executeStatement($sql); } } /** * Adds the Table to the Schema if it doesn't exist. */ public function configureSchema(Schema $schema): void { if ($schema->hasTable($this->table)) { return; } $table = $schema->createTable($this->table); $table->addColumn($this->idCol, 'string', ['length' => 64]); $table->addColumn($this->tokenCol, 'string', ['length' => 44]); $table->addColumn($this->expirationCol, 'integer', ['unsigned' => true]); $table->setPrimaryKey([$this->idCol]); } /** * Cleans up the table by removing all expired locks. */ private function prune(): void { $sql = "DELETE FROM $this->table WHERE $this->expirationCol <= {$this->getCurrentTimestampStatement()}"; $this->conn->executeStatement($sql); } /** * Provides an SQL function to get the current timestamp regarding the current connection's driver. */ private function getCurrentTimestampStatement(): string { $platform = $this->conn->getDatabasePlatform(); switch (true) { case $platform instanceof \Doctrine\DBAL\Platforms\MySQLPlatform: case $platform instanceof \Doctrine\DBAL\Platforms\MySQL57Platform: return 'UNIX_TIMESTAMP()'; case $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform: return 'strftime(\'%s\',\'now\')'; case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform: case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQL94Platform: return 'CAST(EXTRACT(epoch FROM NOW()) AS INT)'; case $platform instanceof \Doctrine\DBAL\Platforms\OraclePlatform: return '(SYSDATE - TO_DATE(\'19700101\',\'yyyymmdd\'))*86400 - TO_NUMBER(SUBSTR(TZ_OFFSET(sessiontimezone), 1, 3))*3600'; case $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform: case $platform instanceof \Doctrine\DBAL\Platforms\SQLServer2012Platform: return 'DATEDIFF(s, \'1970-01-01\', GETUTCDATE())'; default: return (string) time(); } } /** * Checks whether current platform supports table creation within transaction. */ private function platformSupportsTableCreationInTransaction(): bool { $platform = $this->conn->getDatabasePlatform(); switch (true) { case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQLPlatform: case $platform instanceof \Doctrine\DBAL\Platforms\PostgreSQL94Platform: case $platform instanceof \Doctrine\DBAL\Platforms\SqlitePlatform: case $platform instanceof \Doctrine\DBAL\Platforms\SQLServerPlatform: case $platform instanceof \Doctrine\DBAL\Platforms\SQLServer2012Platform: return true; default: return false; } } }