From ca19f5f1f99b844be0610a33518b3e8aaf9cfbec Mon Sep 17 00:00:00 2001 From: Demin Yin Date: Wed, 4 Oct 2023 21:09:18 -0700 Subject: [PATCH] update Swoole Library to v5.1.0 Signed-off-by: Demin Yin --- src/swoole_library/src/__init__.php | 1 + .../src/core/Coroutine/FastCGI/Client.php | 2 +- .../core/Database/DetectsLostConnections.php | 81 +++++++++++++++++++ .../src/core/Database/PDOConfig.php | 6 +- .../src/core/Database/PDOPool.php | 60 ++++++++++---- .../src/core/Database/PDOProxy.php | 72 ++++++++--------- .../src/core/Database/PDOStatementProxy.php | 47 ++++------- 7 files changed, 178 insertions(+), 91 deletions(-) create mode 100644 src/swoole_library/src/core/Database/DetectsLostConnections.php diff --git a/src/swoole_library/src/__init__.php b/src/swoole_library/src/__init__.php index e511283a..30b60703 100644 --- a/src/swoole_library/src/__init__.php +++ b/src/swoole_library/src/__init__.php @@ -40,6 +40,7 @@ 'core/Database/MysqliPool.php', 'core/Database/MysqliProxy.php', 'core/Database/MysqliStatementProxy.php', + 'core/Database/DetectsLostConnections.php', 'core/Database/PDOConfig.php', 'core/Database/PDOPool.php', 'core/Database/PDOProxy.php', diff --git a/src/swoole_library/src/core/Coroutine/FastCGI/Client.php b/src/swoole_library/src/core/Coroutine/FastCGI/Client.php index 7d2c97b9..a5ffbb3a 100644 --- a/src/swoole_library/src/core/Coroutine/FastCGI/Client.php +++ b/src/swoole_library/src/core/Coroutine/FastCGI/Client.php @@ -54,8 +54,8 @@ public function __construct(string $host, int $port = 0, bool $ssl = false) } /** - * @return HttpResponse|Response * @throws Exception + * @return HttpResponse|Response */ public function execute(Request $request, float $timeout = -1): Response { diff --git a/src/swoole_library/src/core/Database/DetectsLostConnections.php b/src/swoole_library/src/core/Database/DetectsLostConnections.php new file mode 100644 index 00000000..6a92be30 --- /dev/null +++ b/src/swoole_library/src/core/Database/DetectsLostConnections.php @@ -0,0 +1,81 @@ +getMessage(); + foreach (self::ERROR_MESSAGES as $needle) { + if ($needle !== '' && mb_strpos($message, $needle) !== false) { + return true; + } + } + + return false; + } +} diff --git a/src/swoole_library/src/core/Database/PDOConfig.php b/src/swoole_library/src/core/Database/PDOConfig.php index bae200ad..6b407a45 100644 --- a/src/swoole_library/src/core/Database/PDOConfig.php +++ b/src/swoole_library/src/core/Database/PDOConfig.php @@ -24,8 +24,8 @@ class PDOConfig /** @var int */ protected $port = 3306; - /** @var null|string */ - protected $unixSocket; + /** @var string */ + protected $unixSocket = ''; /** @var string */ protected $dbname = 'test'; @@ -71,7 +71,7 @@ public function getPort(): int public function hasUnixSocket(): bool { - return isset($this->unixSocket); + return !empty($this->unixSocket); } public function getUnixSocket(): string diff --git a/src/swoole_library/src/core/Database/PDOPool.php b/src/swoole_library/src/core/Database/PDOPool.php index 777c99f4..7ead406a 100644 --- a/src/swoole_library/src/core/Database/PDOPool.php +++ b/src/swoole_library/src/core/Database/PDOPool.php @@ -11,11 +11,11 @@ namespace Swoole\Database; +use Exception; use PDO; use Swoole\ConnectionPool; /** - * @method \PDO|PDOProxy get() * @method void put(PDO|PDOProxy $connection) */ class PDOPool extends ConnectionPool @@ -31,22 +31,48 @@ public function __construct(PDOConfig $config, int $size = self::DEFAULT_SIZE) $this->config = $config; parent::__construct(function () { $driver = $this->config->getDriver(); - return new \PDO( - "{$driver}:" . - ( - $this->config->hasUnixSocket() ? - "unix_socket={$this->config->getUnixSocket()};" : - "host={$this->config->getHost()};port={$this->config->getPort()};" - ) . - "dbname={$this->config->getDbname()};" . - ( - ($driver !== 'pgsql') ? - "charset={$this->config->getCharset()}" : '' - ), - $this->config->getUsername(), - $this->config->getPassword(), - $this->config->getOptions() - ); + if ($driver === 'sqlite') { + return new PDO($this->createDSN('sqlite')); + } + + return new PDO($this->createDSN($driver), $this->config->getUsername(), $this->config->getPassword(), $this->config->getOptions()); }, $size, PDOProxy::class); } + + public function get(float $timeout = -1) + { + $pdo = parent::get($timeout); + /* @var \Swoole\Database\PDOProxy $pdo */ + $pdo->reset(); + return $pdo; + } + + /** + * @purpose create DSN + * @throws Exception + */ + private function createDSN(string $driver): string + { + switch ($driver) { + case 'mysql': + if ($this->config->hasUnixSocket()) { + $dsn = "mysql:unix_socket={$this->config->getUnixSocket()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}"; + } else { + $dsn = "mysql:host={$this->config->getHost()};port={$this->config->getPort()};dbname={$this->config->getDbname()};charset={$this->config->getCharset()}"; + } + break; + case 'pgsql': + $dsn = 'pgsql:host=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ";port={$this->config->getPort()};dbname={$this->config->getDbname()}"; + break; + case 'oci': + $dsn = 'oci:dbname=' . ($this->config->hasUnixSocket() ? $this->config->getUnixSocket() : $this->config->getHost()) . ':' . $this->config->getPort() . '/' . $this->config->getDbname() . ';charset=' . $this->config->getCharset(); + break; + case 'sqlite': + $dsn = 'sqlite:' . $this->config->getDbname(); + break; + default: + throw new Exception('Unsupported Database Driver:' . $driver); + } + return $dsn; + } } diff --git a/src/swoole_library/src/core/Database/PDOProxy.php b/src/swoole_library/src/core/Database/PDOProxy.php index 6ad37ed2..e4d7b23a 100644 --- a/src/swoole_library/src/core/Database/PDOProxy.php +++ b/src/swoole_library/src/core/Database/PDOProxy.php @@ -11,15 +11,12 @@ namespace Swoole\Database; +use PDO; +use PDOException; + class PDOProxy extends ObjectProxy { - public const IO_ERRORS = [ - 2002, // MYSQLND_CR_CONNECTION_ERROR - 2006, // MYSQLND_CR_SERVER_GONE_ERROR - 2013, // MYSQLND_CR_SERVER_LOST - ]; - - /** @var \PDO */ + /** @var PDO */ protected $__object; /** @var null|array */ @@ -31,46 +28,41 @@ class PDOProxy extends ObjectProxy /** @var int */ protected $round = 0; + /** @var int */ + protected $inTransaction = 0; + public function __construct(callable $constructor) { parent::__construct($constructor()); - $this->__object->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT); + $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->constructor = $constructor; } public function __call(string $name, array $arguments) { - for ($n = 3; $n--;) { - $ret = @$this->__object->{$name}(...$arguments); - if ($ret === false) { - $errorInfo = $this->__object->errorInfo(); - if (empty($errorInfo)) { - break; - } - /* no more chances or non-IO failures */ - if ( - !in_array($errorInfo[1], static::IO_ERRORS, true) - || $n === 0 - || $this->__object->inTransaction() - ) { - /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */ - if (!empty($errorInfo) && $errorInfo[0] !== '00000') { - $exception = new \PDOException($errorInfo[2], $errorInfo[1]); - $exception->errorInfo = $errorInfo; - throw $exception; - } - /* no error info, just return false */ - break; - } + try { + $ret = $this->__object->{$name}(...$arguments); + } catch (PDOException $e) { + if (!$this->__object->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) { $this->reconnect(); - continue; + $ret = $this->__object->{$name}(...$arguments); + } else { + throw $e; } - if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) { - $ret = new PDOStatementProxy($ret, $this); - } - break; } - /* @noinspection PhpUndefinedVariableInspection */ + + if (strcasecmp($name, 'beginTransaction') === 0) { + $this->inTransaction++; + } + + if ((strcasecmp($name, 'commit') === 0 || strcasecmp($name, 'rollback') === 0) && $this->inTransaction > 0) { + $this->inTransaction--; + } + + if ((strcasecmp($name, 'prepare') === 0) || (strcasecmp($name, 'query') === 0)) { + $ret = new PDOStatementProxy($ret, $this); + } + return $ret; } @@ -83,6 +75,7 @@ public function reconnect(): void { $constructor = $this->constructor; parent::__construct($constructor()); + $this->__object->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->round++; /* restore context */ if ($this->setAttributeContext) { @@ -100,6 +93,11 @@ public function setAttribute(int $attribute, $value): bool public function inTransaction(): bool { - return $this->__object->inTransaction(); + return $this->inTransaction > 0; + } + + public function reset(): void + { + $this->inTransaction = 0; } } diff --git a/src/swoole_library/src/core/Database/PDOStatementProxy.php b/src/swoole_library/src/core/Database/PDOStatementProxy.php index 64fca577..4a9eda3a 100644 --- a/src/swoole_library/src/core/Database/PDOStatementProxy.php +++ b/src/swoole_library/src/core/Database/PDOStatementProxy.php @@ -11,9 +11,12 @@ namespace Swoole\Database; +use PDOException; +use PDOStatement; + class PDOStatementProxy extends ObjectProxy { - /** @var \PDOStatement */ + /** @var PDOStatement */ protected $__object; /** @var null|array */ @@ -37,7 +40,7 @@ class PDOStatementProxy extends ObjectProxy /** @var int */ protected $parentRound; - public function __construct(\PDOStatement $object, PDOProxy $parent) + public function __construct(PDOStatement $object, PDOProxy $parent) { parent::__construct($object); $this->parent = $parent; @@ -46,40 +49,17 @@ public function __construct(\PDOStatement $object, PDOProxy $parent) public function __call(string $name, array $arguments) { - for ($n = 3; $n--;) { - $ret = @$this->__object->{$name}(...$arguments); - if ($ret === false) { - $errorInfo = $this->__object->errorInfo(); - if (empty($errorInfo)) { - break; - } - /* no more chances or non-IO failures or in transaction */ - if ( - !in_array($errorInfo[1], $this->parent::IO_ERRORS, true) - || $n === 0 - || $this->parent->inTransaction() - ) { - /* '00000' means “no error.”, as specified by ANSI SQL and ODBC. */ - if (!empty($errorInfo) && $errorInfo[0] !== '00000') { - $exception = new \PDOException($errorInfo[2], $errorInfo[1]); - $exception->errorInfo = $errorInfo; - throw $exception; - } - /* no error info, just return false */ - break; - } + try { + $ret = $this->__object->{$name}(...$arguments); + } catch (PDOException $e) { + if (!$this->parent->inTransaction() && DetectsLostConnections::causedByLostConnection($e)) { if ($this->parent->getRound() === $this->parentRound) { /* if not equal, parent has reconnected */ $this->parent->reconnect(); } $parent = $this->parent->__getObject(); $this->__object = $parent->prepare($this->__object->queryString); - if ($this->__object === false) { - $errorInfo = $parent->errorInfo(); - $exception = new \PDOException($errorInfo[2], $errorInfo[1]); - $exception->errorInfo = $errorInfo; - throw $exception; - } + if ($this->setAttributeContext) { foreach ($this->setAttributeContext as $attribute => $value) { $this->__object->setAttribute($attribute, $value); @@ -103,11 +83,12 @@ public function __call(string $name, array $arguments) $this->__object->bindParam($value, ...$item); } } - continue; + $ret = $this->__object->{$name}(...$arguments); + } else { + throw $e; } - break; } - /* @noinspection PhpUndefinedVariableInspection */ + return $ret; }