This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

4
vendor/doctrine/dbal/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,4 @@
Doctrine has [general contributing guidelines][contributor workflow], make
sure you follow them.
[contributor workflow]: https://www.doctrine-project.org/contribute/index.html

19
vendor/doctrine/dbal/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2006-2018 Doctrine Project
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

42
vendor/doctrine/dbal/README.md vendored Normal file
View File

@@ -0,0 +1,42 @@
# Doctrine DBAL
| [4.0-dev][4.0] | [3.3][3.3] | [2.13][2.13] |
|:----------------:|:----------:|:----------:|
| [![GitHub Actions][GA 4.0 image]][GA 4.0] | [![GitHub Actions][GA 3.3 image]][GA 3.3] | [![GitHub Actions][GA 2.13 image]][GA 2.13] |
| [![AppVeyor][AppVeyor 4.0 image]][AppVeyor 4.0] | [![AppVeyor][AppVeyor 3.3 image]][AppVeyor 3.3] | [![AppVeyor][AppVeyor 2.13 image]][AppVeyor 2.13] |
| [![Code Coverage][Coverage image]][CodeCov 4.0] | [![Code Coverage][Coverage 3.3 image]][CodeCov 3.3] | [![Code Coverage][Coverage 2.13 image]][CodeCov 2.13] |
| N/A | [![Code Coverage][TypeCov 3.3 image]][TypeCov 3.3] | N/A |
Powerful database abstraction layer with many features for database schema introspection, schema management and PDO abstraction.
## More resources:
* [Website](http://www.doctrine-project.org/projects/dbal.html)
* [Documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/)
* [Issue Tracker](https://github.com/doctrine/dbal/issues)
[Coverage image]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x/graph/badge.svg
[4.0]: https://github.com/doctrine/dbal/tree/4.0.x
[CodeCov 4.0]: https://codecov.io/gh/doctrine/dbal/branch/4.0.x
[AppVeyor 4.0]: https://ci.appveyor.com/project/doctrine/dbal/branch/4.0.x
[AppVeyor 4.0 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/4.0.x?svg=true
[GA 4.0]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A4.0.x
[GA 4.0 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg
[Coverage 3.3 image]: https://codecov.io/gh/doctrine/dbal/branch/3.3.x/graph/badge.svg
[3.3]: https://github.com/doctrine/dbal/tree/3.3.x
[CodeCov 3.3]: https://codecov.io/gh/doctrine/dbal/branch/3.3.x
[AppVeyor 3.3]: https://ci.appveyor.com/project/doctrine/dbal/branch/3.3.x
[AppVeyor 3.3 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/3.3.x?svg=true
[GA 3.3]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A3.3.x
[GA 3.3 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=3.3.x
[TypeCov 3.3]: https://shepherd.dev/github/doctrine/dbal
[TypeCov 3.3 image]: https://shepherd.dev/github/doctrine/dbal/coverage.svg
[Coverage 2.13 image]: https://codecov.io/gh/doctrine/dbal/branch/2.13.x/graph/badge.svg
[2.13]: https://github.com/doctrine/dbal/tree/2.13.x
[CodeCov 2.13]: https://codecov.io/gh/doctrine/dbal/branch/2.13.x
[AppVeyor 2.13]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.13.x
[AppVeyor 2.13 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/2.13.x?svg=true
[GA 2.13]: https://github.com/doctrine/dbal/actions?query=workflow%3A%22Continuous+Integration%22+branch%3A2.13.x
[GA 2.13 image]: https://github.com/doctrine/dbal/workflows/Continuous%20Integration/badge.svg?branch=2.13.x

View File

@@ -0,0 +1,4 @@
#!/usr/bin/env php
<?php
require __DIR__ . '/doctrine-dbal.php';

View File

@@ -0,0 +1,61 @@
<?php
use Doctrine\DBAL\Tools\Console\ConnectionProvider;
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
use Symfony\Component\Console\Helper\HelperSet;
$files = [__DIR__ . '/../vendor/autoload.php', __DIR__ . '/../../../autoload.php'];
$loader = null;
$cwd = getcwd();
$directories = [$cwd, $cwd . DIRECTORY_SEPARATOR . 'config'];
$configFile = null;
foreach ($files as $file) {
if (file_exists($file)) {
$loader = require $file;
break;
}
}
if (! $loader) {
throw new RuntimeException('vendor/autoload.php could not be found. Did you run `php composer.phar install`?');
}
foreach ($directories as $directory) {
$configFile = $directory . DIRECTORY_SEPARATOR . 'cli-config.php';
if (file_exists($configFile)) {
break;
}
}
if (! file_exists($configFile)) {
ConsoleRunner::printCliConfigTemplate();
exit(1);
}
if (! is_readable($configFile)) {
echo 'Configuration file [' . $configFile . '] does not have read permission.' . PHP_EOL;
exit(1);
}
$commands = [];
$helperSetOrConnectionProvider = require $configFile;
if (
! $helperSetOrConnectionProvider instanceof HelperSet
&& ! $helperSetOrConnectionProvider instanceof ConnectionProvider
) {
foreach ($GLOBALS as $candidate) {
if ($candidate instanceof HelperSet) {
$helperSetOrConnectionProvider = $candidate;
break;
}
}
}
ConsoleRunner::run($helperSetOrConnectionProvider, $commands);

69
vendor/doctrine/dbal/composer.json vendored Normal file
View File

@@ -0,0 +1,69 @@
{
"name": "doctrine/dbal",
"type": "library",
"description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
"keywords": [
"abstraction",
"database",
"dbal",
"db2",
"mariadb",
"mssql",
"mysql",
"pgsql",
"postgresql",
"oci8",
"oracle",
"pdo",
"queryobject",
"sasql",
"sql",
"sqlanywhere",
"sqlite",
"sqlserver",
"sqlsrv"
],
"homepage": "https://www.doctrine-project.org/projects/dbal.html",
"license": "MIT",
"authors": [
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
{"name": "Roman Borschel", "email": "roman@code-factory.org"},
{"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"},
{"name": "Jonathan Wage", "email": "jonwage@gmail.com"}
],
"require": {
"php": "^7.1 || ^8",
"ext-pdo": "*",
"doctrine/cache": "^1.0|^2.0",
"doctrine/deprecations": "^0.5.3|^1",
"doctrine/event-manager": "^1.0"
},
"require-dev": {
"doctrine/coding-standard": "9.0.0",
"jetbrains/phpstorm-stubs": "2021.1",
"phpstan/phpstan": "1.4.6",
"phpunit/phpunit": "^7.5.20|^8.5|9.5.16",
"psalm/plugin-phpunit": "0.16.1",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4",
"symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
"vimeo/psalm": "4.22.0"
},
"suggest": {
"symfony/console": "For helpful console commands such as SQL execution and import of files."
},
"bin": ["bin/doctrine-dbal"],
"config": {
"sort-packages": true,
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"composer/package-versions-deprecated": true
}
},
"autoload": {
"psr-4": { "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" }
},
"autoload-dev": {
"psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" }
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Abstraction;
use Doctrine\DBAL\Driver\Result as DriverResult;
use Doctrine\DBAL\Exception;
use Traversable;
/**
* Abstraction-level result statement execution result. Provides additional methods on top
* of the driver-level interface.
*
* @deprecated
*/
interface Result extends DriverResult
{
/**
* Returns an iterator over the result set rows represented as numeric arrays.
*
* @return Traversable<int,array<int,mixed>>
*
* @throws Exception
*/
public function iterateNumeric(): Traversable;
/**
* Returns an iterator over the result set rows represented as associative arrays.
*
* @return Traversable<int,array<string,mixed>>
*
* @throws Exception
*/
public function iterateAssociative(): Traversable;
/**
* Returns an iterator over the values of the first column of the result set.
*
* @return Traversable<int,mixed>
*
* @throws Exception
*/
public function iterateColumn(): Traversable;
}

View File

@@ -0,0 +1,244 @@
<?php
namespace Doctrine\DBAL\Cache;
use ArrayIterator;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\FetchMode;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use ReturnTypeWillChange;
use function array_merge;
use function array_values;
use function count;
use function reset;
/**
* @deprecated
*/
class ArrayStatement implements IteratorAggregate, ResultStatement, Result
{
/** @var mixed[] */
private $data;
/** @var int */
private $columnCount = 0;
/** @var int */
private $num = 0;
/** @var int */
private $defaultFetchMode = FetchMode::MIXED;
/**
* @param mixed[] $data
*/
public function __construct(array $data)
{
$this->data = $data;
if (! count($data)) {
return;
}
$this->columnCount = count($data[0]);
}
/**
* {@inheritdoc}
*
* @deprecated Use free() instead.
*/
public function closeCursor()
{
$this->free();
return true;
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return count($this->data);
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return $this->columnCount;
}
/**
* {@inheritdoc}
*
* @deprecated Use one of the fetch- or iterate-related methods.
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
if ($arg2 !== null || $arg3 !== null) {
throw new InvalidArgumentException('Caching layer does not support 2nd/3rd argument to setFetchMode()');
}
$this->defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*
* @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead.
*/
#[ReturnTypeWillChange]
public function getIterator()
{
$data = $this->fetchAll();
return new ArrayIterator($data);
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
if (! isset($this->data[$this->num])) {
return false;
}
$row = $this->data[$this->num++];
$fetchMode = $fetchMode ?: $this->defaultFetchMode;
if ($fetchMode === FetchMode::ASSOCIATIVE) {
return $row;
}
if ($fetchMode === FetchMode::NUMERIC) {
return array_values($row);
}
if ($fetchMode === FetchMode::MIXED) {
return array_merge($row, array_values($row));
}
if ($fetchMode === FetchMode::COLUMN) {
return reset($row);
}
throw new InvalidArgumentException('Invalid fetch-style given for fetching result.');
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$rows = [];
while ($row = $this->fetch($fetchMode)) {
$rows[] = $row;
}
return $rows;
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchOne() instead.
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
}
/**
* {@inheritdoc}
*/
public function fetchNumeric()
{
$row = $this->doFetch();
if ($row === false) {
return false;
}
return array_values($row);
}
/**
* {@inheritdoc}
*/
public function fetchAssociative()
{
return $this->doFetch();
}
/**
* {@inheritdoc}
*/
public function fetchOne()
{
$row = $this->doFetch();
if ($row === false) {
return false;
}
return reset($row);
}
/**
* {@inheritdoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritdoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
public function free(): void
{
$this->data = [];
}
/**
* @return mixed|false
*/
private function doFetch()
{
if (! isset($this->data[$this->num])) {
return false;
}
return $this->data[$this->num++];
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Doctrine\DBAL\Cache;
use Doctrine\DBAL\Exception;
/**
* @psalm-immutable
*/
class CacheException extends Exception
{
/**
* @return CacheException
*/
public static function noCacheKey()
{
return new self('No cache key was set.');
}
/**
* @return CacheException
*/
public static function noResultDriverConfigured()
{
return new self('Trying to cache a query but no result driver is configured.');
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace Doctrine\DBAL\Cache;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Types\Type;
use function hash;
use function serialize;
use function sha1;
/**
* Query Cache Profile handles the data relevant for query caching.
*
* It is a value object, setter methods return NEW instances.
*/
class QueryCacheProfile
{
/** @var Cache|null */
private $resultCacheDriver;
/** @var int */
private $lifetime = 0;
/** @var string|null */
private $cacheKey;
/**
* @param int $lifetime
* @param string|null $cacheKey
*/
public function __construct($lifetime = 0, $cacheKey = null, ?Cache $resultCache = null)
{
$this->lifetime = $lifetime;
$this->cacheKey = $cacheKey;
$this->resultCacheDriver = $resultCache;
}
/**
* @return Cache|null
*/
public function getResultCacheDriver()
{
return $this->resultCacheDriver;
}
/**
* @return int
*/
public function getLifetime()
{
return $this->lifetime;
}
/**
* @return string
*
* @throws CacheException
*/
public function getCacheKey()
{
if ($this->cacheKey === null) {
throw CacheException::noCacheKey();
}
return $this->cacheKey;
}
/**
* Generates the real cache key from query, params, types and connection parameters.
*
* @param string $sql
* @param array<int, mixed>|array<string, mixed> $params
* @param array<int, Type|int|string|null>|array<string, Type|int|string|null> $types
* @param array<string, mixed> $connectionParams
*
* @return string[]
*/
public function generateCacheKeys($sql, $params, $types, array $connectionParams = [])
{
$realCacheKey = 'query=' . $sql .
'&params=' . serialize($params) .
'&types=' . serialize($types) .
'&connectionParams=' . hash('sha256', serialize($connectionParams));
// should the key be automatically generated using the inputs or is the cache key set?
if ($this->cacheKey === null) {
$cacheKey = sha1($realCacheKey);
} else {
$cacheKey = $this->cacheKey;
}
return [$cacheKey, $realCacheKey];
}
/**
* @return QueryCacheProfile
*/
public function setResultCacheDriver(Cache $cache)
{
return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache);
}
/**
* @param string|null $cacheKey
*
* @return QueryCacheProfile
*/
public function setCacheKey($cacheKey)
{
return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver);
}
/**
* @param int $lifetime
*
* @return QueryCacheProfile
*/
public function setLifetime($lifetime)
{
return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver);
}
}

View File

@@ -0,0 +1,361 @@
<?php
namespace Doctrine\DBAL\Cache;
use ArrayIterator;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Driver\Exception;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\FetchMode;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use ReturnTypeWillChange;
use function array_map;
use function array_merge;
use function array_values;
use function assert;
use function reset;
/**
* Cache statement for SQL results.
*
* A result is saved in multiple cache keys, there is the originally specified
* cache key which is just pointing to result rows by key. The following things
* have to be ensured:
*
* 1. lifetime of the original key has to be longer than that of all the individual rows keys
* 2. if any one row key is missing the query has to be re-executed.
*
* Also you have to realize that the cache will load the whole result into memory at once to ensure 2.
* This means that the memory usage for cached results might increase by using this feature.
*
* @deprecated
*/
class ResultCacheStatement implements IteratorAggregate, ResultStatement, Result
{
/** @var Cache */
private $resultCache;
/** @var string */
private $cacheKey;
/** @var string */
private $realKey;
/** @var int */
private $lifetime;
/** @var ResultStatement */
private $statement;
/** @var array<int,array<string,mixed>>|null */
private $data;
/** @var int */
private $defaultFetchMode = FetchMode::MIXED;
/**
* @param string $cacheKey
* @param string $realKey
* @param int $lifetime
*/
public function __construct(ResultStatement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime)
{
$this->statement = $stmt;
$this->resultCache = $resultCache;
$this->cacheKey = $cacheKey;
$this->realKey = $realKey;
$this->lifetime = $lifetime;
}
/**
* {@inheritdoc}
*
* @deprecated Use free() instead.
*/
public function closeCursor()
{
$this->free();
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return $this->statement->columnCount();
}
/**
* {@inheritdoc}
*
* @deprecated Use one of the fetch- or iterate-related methods.
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*
* @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead.
*/
#[ReturnTypeWillChange]
public function getIterator()
{
$data = $this->fetchAll();
return new ArrayIterator($data);
}
/**
* Be warned that you will need to call this method until no rows are
* available for caching to happen.
*
* {@inheritdoc}
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
if ($this->data === null) {
$this->data = [];
}
$row = $this->statement->fetch(FetchMode::ASSOCIATIVE);
if ($row) {
$this->data[] = $row;
$fetchMode = $fetchMode ?: $this->defaultFetchMode;
if ($fetchMode === FetchMode::ASSOCIATIVE) {
return $row;
}
if ($fetchMode === FetchMode::NUMERIC) {
return array_values($row);
}
if ($fetchMode === FetchMode::MIXED) {
return array_merge($row, array_values($row));
}
if ($fetchMode === FetchMode::COLUMN) {
return reset($row);
}
throw new InvalidArgumentException('Invalid fetch-style given for caching result.');
}
$this->saveToCache();
return false;
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE, $fetchArgument, $ctorArgs);
$this->data = $data;
$this->saveToCache();
if ($fetchMode === FetchMode::NUMERIC) {
foreach ($data as $i => $row) {
$data[$i] = array_values($row);
}
} elseif ($fetchMode === FetchMode::MIXED) {
foreach ($data as $i => $row) {
$data[$i] = array_merge($row, array_values($row));
}
} elseif ($fetchMode === FetchMode::COLUMN) {
foreach ($data as $i => $row) {
$data[$i] = reset($row);
}
}
return $data;
}
/**
* Be warned that you will need to call this method until no rows are
* available for caching to happen.
*
* {@inheritdoc}
*
* @deprecated Use fetchOne() instead.
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
}
/**
* Be warned that you will need to call this method until no rows are
* available for caching to happen.
*
* {@inheritdoc}
*/
public function fetchNumeric()
{
$row = $this->doFetch();
if ($row === false) {
return false;
}
return array_values($row);
}
/**
* Be warned that you will need to call this method until no rows are
* available for caching to happen.
*
* {@inheritdoc}
*/
public function fetchAssociative()
{
return $this->doFetch();
}
/**
* Be warned that you will need to call this method until no rows are
* available for caching to happen.
*
* {@inheritdoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllNumeric(): array
{
if ($this->statement instanceof Result) {
$data = $this->statement->fetchAllAssociative();
} else {
$data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE);
}
$this->data = $data;
$this->saveToCache();
return array_map('array_values', $data);
}
/**
* {@inheritdoc}
*/
public function fetchAllAssociative(): array
{
if ($this->statement instanceof Result) {
$data = $this->statement->fetchAllAssociative();
} else {
$data = $this->statement->fetchAll(FetchMode::ASSOCIATIVE);
}
$this->data = $data;
$this->saveToCache();
return $data;
}
/**
* {@inheritdoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
/**
* Returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement
* executed by the corresponding object.
*
* If the last SQL statement executed by the associated Statement object was a SELECT statement,
* some databases may return the number of rows returned by that statement. However,
* this behaviour is not guaranteed for all databases and should not be
* relied on for portable applications.
*
* @return int|string The number of rows.
*/
public function rowCount()
{
assert($this->statement instanceof Statement);
return $this->statement->rowCount();
}
public function free(): void
{
$this->data = null;
}
/**
* @return array<string,mixed>|false
*
* @throws Exception
*/
private function doFetch()
{
if ($this->data === null) {
$this->data = [];
}
if ($this->statement instanceof Result) {
$row = $this->statement->fetchAssociative();
} else {
$row = $this->statement->fetch(FetchMode::ASSOCIATIVE);
}
if ($row !== false) {
$this->data[] = $row;
return $row;
}
$this->saveToCache();
return false;
}
private function saveToCache(): void
{
if ($this->data === null) {
return;
}
$data = $this->resultCache->fetch($this->cacheKey);
if (! $data) {
$data = [];
}
$data[$this->realKey] = $this->data;
$this->resultCache->save($this->cacheKey, $data, $this->lifetime);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Doctrine\DBAL;
use PDO;
/**
* Contains portable column case conversions.
*/
final class ColumnCase
{
/**
* Convert column names to upper case.
*
* @see \PDO::CASE_UPPER
*/
public const UPPER = PDO::CASE_UPPER;
/**
* Convert column names to lower case.
*
* @see \PDO::CASE_LOWER
*/
public const LOWER = PDO::CASE_LOWER;
/**
* This class cannot be instantiated.
*
* @codeCoverageIgnore
*/
private function __construct()
{
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Logging\SQLLogger;
use Doctrine\DBAL\Schema\AbstractAsset;
use Doctrine\Deprecations\Deprecation;
use function preg_match;
/**
* Configuration container for the Doctrine DBAL.
*
* Internal note: When adding a new configuration option just write a getter/setter
* pair and add the option to the _attributes array with a proper default value.
*/
class Configuration
{
/**
* The attributes that are contained in the configuration.
* Values are default values.
*
* @var mixed[]
*/
protected $_attributes = [];
/**
* Sets the SQL logger to use. Defaults to NULL which means SQL logging is disabled.
*
* @return void
*/
public function setSQLLogger(?SQLLogger $logger = null)
{
$this->_attributes['sqlLogger'] = $logger;
}
/**
* Gets the SQL logger that is used.
*
* @return SQLLogger|null
*/
public function getSQLLogger()
{
return $this->_attributes['sqlLogger'] ?? null;
}
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return Cache|null
*/
public function getResultCacheImpl()
{
return $this->_attributes['resultCacheImpl'] ?? null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @return void
*/
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/**
* Sets the filter schema assets expression.
*
* Only include tables/sequences matching the filter expression regexp in
* schema instances generated for the active connection when calling
* {AbstractSchemaManager#createSchema()}.
*
* @deprecated Use Configuration::setSchemaAssetsFilter() instead
*
* @param string|null $filterExpression
*
* @return void
*/
public function setFilterSchemaAssetsExpression($filterExpression)
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3316',
'Configuration::setFilterSchemaAssetsExpression() is deprecated, use setSchemaAssetsFilter() instead.'
);
$this->_attributes['filterSchemaAssetsExpression'] = $filterExpression;
if ($filterExpression) {
$this->_attributes['filterSchemaAssetsExpressionCallable']
= $this->buildSchemaAssetsFilterFromExpression($filterExpression);
} else {
$this->_attributes['filterSchemaAssetsExpressionCallable'] = null;
}
}
/**
* Returns filter schema assets expression.
*
* @deprecated Use Configuration::getSchemaAssetsFilter() instead
*
* @return string|null
*/
public function getFilterSchemaAssetsExpression()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3316',
'Configuration::getFilterSchemaAssetsExpression() is deprecated, use getSchemaAssetsFilter() instead.'
);
return $this->_attributes['filterSchemaAssetsExpression'] ?? null;
}
/**
* @param string $filterExpression
*
* @return callable(string|AbstractAsset)
*/
private function buildSchemaAssetsFilterFromExpression($filterExpression): callable
{
return static function ($assetName) use ($filterExpression) {
if ($assetName instanceof AbstractAsset) {
$assetName = $assetName->getName();
}
return preg_match($filterExpression, $assetName);
};
}
/**
* Sets the callable to use to filter schema assets.
*/
public function setSchemaAssetsFilter(?callable $callable = null): ?callable
{
$this->_attributes['filterSchemaAssetsExpression'] = null;
return $this->_attributes['filterSchemaAssetsExpressionCallable'] = $callable;
}
/**
* Returns the callable to use to filter schema assets.
*/
public function getSchemaAssetsFilter(): ?callable
{
return $this->_attributes['filterSchemaAssetsExpressionCallable'] ?? null;
}
/**
* Sets the default auto-commit mode for connections.
*
* If a connection is in auto-commit mode, then all its SQL statements will be executed and committed as individual
* transactions. Otherwise, its SQL statements are grouped into transactions that are terminated by a call to either
* the method commit or the method rollback. By default, new connections are in auto-commit mode.
*
* @see getAutoCommit
*
* @param bool $autoCommit True to enable auto-commit mode; false to disable it.
*
* @return void
*/
public function setAutoCommit($autoCommit)
{
$this->_attributes['autoCommit'] = (bool) $autoCommit;
}
/**
* Returns the default auto-commit mode for connections.
*
* @see setAutoCommit
*
* @return bool True if auto-commit mode is enabled by default for connections, false otherwise.
*/
public function getAutoCommit()
{
return $this->_attributes['autoCommit'] ?? true;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\DBAL;
/**
* @psalm-immutable
*/
class ConnectionException extends Exception
{
/**
* @return ConnectionException
*/
public static function commitFailedRollbackOnly()
{
return new self('Transaction commit failed because the transaction has been marked for rollback only.');
}
/**
* @return ConnectionException
*/
public static function noActiveTransaction()
{
return new self('There is no active transaction.');
}
/**
* @return ConnectionException
*/
public static function savepointsNotSupported()
{
return new self('Savepoints are not supported by this driver.');
}
/**
* @return ConnectionException
*/
public static function mayNotAlterNestedTransactionWithSavepointsInTransaction()
{
return new self('May not alter the nested transaction with savepoints behavior while a transaction is open.');
}
}

View File

@@ -0,0 +1,101 @@
<?php
namespace Doctrine\DBAL\Connections;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\DriverManager;
use Doctrine\Deprecations\Deprecation;
use InvalidArgumentException;
/**
* @deprecated Use PrimaryReadReplicaConnection instead
*
* @psalm-import-type Params from DriverManager
*/
class MasterSlaveConnection extends PrimaryReadReplicaConnection
{
/**
* Creates Primary Replica Connection.
*
* @internal The connection can be only instantiated by the driver manager.
*
* @param array<string,mixed> $params
* @psalm-param Params $params
* @phpstan-param array<string,mixed> $params
*
* @throws InvalidArgumentException
*/
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
$this->deprecated(self::class, PrimaryReadReplicaConnection::class);
if (isset($params['master'])) {
$this->deprecated('Params key "master"', '"primary"');
$params['primary'] = $params['master'];
}
if (isset($params['slaves'])) {
$this->deprecated('Params key "slaves"', '"replica"');
$params['replica'] = $params['slaves'];
}
if (isset($params['keepSlave'])) {
$this->deprecated('Params key "keepSlave"', '"keepReplica"');
$params['keepReplica'] = $params['keepSlave'];
}
parent::__construct($params, $driver, $config, $eventManager);
}
/**
* Checks if the connection is currently towards the primary or not.
*/
public function isConnectedToMaster(): bool
{
$this->deprecated('isConnectedtoMaster()', 'isConnectedToPrimary()');
return $this->isConnectedToPrimary();
}
/**
* @param string|null $connectionName
*
* @return bool
*/
public function connect($connectionName = null)
{
if ($connectionName === 'master') {
$connectionName = 'primary';
$this->deprecated('connect("master")', 'ensureConnectedToPrimary()');
}
if ($connectionName === 'slave') {
$connectionName = 'replica';
$this->deprecated('connect("slave")', 'ensureConnectedToReplica()');
}
return $this->performConnect($connectionName);
}
private function deprecated(string $thing, string $instead): void
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4054',
'%s is deprecated since doctrine/dbal 2.11 and will be removed in 3.0, use %s instead.',
$thing,
$instead
);
}
}

View File

@@ -0,0 +1,442 @@
<?php
namespace Doctrine\DBAL\Connections;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Configuration;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use InvalidArgumentException;
use function array_rand;
use function assert;
use function count;
use function func_get_args;
/**
* Primary-Replica Connection
*
* Connection can be used with primary-replica setups.
*
* Important for the understanding of this connection should be how and when
* it picks the replica or primary.
*
* 1. Replica if primary was never picked before and ONLY if 'getWrappedConnection'
* or 'executeQuery' is used.
* 2. Primary picked when 'exec', 'executeUpdate', 'executeStatement', 'insert', 'delete', 'update', 'createSavepoint',
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or
* 'prepare' is called.
* 3. If Primary was picked once during the lifetime of the connection it will always get picked afterwards.
* 4. One replica connection is randomly picked ONCE during a request.
*
* ATTENTION: You can write to the replica with this connection if you execute a write query without
* opening up a transaction. For example:
*
* $conn = DriverManager::getConnection(...);
* $conn->executeQuery("DELETE FROM table");
*
* Be aware that Connection#executeQuery is a method specifically for READ
* operations only.
*
* Use Connection#executeStatement for any SQL statement that changes/updates
* state in the database (UPDATE, INSERT, DELETE or DDL statements).
*
* This connection is limited to replica operations using the
* Connection#executeQuery operation only, because it wouldn't be compatible
* with the ORM or SchemaManager code otherwise. Both use all the other
* operations in a context where writes could happen to a replica, which makes
* this restricted approach necessary.
*
* You can manually connect to the primary at any time by calling:
*
* $conn->ensureConnectedToPrimary();
*
* Instantiation through the DriverManager looks like:
*
* @psalm-import-type Params from DriverManager
* @example
*
* $conn = DriverManager::getConnection(array(
* 'wrapperClass' => 'Doctrine\DBAL\Connections\PrimaryReadReplicaConnection',
* 'driver' => 'pdo_mysql',
* 'primary' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'replica' => array(
* array('user' => 'replica1', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'replica2', 'password', 'host' => '', 'dbname' => ''),
* )
* ));
*
* You can also pass 'driverOptions' and any other documented option to each of this drivers
* to pass additional information.
*/
class PrimaryReadReplicaConnection extends Connection
{
/**
* Primary and Replica connection (one of the randomly picked replicas).
*
* @var DriverConnection[]|null[]
*/
protected $connections = ['primary' => null, 'replica' => null];
/**
* You can keep the replica connection and then switch back to it
* during the request if you know what you are doing.
*
* @var bool
*/
protected $keepReplica = false;
/**
* Creates Primary Replica Connection.
*
* @internal The connection can be only instantiated by the driver manager.
*
* @param array<string,mixed> $params
* @psalm-param Params $params
* @phpstan-param array<string,mixed> $params
*
* @throws InvalidArgumentException
*/
public function __construct(
array $params,
Driver $driver,
?Configuration $config = null,
?EventManager $eventManager = null
) {
if (! isset($params['replica'], $params['primary'])) {
throw new InvalidArgumentException('primary or replica configuration missing');
}
if (count($params['replica']) === 0) {
throw new InvalidArgumentException('You have to configure at least one replica.');
}
if (isset($params['driver'])) {
$params['primary']['driver'] = $params['driver'];
foreach ($params['replica'] as $replicaKey => $replica) {
$params['replica'][$replicaKey]['driver'] = $params['driver'];
}
}
$this->keepReplica = (bool) ($params['keepReplica'] ?? false);
parent::__construct($params, $driver, $config, $eventManager);
}
/**
* Checks if the connection is currently towards the primary or not.
*/
public function isConnectedToPrimary(): bool
{
return $this->_conn !== null && $this->_conn === $this->connections['primary'];
}
/**
* @param string|null $connectionName
*
* @return bool
*/
public function connect($connectionName = null)
{
if ($connectionName !== null) {
throw new InvalidArgumentException(
'Passing a connection name as first argument is not supported anymore.'
. ' Use ensureConnectedToPrimary()/ensureConnectedToReplica() instead.'
);
}
return $this->performConnect();
}
protected function performConnect(?string $connectionName = null): bool
{
$requestedConnectionChange = ($connectionName !== null);
$connectionName = $connectionName ?: 'replica';
if ($connectionName !== 'replica' && $connectionName !== 'primary') {
throw new InvalidArgumentException('Invalid option to connect(), only primary or replica allowed.');
}
// If we have a connection open, and this is not an explicit connection
// change request, then abort right here, because we are already done.
// This prevents writes to the replica in case of "keepReplica" option enabled.
if ($this->_conn !== null && ! $requestedConnectionChange) {
return false;
}
$forcePrimaryAsReplica = false;
if ($this->getTransactionNestingLevel() > 0) {
$connectionName = 'primary';
$forcePrimaryAsReplica = true;
}
if (isset($this->connections[$connectionName])) {
$this->_conn = $this->connections[$connectionName];
if ($forcePrimaryAsReplica && ! $this->keepReplica) {
$this->connections['replica'] = $this->_conn;
}
return false;
}
if ($connectionName === 'primary') {
$this->connections['primary'] = $this->_conn = $this->connectTo($connectionName);
// Set replica connection to primary to avoid invalid reads
if (! $this->keepReplica) {
$this->connections['replica'] = $this->connections['primary'];
}
} else {
$this->connections['replica'] = $this->_conn = $this->connectTo($connectionName);
}
if ($this->_eventManager->hasListeners(Events::postConnect)) {
$eventArgs = new ConnectionEventArgs($this);
$this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
}
return true;
}
/**
* Connects to the primary node of the database cluster.
*
* All following statements after this will be executed against the primary node.
*/
public function ensureConnectedToPrimary(): bool
{
return $this->performConnect('primary');
}
/**
* Connects to a replica node of the database cluster.
*
* All following statements after this will be executed against the replica node,
* unless the keepReplica option is set to false and a primary connection
* was already opened.
*/
public function ensureConnectedToReplica(): bool
{
return $this->performConnect('replica');
}
/**
* Connects to a specific connection.
*
* @param string $connectionName
*
* @return DriverConnection
*/
protected function connectTo($connectionName)
{
$params = $this->getParams();
$driverOptions = $params['driverOptions'] ?? [];
$connectionParams = $this->chooseConnectionConfiguration($connectionName, $params);
$user = $connectionParams['user'] ?? null;
$password = $connectionParams['password'] ?? null;
return $this->_driver->connect($connectionParams, $user, $password, $driverOptions);
}
/**
* @param string $connectionName
* @param mixed[] $params
*
* @return mixed
*/
protected function chooseConnectionConfiguration($connectionName, $params)
{
if ($connectionName === 'primary') {
return $params['primary'];
}
$config = $params['replica'][array_rand($params['replica'])];
if (! isset($config['charset']) && isset($params['primary']['charset'])) {
$config['charset'] = $params['primary']['charset'];
}
return $config;
}
/**
* {@inheritDoc}
*
* @deprecated Use {@link executeStatement()} instead.
*/
public function executeUpdate($sql, array $params = [], array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::executeUpdate($sql, $params, $types);
}
/**
* {@inheritDoc}
*/
public function executeStatement($sql, array $params = [], array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::executeStatement($sql, $params, $types);
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
$this->ensureConnectedToPrimary();
return parent::beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
{
$this->ensureConnectedToPrimary();
return parent::commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
$this->ensureConnectedToPrimary();
return parent::rollBack();
}
/**
* {@inheritDoc}
*/
public function delete($table, array $criteria, array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::delete($table, $criteria, $types);
}
/**
* {@inheritDoc}
*/
public function close()
{
unset($this->connections['primary'], $this->connections['replica']);
parent::close();
$this->_conn = null;
$this->connections = ['primary' => null, 'replica' => null];
}
/**
* {@inheritDoc}
*/
public function update($table, array $data, array $criteria, array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::update($table, $data, $criteria, $types);
}
/**
* {@inheritDoc}
*/
public function insert($table, array $data, array $types = [])
{
$this->ensureConnectedToPrimary();
return parent::insert($table, $data, $types);
}
/**
* {@inheritDoc}
*/
public function exec($sql)
{
$this->ensureConnectedToPrimary();
return parent::exec($sql);
}
/**
* {@inheritDoc}
*/
public function createSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::createSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function releaseSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::releaseSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function rollbackSavepoint($savepoint)
{
$this->ensureConnectedToPrimary();
parent::rollbackSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function query()
{
$this->ensureConnectedToPrimary();
assert($this->_conn instanceof DriverConnection);
$args = func_get_args();
$logger = $this->getConfiguration()->getSQLLogger();
if ($logger) {
$logger->startQuery($args[0]);
}
$statement = $this->_conn->query(...$args);
$statement->setFetchMode($this->defaultFetchMode);
if ($logger) {
$logger->stopQuery();
}
return $statement;
}
/**
* {@inheritDoc}
*/
public function prepare($sql)
{
$this->ensureConnectedToPrimary();
return parent::prepare($sql);
}
}

View File

@@ -0,0 +1,314 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Throwable;
use function array_map;
use function bin2hex;
use function get_class;
use function gettype;
use function implode;
use function is_object;
use function is_resource;
use function is_string;
use function json_encode;
use function preg_replace;
use function spl_object_hash;
use function sprintf;
/**
* @deprecated Use {@link Exception} instead
*
* @psalm-immutable
*/
class DBALException extends \Exception
{
/**
* @param string $method
*
* @return Exception
*/
public static function notSupported($method)
{
return new Exception(sprintf("Operation '%s' is not supported by platform.", $method));
}
/**
* @deprecated Use {@link invalidPlatformType()} instead.
*/
public static function invalidPlatformSpecified(): self
{
return new Exception(
"Invalid 'platform' option specified, need to give an instance of " . AbstractPlatform::class . '.'
);
}
/**
* @param mixed $invalidPlatform
*/
public static function invalidPlatformType($invalidPlatform): self
{
if (is_object($invalidPlatform)) {
return new Exception(
sprintf(
"Option 'platform' must be a subtype of '%s', instance of '%s' given",
AbstractPlatform::class,
get_class($invalidPlatform)
)
);
}
return new Exception(
sprintf(
"Option 'platform' must be an object and subtype of '%s'. Got '%s'",
AbstractPlatform::class,
gettype($invalidPlatform)
)
);
}
/**
* Returns a new instance for an invalid specified platform version.
*
* @param string $version The invalid platform version given.
* @param string $expectedFormat The expected platform version format.
*
* @return Exception
*/
public static function invalidPlatformVersionSpecified($version, $expectedFormat)
{
return new Exception(
sprintf(
'Invalid platform version "%s" specified. ' .
'The platform version has to be specified in the format: "%s".',
$version,
$expectedFormat
)
);
}
/**
* @deprecated Passing a PDO instance in connection parameters is deprecated.
*
* @return Exception
*/
public static function invalidPdoInstance()
{
return new Exception(
"The 'pdo' option was used in DriverManager::getConnection() but no " .
'instance of PDO was given.'
);
}
/**
* @param string|null $url The URL that was provided in the connection parameters (if any).
*
* @return Exception
*/
public static function driverRequired($url = null)
{
if ($url) {
return new Exception(
sprintf(
"The options 'driver' or 'driverClass' are mandatory if a connection URL without scheme " .
'is given to DriverManager::getConnection(). Given URL: %s',
$url
)
);
}
return new Exception("The options 'driver' or 'driverClass' are mandatory if no PDO " .
'instance is given to DriverManager::getConnection().');
}
/**
* @param string $unknownDriverName
* @param string[] $knownDrivers
*
* @return Exception
*/
public static function unknownDriver($unknownDriverName, array $knownDrivers)
{
return new Exception("The given 'driver' " . $unknownDriverName . ' is unknown, ' .
'Doctrine currently supports only the following drivers: ' . implode(', ', $knownDrivers));
}
/**
* @deprecated
*
* @param string $sql
* @param mixed[] $params
*
* @return Exception
*/
public static function driverExceptionDuringQuery(Driver $driver, Throwable $driverEx, $sql, array $params = [])
{
$msg = "An exception occurred while executing '" . $sql . "'";
if ($params) {
$msg .= ' with params ' . self::formatParameters($params);
}
$msg .= ":\n\n" . $driverEx->getMessage();
return self::wrapException($driver, $driverEx, $msg);
}
/**
* @deprecated
*
* @return Exception
*/
public static function driverException(Driver $driver, Throwable $driverEx)
{
return self::wrapException($driver, $driverEx, 'An exception occurred in driver: ' . $driverEx->getMessage());
}
/**
* @return Exception
*/
private static function wrapException(Driver $driver, Throwable $driverEx, string $msg)
{
if ($driverEx instanceof DriverException) {
return $driverEx;
}
if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DeprecatedDriverException) {
return $driver->convertException($msg, $driverEx);
}
return new Exception($msg, 0, $driverEx);
}
/**
* Returns a human-readable representation of an array of parameters.
* This properly handles binary data by returning a hex representation.
*
* @param mixed[] $params
*
* @return string
*/
private static function formatParameters(array $params)
{
return '[' . implode(', ', array_map(static function ($param) {
if (is_resource($param)) {
return (string) $param;
}
$json = @json_encode($param);
if (! is_string($json) || $json === 'null' && is_string($param)) {
// JSON encoding failed, this is not a UTF-8 string.
return sprintf('"%s"', preg_replace('/.{2}/', '\\x$0', bin2hex($param)));
}
return $json;
}, $params)) . ']';
}
/**
* @param string $wrapperClass
*
* @return Exception
*/
public static function invalidWrapperClass($wrapperClass)
{
return new Exception("The given 'wrapperClass' " . $wrapperClass . ' has to be a ' .
'subtype of \Doctrine\DBAL\Connection.');
}
/**
* @param string $driverClass
*
* @return Exception
*/
public static function invalidDriverClass($driverClass)
{
return new Exception(
"The given 'driverClass' " . $driverClass . ' has to implement the ' . Driver::class . ' interface.'
);
}
/**
* @param string $tableName
*
* @return Exception
*/
public static function invalidTableName($tableName)
{
return new Exception('Invalid table name specified: ' . $tableName);
}
/**
* @param string $tableName
*
* @return Exception
*/
public static function noColumnsSpecifiedForTable($tableName)
{
return new Exception('No columns specified for table ' . $tableName);
}
/**
* @return Exception
*/
public static function limitOffsetInvalid()
{
return new Exception('Invalid Offset in Limit Query, it has to be larger than or equal to 0.');
}
/**
* @param string $name
*
* @return Exception
*/
public static function typeExists($name)
{
return new Exception('Type ' . $name . ' already exists.');
}
/**
* @param string $name
*
* @return Exception
*/
public static function unknownColumnType($name)
{
return new Exception('Unknown column type "' . $name . '" requested. Any Doctrine type that you use has ' .
'to be registered with \Doctrine\DBAL\Types\Type::addType(). You can get a list of all the ' .
'known types with \Doctrine\DBAL\Types\Type::getTypesMap(). If this error occurs during database ' .
'introspection then you might have forgotten to register all database types for a Doctrine Type. Use ' .
'AbstractPlatform#registerDoctrineTypeMapping() or have your custom types implement ' .
'Type#getMappedDatabaseTypes(). If the type name is empty you might ' .
'have a problem with the cache or forgot some mapping information.');
}
/**
* @param string $name
*
* @return Exception
*/
public static function typeNotFound($name)
{
return new Exception('Type to be overwritten ' . $name . ' does not exist.');
}
public static function typeNotRegistered(Type $type): self
{
return new Exception(
sprintf('Type of the class %s@%s is not registered.', get_class($type), spl_object_hash($type))
);
}
public static function typeAlreadyRegistered(Type $type): self
{
return new Exception(
sprintf('Type of the class %s@%s is already registered.', get_class($type), spl_object_hash($type))
);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
/**
* Driver interface.
* Interface that all DBAL drivers must implement.
*/
interface Driver
{
/**
* Attempts to create a connection with the database.
*
* The usage of NULL to indicate empty username or password is deprecated. Use an empty string instead.
*
* @param mixed[] $params All connection parameters passed by the user.
* @param string|null $username The username to use when connecting.
* @param string|null $password The password to use when connecting.
* @param mixed[] $driverOptions The driver options to use when connecting.
*
* @return \Doctrine\DBAL\Driver\Connection The database connection.
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = []);
/**
* Gets the DatabasePlatform instance that provides all the metadata about
* the platform this driver connects to.
*
* @return AbstractPlatform The database platform.
*/
public function getDatabasePlatform();
/**
* Gets the SchemaManager that can be used to inspect and change the underlying
* database schema of the platform this driver connects to.
*
* @return AbstractSchemaManager
*/
public function getSchemaManager(Connection $conn);
/**
* Gets the name of the driver.
*
* @deprecated
*
* @return string The name of the driver.
*/
public function getName();
/**
* Gets the name of the database connected to for this driver.
*
* @deprecated Use Connection::getDatabase() instead.
*
* @return string The name of the database.
*/
public function getDatabase(Connection $conn);
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as TheDriverException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\DB2SchemaManager;
/**
* Abstract base implementation of the {@link Driver} interface for IBM DB2 based drivers.
*/
abstract class AbstractDB2Driver implements Driver
{
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'];
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new DB2Platform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new DB2SchemaManager($conn);
}
/**
* @param string $message
*
* @return DriverException
*/
public function convertException($message, TheDriverException $exception)
{
return new DriverException($message, $exception);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* @deprecated
*
* @psalm-immutable
*/
class AbstractDriverException extends AbstractException
{
}

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Doctrine\Deprecations\Deprecation;
use Exception as BaseException;
use Throwable;
/**
* Base implementation of the {@link Exception} interface.
*
* @internal
*
* @psalm-immutable
*/
abstract class AbstractException extends BaseException implements DriverException
{
/**
* The driver specific error code.
*
* @var int|string|null
*/
private $errorCode;
/**
* The SQLSTATE of the driver.
*
* @var string|null
*/
private $sqlState;
/**
* @param string $message The driver error message.
* @param string|null $sqlState The SQLSTATE the driver is in at the time the error occurred, if any.
* @param int|string|null $errorCode The driver specific error code if any.
*/
public function __construct($message, $sqlState = null, $errorCode = null, ?Throwable $previous = null)
{
parent::__construct($message, 0, $previous);
$this->errorCode = $errorCode;
$this->sqlState = $sqlState;
}
/**
* {@inheritdoc}
*/
public function getErrorCode()
{
/** @psalm-suppress ImpureMethodCall */
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4112',
'Driver\AbstractException::getErrorCode() is deprecated, use getSQLState() or getCode() instead.'
);
return $this->errorCode;
}
/**
* {@inheritdoc}
*/
public function getSQLState()
{
return $this->sqlState;
}
}

View File

@@ -0,0 +1,256 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\ConnectionLost;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\MariaDb1027Platform;
use Doctrine\DBAL\Platforms\MySQL57Platform;
use Doctrine\DBAL\Platforms\MySQL80Platform;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\MySqlSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function assert;
use function preg_match;
use function stripos;
use function version_compare;
/**
* Abstract base implementation of the {@link Driver} interface for MySQL based drivers.
*/
abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @deprecated
*
* @link https://dev.mysql.com/doc/mysql-errors/8.0/en/client-error-reference.html
* @link https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html
*/
public function convertException($message, DeprecatedDriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1213':
return new DeadlockException($message, $exception);
case '1205':
return new LockWaitTimeoutException($message, $exception);
case '1050':
return new TableExistsException($message, $exception);
case '1051':
case '1146':
return new TableNotFoundException($message, $exception);
case '1216':
case '1217':
case '1451':
case '1452':
case '1701':
return new ForeignKeyConstraintViolationException($message, $exception);
case '1062':
case '1557':
case '1569':
case '1586':
return new UniqueConstraintViolationException($message, $exception);
case '1054':
case '1166':
case '1611':
return new InvalidFieldNameException($message, $exception);
case '1052':
case '1060':
case '1110':
return new NonUniqueFieldNameException($message, $exception);
case '1064':
case '1149':
case '1287':
case '1341':
case '1342':
case '1343':
case '1344':
case '1382':
case '1479':
case '1541':
case '1554':
case '1626':
return new SyntaxErrorException($message, $exception);
case '1044':
case '1045':
case '1046':
case '1049':
case '1095':
case '1142':
case '1143':
case '1227':
case '1370':
case '1429':
case '2002':
case '2005':
return new ConnectionException($message, $exception);
case '2006':
return new ConnectionLost($message, $exception);
case '1048':
case '1121':
case '1138':
case '1171':
case '1252':
case '1263':
case '1364':
case '1566':
return new NotNullConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function createDatabasePlatformForVersion($version)
{
$mariadb = stripos($version, 'mariadb') !== false;
if ($mariadb && version_compare($this->getMariaDbMysqlVersionNumber($version), '10.2.7', '>=')) {
return new MariaDb1027Platform();
}
if (! $mariadb) {
$oracleMysqlVersion = $this->getOracleMysqlVersionNumber($version);
if (version_compare($oracleMysqlVersion, '8', '>=')) {
return new MySQL80Platform();
}
if (version_compare($oracleMysqlVersion, '5.7.9', '>=')) {
return new MySQL57Platform();
}
}
return $this->getDatabasePlatform();
}
/**
* Get a normalized 'version number' from the server string
* returned by Oracle MySQL servers.
*
* @param string $versionString Version string returned by the driver, i.e. '5.7.10'
*
* @throws Exception
*/
private function getOracleMysqlVersionNumber(string $versionString): string
{
if (
! preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/',
$versionString,
$versionParts
)
) {
throw Exception::invalidPlatformVersionSpecified(
$versionString,
'<major_version>.<minor_version>.<patch_version>'
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? null;
if ($majorVersion === '5' && $minorVersion === '7' && $patchVersion === null) {
$patchVersion = '9';
}
return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
}
/**
* Detect MariaDB server version, including hack for some mariadb distributions
* that starts with the prefix '5.5.5-'
*
* @param string $versionString Version string as returned by mariadb server, i.e. '5.5.5-Mariadb-10.0.8-xenial'
*
* @throws Exception
*/
private function getMariaDbMysqlVersionNumber(string $versionString): string
{
if (
! preg_match(
'/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts
)
) {
throw Exception::invalidPlatformVersionSpecified(
$versionString,
'^(?:5\.5\.5-)?(mariadb-)?<major_version>.<minor_version>.<patch_version>'
);
}
return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
}
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
if (isset($params['dbname'])) {
return $params['dbname'];
}
$database = $conn->query('SELECT DATABASE()')->fetchColumn();
assert($database !== false);
return $database;
}
/**
* {@inheritdoc}
*
* @return MySqlPlatform
*/
public function getDatabasePlatform()
{
return new MySqlPlatform();
}
/**
* {@inheritdoc}
*
* @return MySqlSchemaManager
*/
public function getSchemaManager(Connection $conn)
{
return new MySqlSchemaManager($conn);
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Schema\OracleSchemaManager;
/**
* Abstract base implementation of the {@link Driver} interface for Oracle based drivers.
*/
abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver
{
/**
* {@inheritdoc}
*
* @deprecated
*/
public function convertException($message, DeprecatedDriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1':
case '2299':
case '38911':
return new UniqueConstraintViolationException($message, $exception);
case '904':
return new InvalidFieldNameException($message, $exception);
case '918':
case '960':
return new NonUniqueFieldNameException($message, $exception);
case '923':
return new SyntaxErrorException($message, $exception);
case '942':
return new TableNotFoundException($message, $exception);
case '955':
return new TableExistsException($message, $exception);
case '1017':
case '12545':
return new ConnectionException($message, $exception);
case '1400':
return new NotNullConstraintViolationException($message, $exception);
case '2266':
case '2291':
case '2292':
return new ForeignKeyConstraintViolationException($message, $exception);
}
return new DriverException($message, $exception);
}
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['user'];
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new OraclePlatform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new OracleSchemaManager($conn);
}
/**
* Returns an appropriate Easy Connect String for the given parameters.
*
* @param mixed[] $params The connection parameters to return the Easy Connect String for.
*
* @return string
*/
protected function getEasyConnectString(array $params)
{
return (string) EasyConnectString::fromConnectionParameters($params);
}
}

View File

@@ -0,0 +1,121 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\AbstractOracleDriver;
use function implode;
use function is_array;
use function sprintf;
/**
* Represents an Oracle Easy Connect string
*
* @link https://docs.oracle.com/database/121/NETAG/naming.htm
*/
final class EasyConnectString
{
/** @var string */
private $string;
private function __construct(string $string)
{
$this->string = $string;
}
public function __toString(): string
{
return $this->string;
}
/**
* Creates the object from an array representation
*
* @param mixed[] $params
*/
public static function fromArray(array $params): self
{
return new self(self::renderParams($params));
}
/**
* Creates the object from the given DBAL connection parameters.
*
* @param mixed[] $params
*/
public static function fromConnectionParameters(array $params): self
{
if (! empty($params['connectstring'])) {
return new self($params['connectstring']);
}
if (empty($params['host'])) {
return new self($params['dbname'] ?? '');
}
$connectData = [];
if (isset($params['servicename']) || isset($params['dbname'])) {
$serviceKey = 'SID';
if (! empty($params['service'])) {
$serviceKey = 'SERVICE_NAME';
}
$serviceName = $params['servicename'] ?? $params['dbname'];
$connectData[$serviceKey] = $serviceName;
}
if (! empty($params['instancename'])) {
$connectData['INSTANCE_NAME'] = $params['instancename'];
}
if (! empty($params['pooled'])) {
$connectData['SERVER'] = 'POOLED';
}
return self::fromArray([
'DESCRIPTION' => [
'ADDRESS' => [
'PROTOCOL' => 'TCP',
'HOST' => $params['host'],
'PORT' => $params['port'] ?? 1521,
],
'CONNECT_DATA' => $connectData,
],
]);
}
/**
* @param mixed[] $params
*/
private static function renderParams(array $params): string
{
$chunks = [];
foreach ($params as $key => $value) {
$string = self::renderValue($value);
if ($string === '') {
continue;
}
$chunks[] = sprintf('(%s=%s)', $key, $string);
}
return implode('', $chunks);
}
/**
* @param mixed $value
*/
private static function renderValue($value): string
{
if (is_array($value)) {
return self::renderParams($value);
}
return (string) $value;
}
}

View File

@@ -0,0 +1,171 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\PostgreSQL100Platform;
use Doctrine\DBAL\Platforms\PostgreSQL91Platform;
use Doctrine\DBAL\Platforms\PostgreSQL92Platform;
use Doctrine\DBAL\Platforms\PostgreSQL94Platform;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
use Doctrine\DBAL\Schema\PostgreSqlSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function assert;
use function preg_match;
use function strpos;
use function version_compare;
/**
* Abstract base implementation of the {@link Driver} interface for PostgreSQL based drivers.
*/
abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @deprecated
*
* @link http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html
*/
public function convertException($message, DeprecatedDriverException $exception)
{
$sqlState = $exception->getSQLState();
switch ($sqlState) {
case '40001':
case '40P01':
return new DeadlockException($message, $exception);
case '0A000':
// Foreign key constraint violations during a TRUNCATE operation
// are considered "feature not supported" in PostgreSQL.
if (strpos($exception->getMessage(), 'truncate') !== false) {
return new ForeignKeyConstraintViolationException($message, $exception);
}
break;
case '23502':
return new NotNullConstraintViolationException($message, $exception);
case '23503':
return new ForeignKeyConstraintViolationException($message, $exception);
case '23505':
return new UniqueConstraintViolationException($message, $exception);
case '42601':
return new SyntaxErrorException($message, $exception);
case '42702':
return new NonUniqueFieldNameException($message, $exception);
case '42703':
return new InvalidFieldNameException($message, $exception);
case '42P01':
return new TableNotFoundException($message, $exception);
case '42P07':
return new TableExistsException($message, $exception);
case '08006':
return new Exception\ConnectionException($message, $exception);
case '7':
// Prior to fixing https://bugs.php.net/bug.php?id=64705 (PHP 7.3.22 and PHP 7.4.10),
// in some cases (mainly connection errors) the PDO exception wouldn't provide a SQLSTATE via its code.
// The exception code would be always set to 7 here.
// We have to match against the SQLSTATE in the error message in these cases.
if (strpos($exception->getMessage(), 'SQLSTATE[08006]') !== false) {
return new ConnectionException($message, $exception);
}
break;
}
return new DriverException($message, $exception);
}
/**
* {@inheritdoc}
*/
public function createDatabasePlatformForVersion($version)
{
if (! preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts)) {
throw Exception::invalidPlatformVersionSpecified(
$version,
'<major_version>.<minor_version>.<patch_version>'
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? 0;
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
switch (true) {
case version_compare($version, '10.0', '>='):
return new PostgreSQL100Platform();
case version_compare($version, '9.4', '>='):
return new PostgreSQL94Platform();
case version_compare($version, '9.2', '>='):
return new PostgreSQL92Platform();
case version_compare($version, '9.1', '>='):
return new PostgreSQL91Platform();
default:
return new PostgreSqlPlatform();
}
}
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
if (isset($params['dbname'])) {
return $params['dbname'];
}
$database = $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn();
assert($database !== false);
return $database;
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new PostgreSqlPlatform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new PostgreSqlSchemaManager($conn);
}
}

View File

@@ -0,0 +1,170 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DeadlockException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\SQLAnywhere11Platform;
use Doctrine\DBAL\Platforms\SQLAnywhere12Platform;
use Doctrine\DBAL\Platforms\SQLAnywhere16Platform;
use Doctrine\DBAL\Platforms\SQLAnywherePlatform;
use Doctrine\DBAL\Schema\SQLAnywhereSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function assert;
use function preg_match;
use function version_compare;
/**
* Abstract base implementation of the {@link Driver} interface for SAP Sybase SQL Anywhere based drivers.
*
* @deprecated Support for SQLAnywhere will be removed in 3.0.
*/
abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @deprecated
*
* @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html
*/
public function convertException($message, DeprecatedDriverException $exception)
{
switch ($exception->getErrorCode()) {
case '-306':
case '-307':
case '-684':
return new DeadlockException($message, $exception);
case '-210':
case '-1175':
case '-1281':
return new LockWaitTimeoutException($message, $exception);
case '-100':
case '-103':
case '-832':
return new ConnectionException($message, $exception);
case '-143':
return new InvalidFieldNameException($message, $exception);
case '-193':
case '-196':
return new UniqueConstraintViolationException($message, $exception);
case '-194':
case '-198':
return new ForeignKeyConstraintViolationException($message, $exception);
case '-144':
return new NonUniqueFieldNameException($message, $exception);
case '-184':
case '-195':
return new NotNullConstraintViolationException($message, $exception);
case '-131':
return new SyntaxErrorException($message, $exception);
case '-110':
return new TableExistsException($message, $exception);
case '-141':
case '-1041':
return new TableNotFoundException($message, $exception);
}
return new DriverException($message, $exception);
}
/**
* {@inheritdoc}
*/
public function createDatabasePlatformForVersion($version)
{
if (
! preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+)(?:\.(?P<build>\d+))?)?)?/',
$version,
$versionParts
)
) {
throw Exception::invalidPlatformVersionSpecified(
$version,
'<major_version>.<minor_version>.<patch_version>.<build_version>'
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? 0;
$buildVersion = $versionParts['build'] ?? 0;
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion;
switch (true) {
case version_compare($version, '16', '>='):
return new SQLAnywhere16Platform();
case version_compare($version, '12', '>='):
return new SQLAnywhere12Platform();
case version_compare($version, '11', '>='):
return new SQLAnywhere11Platform();
default:
return new SQLAnywherePlatform();
}
}
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
if (isset($params['dbname'])) {
return $params['dbname'];
}
$database = $conn->query('SELECT DB_NAME()')->fetchColumn();
assert($database !== false);
return $database;
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new SQLAnywhere12Platform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new SQLAnywhereSchemaManager($conn);
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as TheDriverException;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\SQLServer2005Platform;
use Doctrine\DBAL\Platforms\SQLServer2008Platform;
use Doctrine\DBAL\Platforms\SQLServer2012Platform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Schema\SQLServerSchemaManager;
use Doctrine\DBAL\VersionAwarePlatformDriver;
use function assert;
use function preg_match;
use function version_compare;
/**
* Abstract base implementation of the {@link Driver} interface for Microsoft SQL Server based drivers.
*/
abstract class AbstractSQLServerDriver implements Driver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*/
public function createDatabasePlatformForVersion($version)
{
if (
! preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+)(?:\.(?P<build>\d+))?)?)?/',
$version,
$versionParts
)
) {
throw Exception::invalidPlatformVersionSpecified(
$version,
'<major_version>.<minor_version>.<patch_version>.<build_version>'
);
}
$majorVersion = $versionParts['major'];
$minorVersion = $versionParts['minor'] ?? 0;
$patchVersion = $versionParts['patch'] ?? 0;
$buildVersion = $versionParts['build'] ?? 0;
$version = $majorVersion . '.' . $minorVersion . '.' . $patchVersion . '.' . $buildVersion;
switch (true) {
case version_compare($version, '11.00.2100', '>='):
return new SQLServer2012Platform();
case version_compare($version, '10.00.1600', '>='):
return new SQLServer2008Platform();
case version_compare($version, '9.00.1399', '>='):
return new SQLServer2005Platform();
default:
return new SQLServerPlatform();
}
}
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
if (isset($params['dbname'])) {
return $params['dbname'];
}
$database = $conn->query('SELECT DB_NAME()')->fetchColumn();
assert($database !== false);
return $database;
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new SQLServer2008Platform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new SQLServerSchemaManager($conn);
}
/**
* @param string $message
*
* @return DriverException
*/
public function convertException($message, TheDriverException $exception)
{
return new DriverException($message, $exception);
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @internal
*
* @psalm-immutable
*/
final class PortWithoutHost extends AbstractDriverException
{
public static function new(): self
{
return new self('Connection port specified without the host');
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as DeprecatedDriverException;
use Doctrine\DBAL\Exception\ConnectionException;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\InvalidFieldNameException;
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
use Doctrine\DBAL\Exception\NonUniqueFieldNameException;
use Doctrine\DBAL\Exception\NotNullConstraintViolationException;
use Doctrine\DBAL\Exception\ReadOnlyException;
use Doctrine\DBAL\Exception\SyntaxErrorException;
use Doctrine\DBAL\Exception\TableExistsException;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\SqliteSchemaManager;
use function strpos;
/**
* Abstract base implementation of the {@link Driver} interface for SQLite based drivers.
*/
abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver
{
/**
* {@inheritdoc}
*
* @deprecated
*
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convertException($message, DeprecatedDriverException $exception)
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new LockWaitTimeoutException($message, $exception);
}
if (
strpos($exception->getMessage(), 'must be unique') !== false ||
strpos($exception->getMessage(), 'is not unique') !== false ||
strpos($exception->getMessage(), 'are not unique') !== false ||
strpos($exception->getMessage(), 'UNIQUE constraint failed') !== false
) {
return new UniqueConstraintViolationException($message, $exception);
}
if (
strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new NotNullConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new TableNotFoundException($message, $exception);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new TableExistsException($message, $exception);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new InvalidFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new NonUniqueFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new SyntaxErrorException($message, $exception);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new ReadOnlyException($message, $exception);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new ConnectionException($message, $exception);
}
return new DriverException($message, $exception);
}
/**
* {@inheritdoc}
*
* @deprecated Use Connection::getDatabase() instead.
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['path'] ?? null;
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new SqlitePlatform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new SqliteSchemaManager($conn);
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\ParameterType;
/**
* Connection interface.
* Driver connections must implement this interface.
*
* This resembles (a subset of) the PDO interface.
*/
interface Connection
{
/**
* Prepares a statement for execution and returns a Statement object.
*
* @param string $sql
*
* @return Statement
*/
public function prepare($sql);
/**
* Executes an SQL statement, returning a result set as a Statement object.
*
* @return Statement
*/
public function query();
/**
* Quotes a string for use in a query.
*
* @param mixed $value
* @param int $type
*
* @return mixed
*/
public function quote($value, $type = ParameterType::STRING);
/**
* Executes an SQL statement and return the number of affected rows.
*
* @param string $sql
*
* @return int|string
*/
public function exec($sql);
/**
* Returns the ID of the last inserted row or sequence value.
*
* @param string|null $name
*
* @return string|int|false
*/
public function lastInsertId($name = null);
/**
* Initiates a transaction.
*
* @return bool TRUE on success or FALSE on failure.
*/
public function beginTransaction();
/**
* Commits a transaction.
*
* @return bool TRUE on success or FALSE on failure.
*/
public function commit();
/**
* Rolls back the current transaction, as initiated by beginTransaction().
*
* @return bool TRUE on success or FALSE on failure.
*/
public function rollBack();
/**
* Returns the error code associated with the last operation on the database handle.
*
* @deprecated The error information is available via exceptions.
*
* @return string|null The error code, or null if no operation has been run on the database handle.
*/
public function errorCode();
/**
* Returns extended error information associated with the last operation on the database handle.
*
* @deprecated The error information is available via exceptions.
*
* @return mixed[]
*/
public function errorInfo();
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* @deprecated Use {@link Exception} instead
*
* @psalm-immutable
*/
interface DriverException extends Exception
{
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Doctrine\DBAL\Driver\DrizzlePDOMySql;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\ParameterType;
/**
* @deprecated
*/
class Connection extends PDOConnection
{
/**
* {@inheritdoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
if ($type === ParameterType::BOOLEAN) {
return $value ? 'true' : 'false';
}
return parent::quote($value, $type);
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Doctrine\DBAL\Driver\DrizzlePDOMySql;
use Doctrine\DBAL\Platforms\DrizzlePlatform;
use Doctrine\DBAL\Schema\DrizzleSchemaManager;
/**
* Drizzle driver using PDO MySql.
*
* @deprecated
*/
class Driver extends \Doctrine\DBAL\Driver\PDOMySql\Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
return new Connection(
$this->constructPdoDsn($params),
$username,
$password,
$driverOptions
);
}
/**
* {@inheritdoc}
*/
public function createDatabasePlatformForVersion($version)
{
return $this->getDatabasePlatform();
}
/**
* {@inheritdoc}
*
* @return DrizzlePlatform
*/
public function getDatabasePlatform()
{
return new DrizzlePlatform();
}
/**
* {@inheritdoc}
*
* @return DrizzleSchemaManager
*/
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new DrizzleSchemaManager($conn);
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
return 'drizzle_pdo_mysql';
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
use Throwable;
/**
* @psalm-immutable
*/
interface Exception extends Throwable
{
/**
* Returns the driver specific error code if available.
*
* @deprecated Use {@link getCode()} or {@link getSQLState()} instead
*
* Returns null if no driver specific error code is available
* for the error raised by the driver.
*
* @return int|string|null
*/
public function getErrorCode();
/**
* Returns the SQLSTATE the driver was in at the time the error occurred.
*
* Returns null if the driver does not provide a SQLSTATE for the error occurred.
*
* @return string|null
*/
public function getSQLState();
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\DriverException as TheDriverException;
use Doctrine\DBAL\Exception\DriverException;
/**
* Contract for a driver that is capable of converting DBAL driver exceptions into standardized DBAL driver exceptions.
*
* @deprecated
*/
interface ExceptionConverterDriver
{
/**
* Converts a given DBAL driver exception into a standardized DBAL driver exception.
*
* It evaluates the vendor specific error code and SQLSTATE and transforms
* it into a unified {@link DriverException} subclass.
*
* @deprecated
*
* @param string $message The DBAL exception message to use.
* @param TheDriverException $exception The DBAL driver exception to convert.
*
* @return DriverException An instance of one of the DriverException subclasses.
*/
public function convertException($message, TheDriverException $exception);
}

View File

@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
/**
* @internal
*/
final class FetchUtils
{
/**
* @return mixed|false
*
* @throws Exception
*/
public static function fetchOne(Result $result)
{
$row = $result->fetchNumeric();
if ($row === false) {
return false;
}
return $row[0];
}
/**
* @return array<int,array<int,mixed>>
*
* @throws Exception
*/
public static function fetchAllNumeric(Result $result): array
{
$rows = [];
while (($row = $result->fetchNumeric()) !== false) {
$rows[] = $row;
}
return $rows;
}
/**
* @return array<int,array<string,mixed>>
*
* @throws Exception
*/
public static function fetchAllAssociative(Result $result): array
{
$rows = [];
while (($row = $result->fetchAssociative()) !== false) {
$rows[] = $row;
}
return $rows;
}
/**
* @return array<int,mixed>
*
* @throws Exception
*/
public static function fetchFirstColumn(Result $result): array
{
$rows = [];
while (($row = $result->fetchOne()) !== false) {
$rows[] = $row;
}
return $rows;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
final class Connection extends DB2Connection
{
}

View File

@@ -0,0 +1,221 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionError;
use Doctrine\DBAL\Driver\IBMDB2\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\IBMDB2\Exception\PrepareFailed;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use stdClass;
use function assert;
use function db2_autocommit;
use function db2_commit;
use function db2_conn_error;
use function db2_conn_errormsg;
use function db2_connect;
use function db2_escape_string;
use function db2_exec;
use function db2_last_insert_id;
use function db2_num_rows;
use function db2_pconnect;
use function db2_prepare;
use function db2_rollback;
use function db2_server_info;
use function error_get_last;
use function func_get_args;
use function is_bool;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
/**
* @deprecated Use {@link Connection} instead
*/
class DB2Connection implements ConnectionInterface, ServerInfoAwareConnection
{
/** @var resource */
private $conn;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param mixed[] $params
* @param string $username
* @param string $password
* @param mixed[] $driverOptions
*
* @throws DB2Exception
*/
public function __construct(array $params, $username, $password, $driverOptions = [])
{
$isPersistent = (isset($params['persistent']) && $params['persistent'] === true);
if ($isPersistent) {
$conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions);
} else {
$conn = db2_connect($params['dbname'], $username, $password, $driverOptions);
}
if ($conn === false) {
throw ConnectionFailed::new();
}
$this->conn = $conn;
}
/**
* {@inheritdoc}
*/
public function getServerVersion()
{
$serverInfo = db2_server_info($this->conn);
assert($serverInfo instanceof stdClass);
return $serverInfo->DBMS_VER;
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4114',
'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.'
);
return false;
}
/**
* {@inheritdoc}
*/
public function prepare($sql)
{
$stmt = @db2_prepare($this->conn, $sql);
if ($stmt === false) {
throw PrepareFailed::new(error_get_last());
}
return new Statement($stmt);
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$sql = $args[0];
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt;
}
/**
* {@inheritdoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
$value = db2_escape_string($value);
if ($type === ParameterType::INTEGER) {
return $value;
}
return "'" . $value . "'";
}
/**
* {@inheritdoc}
*/
public function exec($sql)
{
$stmt = @db2_exec($this->conn, $sql);
if ($stmt === false) {
throw ConnectionError::new($this->conn);
}
return db2_num_rows($stmt);
}
/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
return db2_last_insert_id($this->conn);
}
/**
* {@inheritdoc}
*/
public function beginTransaction()
{
$result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF);
assert(is_bool($result));
return $result;
}
/**
* {@inheritdoc}
*/
public function commit()
{
if (! db2_commit($this->conn)) {
throw ConnectionError::new($this->conn);
}
$result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
assert(is_bool($result));
return $result;
}
/**
* {@inheritdoc}
*/
public function rollBack()
{
if (! db2_rollback($this->conn)) {
throw ConnectionError::new($this->conn);
}
$result = db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
assert(is_bool($result));
return $result;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorCode()
{
return db2_conn_error($this->conn);
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorInfo()
{
return [
0 => db2_conn_errormsg($this->conn),
1 => $this->errorCode(),
];
}
}

View File

@@ -0,0 +1,47 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\Deprecations\Deprecation;
/**
* IBM DB2 Driver.
*
* @deprecated Use {@link Driver} instead
*/
class DB2Driver extends AbstractDB2Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
$params['user'] = $username;
$params['password'] = $password;
$params['dbname'] = DataSourceName::fromConnectionParameters($params)->toString();
return new Connection(
$params,
(string) $username,
(string) $password,
$driverOptions
);
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'ibm_db2';
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @deprecated Use {@link \Doctrine\DBAL\Driver\Exception} instead
*
* @psalm-immutable
*/
class DB2Exception extends AbstractDriverException
{
}

View File

@@ -0,0 +1,562 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCopyStreamToStream;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotCreateTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\CannotWriteToTemporaryFile;
use Doctrine\DBAL\Driver\IBMDB2\Exception\StatementError;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use PDO;
use ReflectionClass;
use ReflectionObject;
use ReflectionProperty;
use ReturnTypeWillChange;
use stdClass;
use function array_change_key_case;
use function assert;
use function db2_bind_param;
use function db2_execute;
use function db2_fetch_array;
use function db2_fetch_assoc;
use function db2_fetch_both;
use function db2_fetch_object;
use function db2_free_result;
use function db2_num_fields;
use function db2_num_rows;
use function db2_stmt_error;
use function db2_stmt_errormsg;
use function error_get_last;
use function fclose;
use function func_get_args;
use function func_num_args;
use function fwrite;
use function gettype;
use function is_int;
use function is_object;
use function is_resource;
use function is_string;
use function ksort;
use function sprintf;
use function stream_copy_to_stream;
use function stream_get_meta_data;
use function strtolower;
use function tmpfile;
use const CASE_LOWER;
use const DB2_BINARY;
use const DB2_CHAR;
use const DB2_LONG;
use const DB2_PARAM_FILE;
use const DB2_PARAM_IN;
/**
* @deprecated Use {@link Statement} instead
*/
class DB2Statement implements IteratorAggregate, StatementInterface, Result
{
/** @var resource */
private $stmt;
/** @var mixed[] */
private $bindParam = [];
/**
* Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement
* and the temporary file handle bound to the underlying statement
*
* @var mixed[][]
*/
private $lobs = [];
/** @var string Name of the default class to instantiate when fetching class instances. */
private $defaultFetchClass = '\stdClass';
/** @var mixed[] Constructor arguments for the default class to instantiate when fetching class instances. */
private $defaultFetchClassCtorArgs = [];
/** @var int */
private $defaultFetchMode = FetchMode::MIXED;
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private $result = false;
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $stmt
*/
public function __construct($stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($param));
switch ($type) {
case ParameterType::INTEGER:
$this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG);
break;
case ParameterType::LARGE_OBJECT:
if (isset($this->lobs[$param])) {
[, $handle] = $this->lobs[$param];
fclose($handle);
}
$handle = $this->createTemporaryFile();
$path = stream_get_meta_data($handle)['uri'];
$this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY);
$this->lobs[$param] = [&$variable, $handle];
break;
default:
$this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR);
break;
}
return true;
}
/**
* @param int $position Parameter position
* @param mixed $variable
*
* @throws DB2Exception
*/
private function bind($position, &$variable, int $parameterType, int $dataType): void
{
$this->bindParam[$position] =& $variable;
if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
throw StatementError::new($this->stmt);
}
}
/**
* {@inheritdoc}
*
* @deprecated Use free() instead.
*/
public function closeCursor()
{
$this->bindParam = [];
if (! db2_free_result($this->stmt)) {
return false;
}
$this->result = false;
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return db2_num_fields($this->stmt) ?: 0;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorCode()
{
return db2_stmt_error();
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorInfo()
{
return [
db2_stmt_errormsg(),
db2_stmt_error(),
];
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if ($params === null) {
ksort($this->bindParam);
$params = [];
foreach ($this->bindParam as $column => $value) {
$params[] = $value;
}
}
foreach ($this->lobs as [$source, $target]) {
if (is_resource($source)) {
$this->copyStreamToStream($source, $target);
continue;
}
$this->writeStringToStream($source, $target);
}
$retval = db2_execute($this->stmt, $params);
foreach ($this->lobs as [, $handle]) {
fclose($handle);
}
$this->lobs = [];
if ($retval === false) {
throw StatementError::new($this->stmt);
}
$this->result = true;
return $retval;
}
/**
* {@inheritdoc}
*
* @deprecated Use one of the fetch- or iterate-related methods.
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->defaultFetchMode = $fetchMode;
$this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass;
$this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
return true;
}
/**
* {@inheritdoc}
*
* @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead.
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
$fetchMode = $fetchMode ?: $this->defaultFetchMode;
switch ($fetchMode) {
case FetchMode::COLUMN:
return $this->fetchColumn();
case FetchMode::MIXED:
return db2_fetch_both($this->stmt);
case FetchMode::ASSOCIATIVE:
return db2_fetch_assoc($this->stmt);
case FetchMode::CUSTOM_OBJECT:
$className = $this->defaultFetchClass;
$ctorArgs = $this->defaultFetchClassCtorArgs;
if (func_num_args() >= 2) {
$args = func_get_args();
$className = $args[1];
$ctorArgs = $args[2] ?? [];
}
$result = db2_fetch_object($this->stmt);
if ($result instanceof stdClass) {
$result = $this->castObject($result, $className, $ctorArgs);
}
return $result;
case FetchMode::NUMERIC:
return db2_fetch_array($this->stmt);
case FetchMode::STANDARD_OBJECT:
return db2_fetch_object($this->stmt);
default:
throw new DB2Exception('Given Fetch-Style ' . $fetchMode . ' is not supported.');
}
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$rows = [];
switch ($fetchMode) {
case FetchMode::CUSTOM_OBJECT:
while (($row = $this->fetch(...func_get_args())) !== false) {
$rows[] = $row;
}
break;
case FetchMode::COLUMN:
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
}
break;
default:
while (($row = $this->fetch($fetchMode)) !== false) {
$rows[] = $row;
}
}
return $rows;
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchOne() instead.
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritDoc}
*/
public function fetchNumeric()
{
if (! $this->result) {
return false;
}
return db2_fetch_array($this->stmt);
}
/**
* {@inheritdoc}
*/
public function fetchAssociative()
{
// do not try fetching from the statement if it's not expected to contain the result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
return db2_fetch_assoc($this->stmt);
}
/**
* {@inheritdoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritdoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return @db2_num_rows($this->stmt) ? : 0;
}
public function free(): void
{
$this->bindParam = [];
db2_free_result($this->stmt);
$this->result = false;
}
/**
* Casts a stdClass object to the given class name mapping its' properties.
*
* @param stdClass $sourceObject Object to cast from.
* @param class-string|object $destinationClass Name of the class or class instance to cast to.
* @param mixed[] $ctorArgs Arguments to use for constructing the destination class instance.
*
* @return object
*
* @throws DB2Exception
*/
private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
{
if (! is_string($destinationClass)) {
if (! is_object($destinationClass)) {
throw new DB2Exception(sprintf(
'Destination class has to be of type string or object, %s given.',
gettype($destinationClass)
));
}
} else {
$destinationClass = new ReflectionClass($destinationClass);
$destinationClass = $destinationClass->newInstanceArgs($ctorArgs);
}
$sourceReflection = new ReflectionObject($sourceObject);
$destinationClassReflection = new ReflectionObject($destinationClass);
/** @var ReflectionProperty[] $destinationProperties */
$destinationProperties = array_change_key_case($destinationClassReflection->getProperties(), CASE_LOWER);
foreach ($sourceReflection->getProperties() as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
// Try to find a case-matching property.
if ($destinationClassReflection->hasProperty($name)) {
$destinationProperty = $destinationClassReflection->getProperty($name);
$destinationProperty->setAccessible(true);
$destinationProperty->setValue($destinationClass, $value);
continue;
}
$name = strtolower($name);
// Try to find a property without matching case.
// Fallback for the driver returning either all uppercase or all lowercase column names.
if (isset($destinationProperties[$name])) {
$destinationProperty = $destinationProperties[$name];
$destinationProperty->setAccessible(true);
$destinationProperty->setValue($destinationClass, $value);
continue;
}
$destinationClass->$name = $value;
}
return $destinationClass;
}
/**
* @return resource
*
* @throws DB2Exception
*/
private function createTemporaryFile()
{
$handle = @tmpfile();
if ($handle === false) {
throw CannotCreateTemporaryFile::new(error_get_last());
}
return $handle;
}
/**
* @param resource $source
* @param resource $target
*
* @throws DB2Exception
*/
private function copyStreamToStream($source, $target): void
{
if (@stream_copy_to_stream($source, $target) === false) {
throw CannotCopyStreamToStream::new(error_get_last());
}
}
/**
* @param resource $target
*
* @throws DB2Exception
*/
private function writeStringToStream(string $string, $target): void
{
if (@fwrite($target, $string) === false) {
throw CannotWriteToTemporaryFile::new(error_get_last());
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
use function implode;
use function sprintf;
use function strpos;
/**
* IBM DB2 DSN
*/
final class DataSourceName
{
/** @var string */
private $string;
private function __construct(string $string)
{
$this->string = $string;
}
public function toString(): string
{
return $this->string;
}
/**
* Creates the object from an array representation
*
* @param array<string,mixed> $params
*/
public static function fromArray(array $params): self
{
$chunks = [];
foreach ($params as $key => $value) {
$chunks[] = sprintf('%s=%s', $key, $value);
}
return new self(implode(';', $chunks));
}
/**
* Creates the object from the given DBAL connection parameters.
*
* @param array<string,mixed> $params
*/
public static function fromConnectionParameters(array $params): self
{
if (isset($params['dbname']) && strpos($params['dbname'], '=') !== false) {
return new self($params['dbname']);
}
$dsnParams = [];
foreach (
[
'host' => 'HOSTNAME',
'port' => 'PORT',
'protocol' => 'PROTOCOL',
'dbname' => 'DATABASE',
'user' => 'UID',
'password' => 'PWD',
] as $dbalParam => $dsnParam
) {
if (! isset($params[$dbalParam])) {
continue;
}
$dsnParams[$dsnParam] = $params[$dbalParam];
}
return self::fromArray($dsnParams);
}
}

View File

@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2;
final class Driver extends DB2Driver
{
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotCopyStreamToStream extends DB2Exception
{
/**
* @psalm-param array{message: string}|null $error
*/
public static function new(?array $error): self
{
$message = 'Could not copy source stream to temporary file';
if ($error !== null) {
$message .= ': ' . $error['message'];
}
return new self($message);
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotCreateTemporaryFile extends DB2Exception
{
/**
* @psalm-param array{message: string}|null $error
*/
public static function new(?array $error): self
{
$message = 'Could not create temporary file';
if ($error !== null) {
$message .= ': ' . $error['message'];
}
return new self($message);
}
}

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\IBMDB2\DB2Exception;
/**
* @internal
*
* @psalm-immutable
*/
final class CannotWriteToTemporaryFile extends DB2Exception
{
/**
* @psalm-param array{message: string}|null $error
*/
public static function new(?array $error): self
{
$message = 'Could not write string to temporary file';
if ($error !== null) {
$message .= ': ' . $error['message'];
}
return new self($message);
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionError extends AbstractException
{
/**
* @param resource $connection
*/
public static function new($connection): self
{
return new self(db2_conn_errormsg($connection), db2_conn_error($connection));
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_conn_error;
use function db2_conn_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends AbstractException
{
public static function new(): self
{
return new self(db2_conn_errormsg(), db2_conn_error());
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
/**
* @internal
*
* @psalm-immutable
*/
final class PrepareFailed extends AbstractException
{
/**
* @psalm-param array{message: string}|null $error
*/
public static function new(?array $error): self
{
if ($error === null) {
return new self('Unknown error');
}
return new self($error['message']);
}
}

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\IBMDB2\Exception;
use Doctrine\DBAL\Driver\AbstractException;
use function db2_stmt_error;
use function db2_stmt_errormsg;
/**
* @internal
*
* @psalm-immutable
*/
final class StatementError extends AbstractException
{
/**
* @param resource $statement
*/
public static function new($statement): self
{
return new self(db2_stmt_errormsg($statement), db2_stmt_error($statement));
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
final class Statement extends DB2Statement
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
final class Connection extends MysqliConnection
{
}

View File

@@ -0,0 +1,36 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Exception;
use Doctrine\Deprecations\Deprecation;
class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
return new Connection($params, (string) $username, (string) $password, $driverOptions);
} catch (MysqliException $e) {
throw Exception::driverException($this, $e);
}
}
/**
* {@inheritdoc}
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'mysqli';
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use mysqli;
use mysqli_sql_exception;
use ReflectionProperty;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionError extends MysqliException
{
public static function new(mysqli $connection): self
{
return new self($connection->error, $connection->sqlstate, $connection->errno);
}
public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
}
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use mysqli;
use mysqli_sql_exception;
use ReflectionProperty;
use function assert;
/**
* @internal
*
* @psalm-immutable
*/
final class ConnectionFailed extends MysqliException
{
public static function new(mysqli $connection): self
{
$error = $connection->connect_error;
assert($error !== null);
return new self($error, 'HY000', $connection->connect_errno);
}
public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
}
}

View File

@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class FailedReadingStreamOffset extends MysqliException
{
public static function new(int $offset): self
{
return new self(sprintf('Failed reading the stream resource for parameter offset %d.', $offset));
}
}

View File

@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class InvalidOption extends MysqliException
{
/**
* @param mixed $option
* @param mixed $value
*/
public static function fromOption($option, $value): self
{
return new self(
sprintf('Failed to set option %d with value "%s"', $option, $value)
);
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use mysqli_sql_exception;
use mysqli_stmt;
use ReflectionProperty;
/**
* @internal
*
* @psalm-immutable
*/
final class StatementError extends MysqliException
{
public static function new(mysqli_stmt $statement): self
{
return new self($statement->error, $statement->sqlstate, $statement->errno);
}
public static function upcast(mysqli_sql_exception $exception): self
{
$p = new ReflectionProperty(mysqli_sql_exception::class, 'sqlstate');
$p->setAccessible(true);
return new self($exception->getMessage(), $p->getValue($exception), $exception->getCode(), $exception);
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\Mysqli\Exception;
use Doctrine\DBAL\Driver\Mysqli\MysqliException;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownType extends MysqliException
{
/**
* @param mixed $type
*/
public static function new($type): self
{
return new self(sprintf('Unknown type, %d given.', $type));
}
}

View File

@@ -0,0 +1,345 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionFailed;
use Doctrine\DBAL\Driver\Mysqli\Exception\InvalidOption;
use Doctrine\DBAL\Driver\PingableConnection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use mysqli;
use mysqli_sql_exception;
use function assert;
use function floor;
use function func_get_args;
use function in_array;
use function ini_get;
use function mysqli_errno;
use function mysqli_error;
use function mysqli_init;
use function mysqli_options;
use function sprintf;
use function stripos;
use const MYSQLI_INIT_COMMAND;
use const MYSQLI_OPT_CONNECT_TIMEOUT;
use const MYSQLI_OPT_LOCAL_INFILE;
use const MYSQLI_OPT_READ_TIMEOUT;
use const MYSQLI_READ_DEFAULT_FILE;
use const MYSQLI_READ_DEFAULT_GROUP;
use const MYSQLI_SERVER_PUBLIC_KEY;
/**
* @deprecated Use {@link Connection} instead
*/
class MysqliConnection implements ConnectionInterface, PingableConnection, ServerInfoAwareConnection
{
/**
* Name of the option to set connection flags
*/
public const OPTION_FLAGS = 'flags';
/** @var mysqli */
private $conn;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param mixed[] $params
* @param string $username
* @param string $password
* @param mixed[] $driverOptions
*
* @throws MysqliException
*/
public function __construct(array $params, $username, $password, array $driverOptions = [])
{
$port = $params['port'] ?? ini_get('mysqli.default_port');
// Fallback to default MySQL port if not given.
if (! $port) {
$port = 3306;
}
$socket = $params['unix_socket'] ?? ini_get('mysqli.default_socket');
$dbname = $params['dbname'] ?? null;
$flags = $driverOptions[static::OPTION_FLAGS] ?? 0;
$conn = mysqli_init();
assert($conn !== false);
$this->conn = $conn;
$this->setSecureConnection($params);
$this->setDriverOptions($driverOptions);
try {
$success = @$this->conn
->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags);
} catch (mysqli_sql_exception $e) {
throw ConnectionFailed::upcast($e);
}
if (! $success) {
throw ConnectionFailed::new($this->conn);
}
if (! isset($params['charset'])) {
return;
}
$this->conn->set_charset($params['charset']);
}
/**
* Retrieves mysqli native resource handle.
*
* Could be used if part of your application is not using DBAL.
*
* @return mysqli
*/
public function getWrappedResourceHandle()
{
return $this->conn;
}
/**
* {@inheritdoc}
*
* The server version detection includes a special case for MariaDB
* to support '5.5.5-' prefixed versions introduced in Maria 10+
*
* @link https://jira.mariadb.org/browse/MDEV-4088
*/
public function getServerVersion()
{
$serverInfos = $this->conn->get_server_info();
if (stripos($serverInfos, 'mariadb') !== false) {
return $serverInfos;
}
$majorVersion = floor($this->conn->server_version / 10000);
$minorVersion = floor(($this->conn->server_version - $majorVersion * 10000) / 100);
$patchVersion = floor($this->conn->server_version - $majorVersion * 10000 - $minorVersion * 100);
return $majorVersion . '.' . $minorVersion . '.' . $patchVersion;
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4114',
'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.'
);
return false;
}
/**
* {@inheritdoc}
*/
public function prepare($sql)
{
return new Statement($this->conn, $sql);
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$sql = $args[0];
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt;
}
/**
* {@inheritdoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
return "'" . $this->conn->escape_string($value) . "'";
}
/**
* {@inheritdoc}
*/
public function exec($sql)
{
try {
$result = $this->conn->query($sql);
} catch (mysqli_sql_exception $e) {
throw ConnectionError::upcast($e);
}
if ($result === false) {
throw ConnectionError::new($this->conn);
}
return $this->conn->affected_rows;
}
/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
return $this->conn->insert_id;
}
/**
* {@inheritdoc}
*/
public function beginTransaction()
{
$this->conn->query('START TRANSACTION');
return true;
}
/**
* {@inheritdoc}
*/
public function commit()
{
try {
return $this->conn->commit();
} catch (mysqli_sql_exception $e) {
return false;
}
}
/**
* {@inheritdoc}non-PHPdoc)
*/
public function rollBack()
{
try {
return $this->conn->rollback();
} catch (mysqli_sql_exception $e) {
return false;
}
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*
* @return int
*/
public function errorCode()
{
return $this->conn->errno;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*
* @return string
*/
public function errorInfo()
{
return $this->conn->error;
}
/**
* Apply the driver options to the connection.
*
* @param mixed[] $driverOptions
*
* @throws MysqliException When one of of the options is not supported.
* @throws MysqliException When applying doesn't work - e.g. due to incorrect value.
*/
private function setDriverOptions(array $driverOptions = []): void
{
$supportedDriverOptions = [
MYSQLI_OPT_CONNECT_TIMEOUT,
MYSQLI_OPT_LOCAL_INFILE,
MYSQLI_OPT_READ_TIMEOUT,
MYSQLI_INIT_COMMAND,
MYSQLI_READ_DEFAULT_FILE,
MYSQLI_READ_DEFAULT_GROUP,
MYSQLI_SERVER_PUBLIC_KEY,
];
$exceptionMsg = "%s option '%s' with value '%s'";
foreach ($driverOptions as $option => $value) {
if ($option === static::OPTION_FLAGS) {
continue;
}
if (! in_array($option, $supportedDriverOptions, true)) {
throw InvalidOption::fromOption($option, $value);
}
if (@mysqli_options($this->conn, $option, $value)) {
continue;
}
$msg = sprintf($exceptionMsg, 'Failed to set', $option, $value);
$msg .= sprintf(', error: %s (%d)', mysqli_error($this->conn), mysqli_errno($this->conn));
throw new MysqliException(
$msg,
$this->conn->sqlstate,
$this->conn->errno
);
}
}
/**
* Pings the server and re-connects when `mysqli.reconnect = 1`
*
* @deprecated
*
* @return bool
*/
public function ping()
{
return $this->conn->ping();
}
/**
* Establish a secure connection
*
* @param array<string,string> $params
*
* @throws MysqliException
*/
private function setSecureConnection(array $params): void
{
if (
! isset($params['ssl_key']) &&
! isset($params['ssl_cert']) &&
! isset($params['ssl_ca']) &&
! isset($params['ssl_capath']) &&
! isset($params['ssl_cipher'])
) {
return;
}
$this->conn->ssl_set(
$params['ssl_key'] ?? '',
$params['ssl_cert'] ?? '',
$params['ssl_ca'] ?? '',
$params['ssl_capath'] ?? '',
$params['ssl_cipher'] ?? ''
);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @deprecated Use {@link \Doctrine\DBAL\Driver\Exception} instead
*
* @psalm-immutable
*/
class MysqliException extends AbstractDriverException
{
}

View File

@@ -0,0 +1,583 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\Mysqli\Exception\ConnectionError;
use Doctrine\DBAL\Driver\Mysqli\Exception\FailedReadingStreamOffset;
use Doctrine\DBAL\Driver\Mysqli\Exception\StatementError;
use Doctrine\DBAL\Driver\Mysqli\Exception\UnknownType;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use mysqli;
use mysqli_sql_exception;
use mysqli_stmt;
use PDO;
use ReturnTypeWillChange;
use function array_combine;
use function array_fill;
use function assert;
use function count;
use function feof;
use function fread;
use function get_resource_type;
use function is_array;
use function is_int;
use function is_resource;
use function sprintf;
use function str_repeat;
/**
* @deprecated Use {@link Statement} instead
*/
class MysqliStatement implements IteratorAggregate, StatementInterface, Result
{
/** @var string[] */
protected static $_paramTypeMap = [
ParameterType::ASCII => 's',
ParameterType::STRING => 's',
ParameterType::BINARY => 's',
ParameterType::BOOLEAN => 'i',
ParameterType::NULL => 's',
ParameterType::INTEGER => 'i',
ParameterType::LARGE_OBJECT => 'b',
];
/** @var mysqli */
protected $_conn;
/** @var mysqli_stmt */
protected $_stmt;
/** @var string[]|false|null */
protected $_columnNames;
/** @var mixed[] */
protected $_rowBindedValues = [];
/** @var mixed[] */
protected $_bindedValues;
/** @var string */
protected $types;
/**
* Contains ref values for bindValue().
*
* @var mixed[]
*/
protected $_values = [];
/** @var int */
protected $_defaultFetchMode = FetchMode::MIXED;
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private $result = false;
/**
* @internal The statement can be only instantiated by its driver connection.
*
* @param string $prepareString
*
* @throws MysqliException
*/
public function __construct(mysqli $conn, $prepareString)
{
$this->_conn = $conn;
try {
$stmt = $conn->prepare($prepareString);
} catch (mysqli_sql_exception $e) {
throw ConnectionError::upcast($e);
}
if ($stmt === false) {
throw ConnectionError::new($this->_conn);
}
$this->_stmt = $stmt;
$paramCount = $this->_stmt->param_count;
$this->types = str_repeat('s', $paramCount);
$this->_bindedValues = array_fill(1, $paramCount, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
assert(is_int($param));
if (! isset(self::$_paramTypeMap[$type])) {
throw UnknownType::new($type);
}
$this->_bindedValues[$param] =& $variable;
$this->types[$param - 1] = self::$_paramTypeMap[$type];
return true;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
assert(is_int($param));
if (! isset(self::$_paramTypeMap[$type])) {
throw UnknownType::new($type);
}
$this->_values[$param] = $value;
$this->_bindedValues[$param] =& $this->_values[$param];
$this->types[$param - 1] = self::$_paramTypeMap[$type];
return true;
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if ($params !== null && count($params) > 0) {
if (! $this->bindUntypedValues($params)) {
throw StatementError::new($this->_stmt);
}
} elseif (count($this->_bindedValues) > 0) {
$this->bindTypedParameters();
}
try {
$result = $this->_stmt->execute();
} catch (mysqli_sql_exception $e) {
throw StatementError::upcast($e);
}
if (! $result) {
throw StatementError::new($this->_stmt);
}
if ($this->_columnNames === null) {
$meta = $this->_stmt->result_metadata();
if ($meta !== false) {
$fields = $meta->fetch_fields();
assert(is_array($fields));
$columnNames = [];
foreach ($fields as $col) {
$columnNames[] = $col->name;
}
$meta->free();
$this->_columnNames = $columnNames;
} else {
$this->_columnNames = false;
}
}
if ($this->_columnNames !== false) {
// Store result of every execution which has it. Otherwise it will be impossible
// to execute a new statement in case if the previous one has non-fetched rows
// @link http://dev.mysql.com/doc/refman/5.7/en/commands-out-of-sync.html
$this->_stmt->store_result();
// Bind row values _after_ storing the result. Otherwise, if mysqli is compiled with libmysql,
// it will have to allocate as much memory as it may be needed for the given column type
// (e.g. for a LONGBLOB column it's 4 gigabytes)
// @link https://bugs.php.net/bug.php?id=51386#1270673122
//
// Make sure that the values are bound after each execution. Otherwise, if closeCursor() has been
// previously called on the statement, the values are unbound making the statement unusable.
//
// It's also important that row values are bound after _each_ call to store_result(). Otherwise,
// if mysqli is compiled with libmysql, subsequently fetched string values will get truncated
// to the length of the ones fetched during the previous execution.
$this->_rowBindedValues = array_fill(0, count($this->_columnNames), null);
$refs = [];
foreach ($this->_rowBindedValues as $key => &$value) {
$refs[$key] =& $value;
}
if (! $this->_stmt->bind_result(...$refs)) {
throw StatementError::new($this->_stmt);
}
}
$this->result = true;
return true;
}
/**
* Binds parameters with known types previously bound to the statement
*/
private function bindTypedParameters(): void
{
$streams = $values = [];
$types = $this->types;
foreach ($this->_bindedValues as $parameter => $value) {
assert(is_int($parameter));
if (! isset($types[$parameter - 1])) {
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
if ($types[$parameter - 1] === static::$_paramTypeMap[ParameterType::LARGE_OBJECT]) {
if (is_resource($value)) {
if (get_resource_type($value) !== 'stream') {
throw new InvalidArgumentException(
'Resources passed with the LARGE_OBJECT parameter type must be stream resources.'
);
}
$streams[$parameter] = $value;
$values[$parameter] = null;
continue;
}
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
$values[$parameter] = $value;
}
if (! $this->_stmt->bind_param($types, ...$values)) {
throw StatementError::new($this->_stmt);
}
$this->sendLongData($streams);
}
/**
* Handle $this->_longData after regular query parameters have been bound
*
* @param array<int, resource> $streams
*
* @throws MysqliException
*/
private function sendLongData(array $streams): void
{
foreach ($streams as $paramNr => $stream) {
while (! feof($stream)) {
$chunk = fread($stream, 8192);
if ($chunk === false) {
throw FailedReadingStreamOffset::new($paramNr);
}
if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) {
throw StatementError::new($this->_stmt);
}
}
}
}
/**
* Binds a array of values to bound parameters.
*
* @param mixed[] $values
*
* @return bool
*/
private function bindUntypedValues(array $values)
{
$params = [];
$types = str_repeat('s', count($values));
foreach ($values as &$v) {
$params[] =& $v;
}
return $this->_stmt->bind_param($types, ...$params);
}
/**
* @return mixed[]|false|null
*
* @throws StatementError
*/
private function _fetch()
{
try {
$ret = $this->_stmt->fetch();
} catch (mysqli_sql_exception $e) {
throw StatementError::upcast($e);
}
if ($ret === true) {
$values = [];
foreach ($this->_rowBindedValues as $v) {
$values[] = $v;
}
return $values;
}
return $ret;
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
$fetchMode = $fetchMode ?: $this->_defaultFetchMode;
if ($fetchMode === FetchMode::COLUMN) {
return $this->fetchColumn();
}
$values = $this->_fetch();
if ($values === null) {
return false;
}
if ($values === false) {
throw StatementError::new($this->_stmt);
}
if ($fetchMode === FetchMode::NUMERIC) {
return $values;
}
assert(is_array($this->_columnNames));
$assoc = array_combine($this->_columnNames, $values);
assert(is_array($assoc));
switch ($fetchMode) {
case FetchMode::ASSOCIATIVE:
return $assoc;
case FetchMode::MIXED:
return $assoc + $values;
case FetchMode::STANDARD_OBJECT:
return (object) $assoc;
default:
throw new MysqliException(sprintf("Unknown fetch type '%s'", $fetchMode));
}
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$fetchMode = $fetchMode ?: $this->_defaultFetchMode;
$rows = [];
if ($fetchMode === FetchMode::COLUMN) {
while (($row = $this->fetchColumn()) !== false) {
$rows[] = $row;
}
} else {
while (($row = $this->fetch($fetchMode)) !== false) {
$rows[] = $row;
}
}
return $rows;
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchOne() instead.
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function fetchNumeric()
{
// do not try fetching from the statement if it's not expected to contain the result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
$values = $this->_fetch();
if ($values === null) {
return false;
}
if ($values === false) {
throw StatementError::new($this->_stmt);
}
return $values;
}
/**
* {@inheritDoc}
*/
public function fetchAssociative()
{
$values = $this->fetchNumeric();
if ($values === false) {
return false;
}
assert(is_array($this->_columnNames));
$row = array_combine($this->_columnNames, $values);
assert(is_array($row));
return $row;
}
/**
* {@inheritdoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllNumeric(): array
{
return FetchUtils::fetchAllNumeric($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllAssociative(): array
{
return FetchUtils::fetchAllAssociative($this);
}
/**
* {@inheritdoc}
*/
public function fetchFirstColumn(): array
{
return FetchUtils::fetchFirstColumn($this);
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return $this->_stmt->errno;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*
* @return string
*/
public function errorInfo()
{
return $this->_stmt->error;
}
/**
* {@inheritdoc}
*
* @deprecated Use free() instead.
*/
public function closeCursor()
{
$this->free();
return true;
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
if ($this->_columnNames === false) {
return $this->_stmt->affected_rows;
}
return $this->_stmt->num_rows;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return $this->_stmt->field_count;
}
public function free(): void
{
$this->_stmt->free_result();
$this->result = false;
}
/**
* {@inheritdoc}
*
* @deprecated Use one of the fetch- or iterate-related methods.
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->_defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*
* @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead.
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new StatementIterator($this);
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
final class Statement extends MysqliStatement
{
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
final class Connection extends OCI8Connection
{
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Exception;
use Doctrine\Deprecations\Deprecation;
use const OCI_NO_AUTO_COMMIT;
/**
* A Doctrine DBAL driver for the Oracle OCI8 PHP extensions.
*/
class Driver extends AbstractOracleDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
return new Connection(
(string) $username,
(string) $password,
$this->_constructDsn($params),
$params['charset'] ?? '',
$params['sessionMode'] ?? OCI_NO_AUTO_COMMIT,
$params['persistent'] ?? false
);
} catch (OCI8Exception $e) {
throw Exception::driverException($this, $e);
}
}
/**
* Constructs the Oracle DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
protected function _constructDsn(array $params)
{
return $this->getEasyConnectString($params);
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'oci8';
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\OCI8\OCI8Exception;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class NonTerminatedStringLiteral extends OCI8Exception
{
public static function new(int $offset): self
{
return new self(
sprintf(
'The statement contains non-terminated string literal starting at offset %d.',
$offset
)
);
}
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\OCI8\OCI8Exception;
/**
* @internal
*
* @psalm-immutable
*/
final class SequenceDoesNotExist extends OCI8Exception
{
public static function new(): self
{
return new self('lastInsertId failed: Query was executed but no result was returned.');
}
}

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\OCI8\Exception;
use Doctrine\DBAL\Driver\OCI8\OCI8Exception;
use function sprintf;
/**
* @internal
*
* @psalm-immutable
*/
final class UnknownParameterIndex extends OCI8Exception
{
public static function new(int $index): self
{
return new self(
sprintf('Could not find variable mapping with index %d, in the SQL statement', $index)
);
}
}

View File

@@ -0,0 +1,268 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\OCI8\Exception\SequenceDoesNotExist;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use UnexpectedValueException;
use function addcslashes;
use function func_get_args;
use function is_float;
use function is_int;
use function oci_commit;
use function oci_connect;
use function oci_error;
use function oci_pconnect;
use function oci_rollback;
use function oci_server_version;
use function preg_match;
use function sprintf;
use function str_replace;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_NO_AUTO_COMMIT;
/**
* OCI8 implementation of the Connection interface.
*
* @deprecated Use {@link Connection} instead
*/
class OCI8Connection implements ConnectionInterface, ServerInfoAwareConnection
{
/** @var resource */
protected $dbh;
/** @var int */
protected $executeMode = OCI_COMMIT_ON_SUCCESS;
/**
* Creates a Connection to an Oracle Database using oci8 extension.
*
* @internal The connection can be only instantiated by its driver.
*
* @param string $username
* @param string $password
* @param string $db
* @param string $charset
* @param int $sessionMode
* @param bool $persistent
*
* @throws OCI8Exception
*/
public function __construct(
$username,
$password,
$db,
$charset = '',
$sessionMode = OCI_NO_AUTO_COMMIT,
$persistent = false
) {
$dbh = $persistent
? @oci_pconnect($username, $password, $db, $charset, $sessionMode)
: @oci_connect($username, $password, $db, $charset, $sessionMode);
if ($dbh === false) {
throw OCI8Exception::fromErrorInfo(oci_error());
}
$this->dbh = $dbh;
}
/**
* {@inheritdoc}
*
* @throws UnexpectedValueException If the version string returned by the database server
* does not contain a parsable version number.
*/
public function getServerVersion()
{
$version = oci_server_version($this->dbh);
if ($version === false) {
throw OCI8Exception::fromErrorInfo(oci_error($this->dbh));
}
if (! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', $version, $matches)) {
throw new UnexpectedValueException(
sprintf(
'Unexpected database version string "%s". Cannot parse an appropriate version number from it. ' .
'Please report this database version string to the Doctrine team.',
$version
)
);
}
return $matches[1];
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4114',
'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.'
);
return false;
}
/**
* {@inheritdoc}
*/
public function prepare($sql)
{
return new Statement($this->dbh, $sql, $this);
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$sql = $args[0];
//$fetchMode = $args[1];
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt;
}
/**
* {@inheritdoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
if (is_int($value) || is_float($value)) {
return $value;
}
$value = str_replace("'", "''", $value);
return "'" . addcslashes($value, "\000\n\r\\\032") . "'";
}
/**
* {@inheritdoc}
*/
public function exec($sql)
{
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt->rowCount();
}
/**
* {@inheritdoc}
*
* @param string|null $name
*
* @return int|false
*/
public function lastInsertId($name = null)
{
if ($name === null) {
return false;
}
$sql = 'SELECT ' . $name . '.CURRVAL FROM DUAL';
$stmt = $this->query($sql);
$result = $stmt->fetchColumn();
if ($result === false) {
throw SequenceDoesNotExist::new();
}
return (int) $result;
}
/**
* Returns the current execution mode.
*
* @internal
*
* @return int
*/
public function getExecuteMode()
{
return $this->executeMode;
}
/**
* {@inheritdoc}
*/
public function beginTransaction()
{
$this->executeMode = OCI_NO_AUTO_COMMIT;
return true;
}
/**
* {@inheritdoc}
*/
public function commit()
{
if (! oci_commit($this->dbh)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo());
}
$this->executeMode = OCI_COMMIT_ON_SUCCESS;
return true;
}
/**
* {@inheritdoc}
*/
public function rollBack()
{
if (! oci_rollback($this->dbh)) {
throw OCI8Exception::fromErrorInfo($this->errorInfo());
}
$this->executeMode = OCI_COMMIT_ON_SUCCESS;
return true;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorCode()
{
$error = oci_error($this->dbh);
if ($error !== false) {
return $error['code'];
}
return null;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorInfo()
{
$error = oci_error($this->dbh);
if ($error === false) {
return [];
}
return $error;
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* @deprecated Use {@link \Doctrine\DBAL\Driver\Exception} instead
*
* @psalm-immutable
*/
class OCI8Exception extends AbstractDriverException
{
/**
* @param mixed[]|false $error
*
* @return OCI8Exception
*/
public static function fromErrorInfo($error)
{
if ($error === false) {
return new self('Database error occurred but no error information was retrieved from the driver.');
}
return new self($error['message'], null, $error['code']);
}
}

View File

@@ -0,0 +1,655 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\FetchUtils;
use Doctrine\DBAL\Driver\OCI8\Exception\NonTerminatedStringLiteral;
use Doctrine\DBAL\Driver\OCI8\Exception\UnknownParameterIndex;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use ReturnTypeWillChange;
use function array_key_exists;
use function assert;
use function count;
use function implode;
use function is_int;
use function is_resource;
use function oci_bind_by_name;
use function oci_cancel;
use function oci_error;
use function oci_execute;
use function oci_fetch_all;
use function oci_fetch_array;
use function oci_fetch_object;
use function oci_new_descriptor;
use function oci_num_fields;
use function oci_num_rows;
use function oci_parse;
use function preg_match;
use function preg_quote;
use function substr;
use const OCI_ASSOC;
use const OCI_B_BIN;
use const OCI_B_BLOB;
use const OCI_BOTH;
use const OCI_D_LOB;
use const OCI_FETCHSTATEMENT_BY_COLUMN;
use const OCI_FETCHSTATEMENT_BY_ROW;
use const OCI_NUM;
use const OCI_RETURN_LOBS;
use const OCI_RETURN_NULLS;
use const OCI_TEMP_BLOB;
use const PREG_OFFSET_CAPTURE;
use const SQLT_CHR;
/**
* The OCI8 implementation of the Statement interface.
*
* @deprecated Use {@link Statement} instead
*/
class OCI8Statement implements IteratorAggregate, StatementInterface, Result
{
/** @var resource */
protected $_dbh;
/** @var resource */
protected $_sth;
/** @var OCI8Connection */
protected $_conn;
/**
* @deprecated
*
* @var string
*/
protected static $_PARAM = ':param';
/** @var int[] */
protected static $fetchModeMap = [
FetchMode::MIXED => OCI_BOTH,
FetchMode::ASSOCIATIVE => OCI_ASSOC,
FetchMode::NUMERIC => OCI_NUM,
FetchMode::COLUMN => OCI_NUM,
];
/** @var int */
protected $_defaultFetchMode = FetchMode::MIXED;
/** @var string[] */
protected $_paramMap = [];
/**
* Holds references to bound parameter values.
*
* This is a new requirement for PHP7's oci8 extension that prevents bound values from being garbage collected.
*
* @var mixed[]
*/
private $boundValues = [];
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private $result = false;
/**
* Creates a new OCI8Statement that uses the given connection handle and SQL statement.
*
* @internal The statement can be only instantiated by its driver connection.
*
* @param resource $dbh The connection handle.
* @param string $query The SQL query.
*/
public function __construct($dbh, $query, OCI8Connection $conn)
{
[$query, $paramMap] = self::convertPositionalToNamedPlaceholders($query);
$stmt = oci_parse($dbh, $query);
assert(is_resource($stmt));
$this->_sth = $stmt;
$this->_dbh = $dbh;
$this->_paramMap = $paramMap;
$this->_conn = $conn;
}
/**
* Converts positional (?) into named placeholders (:param<num>).
*
* Oracle does not support positional parameters, hence this method converts all
* positional parameters into artificially named parameters. Note that this conversion
* is not perfect. All question marks (?) in the original statement are treated as
* placeholders and converted to a named parameter.
*
* The algorithm uses a state machine with two possible states: InLiteral and NotInLiteral.
* Question marks inside literal strings are therefore handled correctly by this method.
* This comes at a cost, the whole sql statement has to be looped over.
*
* @internal
*
* @param string $statement The SQL statement to convert.
*
* @return mixed[] [0] => the statement value (string), [1] => the paramMap value (array).
*
* @throws OCI8Exception
*
* @todo extract into utility class in Doctrine\DBAL\Util namespace
* @todo review and test for lost spaces. we experienced missing spaces with oci8 in some sql statements.
*/
public static function convertPositionalToNamedPlaceholders($statement)
{
$fragmentOffset = $tokenOffset = 0;
$fragments = $paramMap = [];
$currentLiteralDelimiter = null;
do {
if (! $currentLiteralDelimiter) {
$result = self::findPlaceholderOrOpeningQuote(
$statement,
$tokenOffset,
$fragmentOffset,
$fragments,
$currentLiteralDelimiter,
$paramMap
);
} else {
$result = self::findClosingQuote($statement, $tokenOffset, $currentLiteralDelimiter);
}
} while ($result);
if ($currentLiteralDelimiter !== null) {
throw NonTerminatedStringLiteral::new($tokenOffset - 1);
}
$fragments[] = substr($statement, $fragmentOffset);
$statement = implode('', $fragments);
return [$statement, $paramMap];
}
/**
* Finds next placeholder or opening quote.
*
* @param string $statement The SQL statement to parse
* @param int $tokenOffset The offset to start searching from
* @param int $fragmentOffset The offset to build the next fragment from
* @param string[] $fragments Fragments of the original statement
* not containing placeholders
* @param string|null $currentLiteralDelimiter The delimiter of the current string literal
* or NULL if not currently in a literal
* @param array<int, string> $paramMap Mapping of the original parameter positions
* to their named replacements
*
* @return bool Whether the token was found
*/
private static function findPlaceholderOrOpeningQuote(
$statement,
&$tokenOffset,
&$fragmentOffset,
&$fragments,
&$currentLiteralDelimiter,
&$paramMap
) {
$token = self::findToken($statement, $tokenOffset, '/[?\'"]/');
if (! $token) {
return false;
}
if ($token === '?') {
$position = count($paramMap) + 1;
$param = ':param' . $position;
$fragments[] = substr($statement, $fragmentOffset, $tokenOffset - $fragmentOffset);
$fragments[] = $param;
$paramMap[$position] = $param;
$tokenOffset += 1;
$fragmentOffset = $tokenOffset;
return true;
}
$currentLiteralDelimiter = $token;
++$tokenOffset;
return true;
}
/**
* Finds closing quote
*
* @param string $statement The SQL statement to parse
* @param int $tokenOffset The offset to start searching from
* @param string $currentLiteralDelimiter The delimiter of the current string literal
*
* @return bool Whether the token was found
*
* @param-out string|null $currentLiteralDelimiter
*/
private static function findClosingQuote(
$statement,
&$tokenOffset,
&$currentLiteralDelimiter
) {
$token = self::findToken(
$statement,
$tokenOffset,
'/' . preg_quote($currentLiteralDelimiter, '/') . '/'
);
if (! $token) {
return false;
}
$currentLiteralDelimiter = null;
++$tokenOffset;
return true;
}
/**
* Finds the token described by regex starting from the given offset. Updates the offset with the position
* where the token was found.
*
* @param string $statement The SQL statement to parse
* @param int $offset The offset to start searching from
* @param string $regex The regex containing token pattern
*
* @return string|null Token or NULL if not found
*/
private static function findToken($statement, &$offset, $regex)
{
if (preg_match($regex, $statement, $matches, PREG_OFFSET_CAPTURE, $offset)) {
$offset = $matches[0][1];
return $matches[0][0];
}
return null;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null)
{
if (is_int($param)) {
if (! isset($this->_paramMap[$param])) {
throw UnknownParameterIndex::new($param);
}
$param = $this->_paramMap[$param];
}
if ($type === ParameterType::LARGE_OBJECT) {
$lob = oci_new_descriptor($this->_dbh, OCI_D_LOB);
$lob->writeTemporary($variable, OCI_TEMP_BLOB);
$variable =& $lob;
}
$this->boundValues[$param] =& $variable;
return oci_bind_by_name(
$this->_sth,
$param,
$variable,
$length ?? -1,
$this->convertParameterType($type)
);
}
/**
* Converts DBAL parameter type to oci8 parameter type
*/
private function convertParameterType(int $type): int
{
switch ($type) {
case ParameterType::BINARY:
return OCI_B_BIN;
case ParameterType::LARGE_OBJECT:
return OCI_B_BLOB;
default:
return SQLT_CHR;
}
}
/**
* {@inheritdoc}
*
* @deprecated Use free() instead.
*/
public function closeCursor()
{
$this->free();
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return oci_num_fields($this->_sth) ?: 0;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorCode()
{
$error = oci_error($this->_sth);
if ($error !== false) {
$error = $error['code'];
}
return $error;
}
/**
* {@inheritdoc}
*
* @deprecated The error information is available via exceptions.
*/
public function errorInfo()
{
$error = oci_error($this->_sth);
if ($error === false) {
return [];
}
return $error;
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if ($params) {
$hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) {
if ($hasZeroIndex && is_int($key)) {
$this->bindValue($key + 1, $val);
} else {
$this->bindValue($key, $val);
}
}
}
$ret = @oci_execute($this->_sth, $this->_conn->getExecuteMode());
if (! $ret) {
throw OCI8Exception::fromErrorInfo($this->errorInfo());
}
$this->result = true;
return $ret;
}
/**
* {@inheritdoc}
*
* @deprecated Use one of the fetch- or iterate-related methods.
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->_defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*
* @deprecated Use iterateNumeric(), iterateAssociative() or iterateColumn() instead.
*/
#[ReturnTypeWillChange]
public function getIterator()
{
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
$fetchMode = $fetchMode ?: $this->_defaultFetchMode;
if ($fetchMode === FetchMode::COLUMN) {
return $this->fetchColumn();
}
if ($fetchMode === FetchMode::STANDARD_OBJECT) {
return oci_fetch_object($this->_sth);
}
if (! isset(self::$fetchModeMap[$fetchMode])) {
throw new InvalidArgumentException('Invalid fetch style: ' . $fetchMode);
}
return oci_fetch_array(
$this->_sth,
self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | OCI_RETURN_LOBS
);
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$fetchMode = $fetchMode ?: $this->_defaultFetchMode;
$result = [];
if ($fetchMode === FetchMode::STANDARD_OBJECT) {
while ($row = $this->fetch($fetchMode)) {
$result[] = $row;
}
return $result;
}
if (! isset(self::$fetchModeMap[$fetchMode])) {
throw new InvalidArgumentException('Invalid fetch style: ' . $fetchMode);
}
if (self::$fetchModeMap[$fetchMode] === OCI_BOTH) {
while ($row = $this->fetch($fetchMode)) {
$result[] = $row;
}
} else {
$fetchStructure = OCI_FETCHSTATEMENT_BY_ROW;
if ($fetchMode === FetchMode::COLUMN) {
$fetchStructure = OCI_FETCHSTATEMENT_BY_COLUMN;
}
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if (! $this->result) {
return [];
}
oci_fetch_all(
$this->_sth,
$result,
0,
-1,
self::$fetchModeMap[$fetchMode] | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS
);
if ($fetchMode === FetchMode::COLUMN) {
$result = $result[0];
}
}
return $result;
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchOne() instead.
*/
public function fetchColumn($columnIndex = 0)
{
// do not try fetching from the statement if it's not expected to contain result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
$row = oci_fetch_array($this->_sth, OCI_NUM | OCI_RETURN_NULLS | OCI_RETURN_LOBS);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return oci_num_rows($this->_sth) ?: 0;
}
/**
* {@inheritdoc}
*/
public function fetchNumeric()
{
return $this->doFetch(OCI_NUM);
}
/**
* {@inheritdoc}
*/
public function fetchAssociative()
{
return $this->doFetch(OCI_ASSOC);
}
/**
* {@inheritdoc}
*/
public function fetchOne()
{
return FetchUtils::fetchOne($this);
}
/**
* {@inheritdoc}
*/
public function fetchAllNumeric(): array
{
return $this->doFetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_ROW);
}
/**
* {@inheritdoc}
*/
public function fetchAllAssociative(): array
{
return $this->doFetchAll(OCI_ASSOC, OCI_FETCHSTATEMENT_BY_ROW);
}
/**
* {@inheritdoc}
*/
public function fetchFirstColumn(): array
{
return $this->doFetchAll(OCI_NUM, OCI_FETCHSTATEMENT_BY_COLUMN)[0];
}
public function free(): void
{
// not having the result means there's nothing to close
if (! $this->result) {
return;
}
oci_cancel($this->_sth);
$this->result = false;
}
/**
* @return mixed|false
*/
private function doFetch(int $mode)
{
// do not try fetching from the statement if it's not expected to contain the result
// in order to prevent exceptional situation
if (! $this->result) {
return false;
}
return oci_fetch_array(
$this->_sth,
$mode | OCI_RETURN_NULLS | OCI_RETURN_LOBS
);
}
/**
* @return array<mixed>
*/
private function doFetchAll(int $mode, int $fetchStructure): array
{
// do not try fetching from the statement if it's not expected to contain the result
// in order to prevent exceptional situation
if (! $this->result) {
return [];
}
oci_fetch_all(
$this->_sth,
$result,
0,
-1,
$mode | OCI_RETURN_NULLS | $fetchStructure | OCI_RETURN_LOBS
);
return $result;
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
final class Statement extends OCI8Statement
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDOConnection;
class Connection extends PDOConnection
{
}

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDOException;
/**
* @internal
*
* @psalm-immutable
*/
final class Exception extends PDOException
{
public static function new(\PDOException $exception): self
{
return new self($exception);
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\MySQL;
use Doctrine\DBAL\Driver\PDOMySql;
final class Driver extends PDOMySql\Driver
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\OCI;
use Doctrine\DBAL\Driver\PDOOracle;
final class Driver extends PDOOracle\Driver
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\PgSQL;
use Doctrine\DBAL\Driver\PDOPgSql;
final class Driver extends PDOPgSql\Driver
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\PDOSqlsrv;
final class Connection extends PDOSqlsrv\Connection
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\PDOSqlsrv;
final class Driver extends PDOSqlsrv\Driver
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLSrv;
use Doctrine\DBAL\Driver\PDOSqlsrv;
final class Statement extends PDOSqlsrv\Statement
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO\SQLite;
use Doctrine\DBAL\Driver\PDOSqlite;
final class Driver extends PDOSqlite\Driver
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\PDOStatement;
class Statement extends PDOStatement
{
}

View File

@@ -0,0 +1,150 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\PDO\Statement;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use PDO;
use PDOException;
use PDOStatement;
use ReturnTypeWillChange;
use function assert;
/**
* PDO implementation of the Connection interface.
* Used by all PDO-based drivers.
*
* @deprecated Use {@link Connection} instead
*/
class PDOConnection extends PDO implements ConnectionInterface, ServerInfoAwareConnection
{
use PDOQueryImplementation;
/**
* @internal The connection can be only instantiated by its driver.
*
* @param string $dsn
* @param string|null $user
* @param string|null $password
* @param mixed[]|null $options
*
* @throws PDOException In case of an error.
*/
public function __construct($dsn, $user = null, $password = null, ?array $options = null)
{
try {
parent::__construct($dsn, (string) $user, (string) $password, (array) $options);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [Statement::class, []]);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function exec($sql)
{
try {
$result = parent::exec($sql);
assert($result !== false);
return $result;
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
public function getServerVersion()
{
return PDO::getAttribute(PDO::ATTR_SERVER_VERSION);
}
/**
* @param string $sql
* @param array<int, int> $driverOptions
*
* @return PDOStatement
*/
#[ReturnTypeWillChange]
public function prepare($sql, $driverOptions = [])
{
try {
$statement = parent::prepare($sql, $driverOptions);
assert($statement instanceof PDOStatement);
return $statement;
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function quote($value, $type = ParameterType::STRING)
{
return parent::quote($value, $type);
}
/**
* {@inheritdoc}
*
* @param string|null $name
*
* @return string|int|false
*/
#[ReturnTypeWillChange]
public function lastInsertId($name = null)
{
try {
if ($name === null) {
return parent::lastInsertId();
}
return parent::lastInsertId($name);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4114',
'ServerInfoAwareConnection::requiresQueryForServerVersion() is deprecated and removed in DBAL 3.'
);
return false;
}
/**
* @param mixed ...$args
*/
private function doQuery(...$args): PDOStatement
{
try {
$stmt = parent::query(...$args);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
assert($stmt instanceof PDOStatement);
return $stmt;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\Deprecations\Deprecation;
/**
* @deprecated Use {@link Exception} instead
*
* @psalm-immutable
*/
class PDOException extends \PDOException implements DriverException
{
/**
* The driver specific error code.
*
* @var int|string|null
*/
private $errorCode;
/**
* The SQLSTATE of the driver.
*
* @var string|null
*/
private $sqlState;
/**
* @param \PDOException $exception The PDO exception to wrap.
*/
public function __construct(\PDOException $exception)
{
parent::__construct($exception->getMessage(), 0, $exception);
$this->code = $exception->getCode();
$this->errorInfo = $exception->errorInfo;
$this->errorCode = $exception->errorInfo[1] ?? $exception->getCode();
$this->sqlState = $exception->errorInfo[0] ?? $exception->getCode();
}
/**
* {@inheritdoc}
*/
public function getErrorCode()
{
/** @psalm-suppress ImpureMethodCall */
Deprecation::triggerIfCalledFromOutside(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/4112',
'Driver\AbstractException::getErrorCode() is deprecated, use getSQLState() or getCode() instead.'
);
return $this->errorCode;
}
/**
* {@inheritdoc}
*/
public function getSQLState()
{
return $this->sqlState;
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Doctrine\DBAL\Driver\PDOIbm;
use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\DBAL\Driver\PDO\Connection;
use Doctrine\Deprecations\Deprecation;
/**
* Driver for the PDO IBM extension.
*
* @deprecated Use the driver based on the ibm_db2 extension instead.
*/
class Driver extends AbstractDB2Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
return new Connection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions
);
}
/**
* Constructs the IBM PDO DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
private function _constructPdoDsn(array $params)
{
$dsn = 'ibm:';
if (isset($params['host'])) {
$dsn .= 'HOSTNAME=' . $params['host'] . ';';
}
if (isset($params['port'])) {
$dsn .= 'PORT=' . $params['port'] . ';';
}
$dsn .= 'PROTOCOL=TCPIP;';
if (isset($params['dbname'])) {
$dsn .= 'DATABASE=' . $params['dbname'] . ';';
}
return $dsn;
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'pdo_ibm';
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Doctrine\DBAL\Driver\PDOMySql;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Exception;
use Doctrine\Deprecations\Deprecation;
use PDOException;
/**
* PDO MySql driver.
*
* @deprecated Use {@link PDO\MySQL\Driver} instead.
*/
class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
$conn = new PDO\Connection(
$this->constructPdoDsn($params),
$username,
$password,
$driverOptions
);
} catch (PDOException $e) {
throw Exception::driverException($this, $e);
}
return $conn;
}
/**
* Constructs the MySql PDO DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
protected function constructPdoDsn(array $params)
{
$dsn = 'mysql:';
if (isset($params['host']) && $params['host'] !== '') {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port'])) {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
}
if (isset($params['unix_socket'])) {
$dsn .= 'unix_socket=' . $params['unix_socket'] . ';';
}
if (isset($params['charset'])) {
$dsn .= 'charset=' . $params['charset'] . ';';
}
return $dsn;
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'pdo_mysql';
}
}

View File

@@ -0,0 +1,66 @@
<?php
namespace Doctrine\DBAL\Driver\PDOOracle;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Exception;
use Doctrine\Deprecations\Deprecation;
use PDOException;
/**
* PDO Oracle driver.
*
* @deprecated Use {@link PDO\OCI\Driver} instead.
*/
class Driver extends AbstractOracleDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
return new PDO\Connection(
$this->constructPdoDsn($params),
$username,
$password,
$driverOptions
);
} catch (PDOException $e) {
throw Exception::driverException($this, $e);
}
}
/**
* Constructs the Oracle PDO DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
private function constructPdoDsn(array $params)
{
$dsn = 'oci:dbname=' . $this->getEasyConnectString($params);
if (isset($params['charset'])) {
$dsn .= ';charset=' . $params['charset'];
}
return $dsn;
}
/**
* {@inheritdoc}
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'pdo_oracle';
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace Doctrine\DBAL\Driver\PDOPgSql;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Exception;
use Doctrine\Deprecations\Deprecation;
use PDOException;
use function defined;
/**
* Driver that connects through pdo_pgsql.
*
* @deprecated Use {@link PDO\PgSQL\Driver} instead.
*/
class Driver extends AbstractPostgreSQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
$pdo = new PDO\Connection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions
);
if (
defined('PDO::PGSQL_ATTR_DISABLE_PREPARES')
&& (! isset($driverOptions[\PDO::PGSQL_ATTR_DISABLE_PREPARES])
|| $driverOptions[\PDO::PGSQL_ATTR_DISABLE_PREPARES] === true
)
) {
$pdo->setAttribute(\PDO::PGSQL_ATTR_DISABLE_PREPARES, true);
}
/* defining client_encoding via SET NAMES to avoid inconsistent DSN support
* - the 'client_encoding' connection param only works with postgres >= 9.1
* - passing client_encoding via the 'options' param breaks pgbouncer support
*/
if (isset($params['charset'])) {
$pdo->exec('SET NAMES \'' . $params['charset'] . '\'');
}
return $pdo;
} catch (PDOException $e) {
throw Exception::driverException($this, $e);
}
}
/**
* Constructs the Postgres PDO DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
private function _constructPdoDsn(array $params)
{
$dsn = 'pgsql:';
if (isset($params['host']) && $params['host'] !== '') {
$dsn .= 'host=' . $params['host'] . ';';
}
if (isset($params['port']) && $params['port'] !== '') {
$dsn .= 'port=' . $params['port'] . ';';
}
if (isset($params['dbname'])) {
$dsn .= 'dbname=' . $params['dbname'] . ';';
} elseif (isset($params['default_dbname'])) {
$dsn .= 'dbname=' . $params['default_dbname'] . ';';
} else {
// Used for temporary connections to allow operations like dropping the database currently connected to.
// Connecting without an explicit database does not work, therefore "postgres" database is used
// as it is mostly present in every server setup.
$dsn .= 'dbname=postgres;';
}
if (isset($params['sslmode'])) {
$dsn .= 'sslmode=' . $params['sslmode'] . ';';
}
if (isset($params['sslrootcert'])) {
$dsn .= 'sslrootcert=' . $params['sslrootcert'] . ';';
}
if (isset($params['sslcert'])) {
$dsn .= 'sslcert=' . $params['sslcert'] . ';';
}
if (isset($params['sslkey'])) {
$dsn .= 'sslkey=' . $params['sslkey'] . ';';
}
if (isset($params['sslcrl'])) {
$dsn .= 'sslcrl=' . $params['sslcrl'] . ';';
}
if (isset($params['application_name'])) {
$dsn .= 'application_name=' . $params['application_name'] . ';';
}
return $dsn;
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'pdo_pgsql';
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\DBAL\Driver;
use PDOStatement;
use ReturnTypeWillChange;
use function func_get_args;
use const PHP_VERSION_ID;
if (PHP_VERSION_ID >= 80000) {
/**
* @internal
*/
trait PDOQueryImplementation
{
/**
* @return PDOStatement
*/
#[ReturnTypeWillChange]
public function query(?string $query = null, ?int $fetchMode = null, mixed ...$fetchModeArgs)
{
return $this->doQuery($query, $fetchMode, ...$fetchModeArgs);
}
}
} else {
/**
* @internal
*/
trait PDOQueryImplementation
{
/**
* @return PDOStatement
*/
public function query()
{
return $this->doQuery(...func_get_args());
}
}
}

View File

@@ -0,0 +1,93 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlite;
use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\Deprecations\Deprecation;
use PDOException;
use function array_merge;
/**
* The PDO Sqlite driver.
*
* @deprecated Use {@link PDO\SQLite\Driver} instead.
*/
class Driver extends AbstractSQLiteDriver
{
/** @var mixed[] */
protected $_userDefinedFunctions = [
'sqrt' => ['callback' => [SqlitePlatform::class, 'udfSqrt'], 'numArgs' => 1],
'mod' => ['callback' => [SqlitePlatform::class, 'udfMod'], 'numArgs' => 2],
'locate' => ['callback' => [SqlitePlatform::class, 'udfLocate'], 'numArgs' => -1],
];
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
if (isset($driverOptions['userDefinedFunctions'])) {
$this->_userDefinedFunctions = array_merge(
$this->_userDefinedFunctions,
$driverOptions['userDefinedFunctions']
);
unset($driverOptions['userDefinedFunctions']);
}
try {
$pdo = new PDO\Connection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions
);
} catch (PDOException $ex) {
throw Exception::driverException($this, $ex);
}
foreach ($this->_userDefinedFunctions as $fn => $data) {
$pdo->sqliteCreateFunction($fn, $data['callback'], $data['numArgs']);
}
return $pdo;
}
/**
* Constructs the Sqlite PDO DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
protected function _constructPdoDsn(array $params)
{
$dsn = 'sqlite:';
if (isset($params['path'])) {
$dsn .= $params['path'];
} elseif (isset($params['memory'])) {
$dsn .= ':memory:';
}
return $dsn;
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'pdo_sqlite';
}
}

View File

@@ -0,0 +1,49 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\Driver\Result;
/**
* Sqlsrv Connection implementation.
*
* @deprecated Use {@link PDO\SQLSrv\Connection} instead.
*/
class Connection extends PDO\Connection
{
/**
* {@inheritdoc}
*
* @internal The connection can be only instantiated by its driver.
*
* @param string $dsn
* @param string|null $user
* @param string|null $password
* @param mixed[]|null $options
*/
public function __construct($dsn, $user = null, $password = null, ?array $options = null)
{
parent::__construct($dsn, $user, $password, $options);
$this->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [PDO\SQLSrv\Statement::class, []]);
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name === null) {
return parent::lastInsertId($name);
}
$stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
$stmt->execute([$name]);
if ($stmt instanceof Result) {
return $stmt->fetchOne();
}
return $stmt->fetchColumn();
}
}

View File

@@ -0,0 +1,107 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver\Exception\PortWithoutHost;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\Deprecations\Deprecation;
use function is_int;
use function sprintf;
/**
* The PDO-based Sqlsrv driver.
*
* @deprecated Use {@link PDO\SQLSrv\Driver} instead.
*/
class Driver extends AbstractSQLServerDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
$pdoOptions = $dsnOptions = [];
foreach ($driverOptions as $option => $value) {
if (is_int($option)) {
$pdoOptions[$option] = $value;
} else {
$dsnOptions[$option] = $value;
}
}
return new PDO\SQLSrv\Connection(
$this->_constructPdoDsn($params, $dsnOptions),
$username,
$password,
$pdoOptions
);
}
/**
* Constructs the Sqlsrv PDO DSN.
*
* @param mixed[] $params
* @param string[] $connectionOptions
*
* @return string The DSN.
*/
private function _constructPdoDsn(array $params, array $connectionOptions)
{
$dsn = 'sqlsrv:server=';
if (isset($params['host'])) {
$dsn .= $params['host'];
if (isset($params['port'])) {
$dsn .= ',' . $params['port'];
}
} elseif (isset($params['port'])) {
throw PortWithoutHost::new();
}
if (isset($params['dbname'])) {
$connectionOptions['Database'] = $params['dbname'];
}
if (isset($params['MultipleActiveResultSets'])) {
$connectionOptions['MultipleActiveResultSets'] = $params['MultipleActiveResultSets'] ? 'true' : 'false';
}
return $dsn . $this->getConnectionOptionsDsn($connectionOptions);
}
/**
* Converts a connection options array to the DSN
*
* @param string[] $connectionOptions
*/
private function getConnectionOptionsDsn(array $connectionOptions): string
{
$connectionOptionsDsn = '';
foreach ($connectionOptions as $paramName => $paramValue) {
$connectionOptionsDsn .= sprintf(';%s=%s', $paramName, $paramValue);
}
return $connectionOptionsDsn;
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'pdo_sqlsrv';
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\PDO;
use Doctrine\DBAL\ParameterType;
/**
* PDO SQL Server Statement
*
* @deprecated Use {@link PDO\SQLSrv\Statement} instead.
*/
class Statement extends PDO\Statement
{
/**
* {@inheritdoc}
*/
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
switch ($type) {
case ParameterType::LARGE_OBJECT:
case ParameterType::BINARY:
if ($driverOptions === null) {
$driverOptions = \PDO::SQLSRV_ENCODING_BINARY;
}
break;
case ParameterType::ASCII:
$type = ParameterType::STRING;
$length = 0;
$driverOptions = \PDO::SQLSRV_ENCODING_SYSTEM;
break;
}
return parent::bindParam($param, $variable, $type, $length ?? 0, $driverOptions);
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type);
}
}

View File

@@ -0,0 +1,316 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\PDO\Exception;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use Doctrine\Deprecations\Deprecation;
use PDO;
use PDOException;
use ReturnTypeWillChange;
use function array_slice;
use function assert;
use function func_get_args;
use function is_array;
/**
* The PDO implementation of the Statement interface.
* Used by all PDO-based drivers.
*
* @deprecated Use {@link Statement} instead
*/
class PDOStatement extends \PDOStatement implements StatementInterface, Result
{
use PDOStatementImplementations;
private const PARAM_TYPE_MAP = [
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
ParameterType::STRING => PDO::PARAM_STR,
ParameterType::ASCII => PDO::PARAM_STR,
ParameterType::BINARY => PDO::PARAM_LOB,
ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
ParameterType::BOOLEAN => PDO::PARAM_BOOL,
];
private const FETCH_MODE_MAP = [
FetchMode::ASSOCIATIVE => PDO::FETCH_ASSOC,
FetchMode::NUMERIC => PDO::FETCH_NUM,
FetchMode::MIXED => PDO::FETCH_BOTH,
FetchMode::STANDARD_OBJECT => PDO::FETCH_OBJ,
FetchMode::COLUMN => PDO::FETCH_COLUMN,
FetchMode::CUSTOM_OBJECT => PDO::FETCH_CLASS,
];
/**
* Protected constructor.
*
* @internal The statement can be only instantiated by its driver connection.
*/
protected function __construct()
{
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function bindValue($param, $value, $type = ParameterType::STRING)
{
$type = $this->convertParamType($type);
try {
return parent::bindValue($param, $value, $type);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* @param mixed $param
* @param mixed $variable
* @param int $type
* @param int|null $length
* @param mixed $driverOptions
*
* @return bool
*/
#[ReturnTypeWillChange]
public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
$type = $this->convertParamType($type);
try {
return parent::bindParam($param, $variable, $type, ...array_slice(func_get_args(), 3));
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*
* @deprecated Use free() instead.
*/
#[ReturnTypeWillChange]
public function closeCursor()
{
try {
return parent::closeCursor();
} catch (PDOException $exception) {
// Exceptions not allowed by the interface.
// In case driver implementations do not adhere to the interface, silence exceptions here.
return true;
}
}
/**
* {@inheritdoc}
*/
#[ReturnTypeWillChange]
public function execute($params = null)
{
try {
return parent::execute($params);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*/
#[ReturnTypeWillChange]
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
$args = func_get_args();
if (isset($args[0])) {
$args[0] = $this->convertFetchMode($args[0]);
}
try {
return parent::fetch(...$args);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*
* @deprecated Use fetchOne() instead.
*/
#[ReturnTypeWillChange]
public function fetchColumn($columnIndex = 0)
{
try {
return parent::fetchColumn($columnIndex);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* {@inheritdoc}
*/
public function fetchNumeric()
{
return $this->fetch(PDO::FETCH_NUM);
}
/**
* {@inheritdoc}
*/
public function fetchAssociative()
{
return $this->fetch(PDO::FETCH_ASSOC);
}
/**
* {@inheritdoc}
*/
public function fetchOne()
{
return $this->fetch(PDO::FETCH_COLUMN);
}
/**
* {@inheritdoc}
*/
public function fetchAllNumeric(): array
{
return $this->fetchAll(PDO::FETCH_NUM);
}
/**
* {@inheritdoc}
*/
public function fetchAllAssociative(): array
{
return $this->fetchAll(PDO::FETCH_ASSOC);
}
/**
* {@inheritdoc}
*/
public function fetchFirstColumn(): array
{
return $this->fetchAll(PDO::FETCH_COLUMN);
}
public function free(): void
{
parent::closeCursor();
}
/**
* @param mixed ...$args
*/
private function doSetFetchMode(int $fetchMode, ...$args): bool
{
$fetchMode = $this->convertFetchMode($fetchMode);
// This thin wrapper is necessary to shield against the weird signature
// of PDOStatement::setFetchMode(): even if the second and third
// parameters are optional, PHP will not let us remove it from this
// declaration.
$slice = [];
foreach ($args as $arg) {
if ($arg === null) {
break;
}
$slice[] = $arg;
}
try {
return parent::setFetchMode($fetchMode, ...$slice);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
}
/**
* @param mixed ...$args
*
* @return mixed[]
*/
private function doFetchAll(...$args): array
{
if (isset($args[0])) {
$args[0] = $this->convertFetchMode($args[0]);
}
$slice = [];
foreach ($args as $arg) {
if ($arg === null) {
break;
}
$slice[] = $arg;
}
try {
$data = parent::fetchAll(...$slice);
} catch (PDOException $exception) {
throw Exception::new($exception);
}
assert(is_array($data));
return $data;
}
/**
* Converts DBAL parameter type to PDO parameter type
*
* @param int $type Parameter type
*/
private function convertParamType(int $type): int
{
if (! isset(self::PARAM_TYPE_MAP[$type])) {
// TODO: next major: throw an exception
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3088',
'Using a PDO parameter type (%d given) is deprecated, ' .
'use \Doctrine\DBAL\Types\Types constants instead.',
$type
);
return $type;
}
return self::PARAM_TYPE_MAP[$type];
}
/**
* Converts DBAL fetch mode to PDO fetch mode
*
* @param int $fetchMode Fetch mode
*/
private function convertFetchMode(int $fetchMode): int
{
if (! isset(self::FETCH_MODE_MAP[$fetchMode])) {
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/pull/3088',
'Using an unsupported PDO fetch mode or a bitmask of fetch modes (%d given)' .
' is deprecated and will cause an error in Doctrine DBAL 3.0',
$fetchMode
);
return $fetchMode;
}
return self::FETCH_MODE_MAP[$fetchMode];
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Doctrine\DBAL\Driver;
use ReturnTypeWillChange;
use function func_get_args;
use const PHP_VERSION_ID;
if (PHP_VERSION_ID >= 80000) {
/**
* @internal
*/
trait PDOStatementImplementations
{
/**
* @deprecated Use one of the fetch- or iterate-related methods.
*
* @param int $mode
* @param mixed ...$args
*
* @return bool
*/
#[ReturnTypeWillChange]
public function setFetchMode($mode, ...$args)
{
return $this->doSetFetchMode($mode, ...$args);
}
/**
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*
* @param int|null $mode
* @param mixed ...$args
*
* @return mixed[]
*/
#[ReturnTypeWillChange]
public function fetchAll($mode = null, ...$args)
{
return $this->doFetchAll($mode, ...$args);
}
}
} else {
/**
* @internal
*/
trait PDOStatementImplementations
{
/**
* @deprecated Use one of the fetch- or iterate-related methods.
*
* @param int $fetchMode
* @param mixed $arg2
* @param mixed $arg3
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null): bool
{
return $this->doSetFetchMode(...func_get_args());
}
/**
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*
* @param int|null $fetchMode
* @param mixed $fetchArgument
* @param mixed $ctorArgs
*
* @return mixed[]
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
return $this->doFetchAll(...func_get_args());
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* An interface for connections which support a "native" ping method.
*
* @deprecated
*/
interface PingableConnection
{
/**
* Pings the database server to determine if the connection is still
* available. Return true/false based on if that was successful or not.
*
* @return bool
*/
public function ping();
}

View File

@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace Doctrine\DBAL\Driver;
/**
* Driver-level result statement execution result.
*/
interface Result
{
/**
* Returns the next row of the result as a numeric array or FALSE if there are no more rows.
*
* @return array<int,mixed>|false
*
* @throws Exception
*/
public function fetchNumeric();
/**
* Returns the next row of the result as an associative array or FALSE if there are no more rows.
*
* @return array<string,mixed>|false
*
* @throws Exception
*/
public function fetchAssociative();
/**
* Returns the first value of the next row of the result or FALSE if there are no more rows.
*
* @return mixed|false
*
* @throws Exception
*/
public function fetchOne();
/**
* Returns an array containing all of the result rows represented as numeric arrays.
*
* @return array<int,array<int,mixed>>
*
* @throws Exception
*/
public function fetchAllNumeric(): array;
/**
* Returns an array containing all of the result rows represented as associative arrays.
*
* @return array<int,array<string,mixed>>
*
* @throws Exception
*/
public function fetchAllAssociative(): array;
/**
* Returns an array containing the values of the first column of the result.
*
* @return array<int,mixed>
*
* @throws Exception
*/
public function fetchFirstColumn(): array;
/**
* Returns the number of rows affected by the DELETE, INSERT, or UPDATE statement that produced the result.
*
* If the statement executed a SELECT query or a similar platform-specific SQL (e.g. DESCRIBE, SHOW, etc.),
* some database drivers may return the number of rows returned by that query. However, this behaviour
* is not guaranteed for all drivers and should not be relied on in portable applications.
*
* @return int|string The number of rows.
*/
public function rowCount();
/**
* Returns the number of columns in the result
*
* @return int The number of columns in the result. If the columns cannot be counted,
* this method must return 0.
*/
public function columnCount();
/**
* Discards the non-fetched portion of the result, enabling the originating statement to be executed again.
*/
public function free(): void;
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Doctrine\DBAL\Driver;
use PDO;
use Traversable;
/**
* Interface for the reading part of a prepare statement only.
*/
interface ResultStatement extends Traversable
{
/**
* Closes the cursor, enabling the statement to be executed again.
*
* @deprecated Use Result::free() instead.
*
* @return bool TRUE on success or FALSE on failure.
*/
public function closeCursor();
/**
* Returns the number of columns in the result set
*
* @return int The number of columns in the result set represented
* by the PDOStatement object. If there is no result set,
* this method should return 0.
*/
public function columnCount();
/**
* Sets the fetch mode to use while iterating this statement.
*
* @deprecated Use one of the fetch- or iterate-related methods.
*
* @param int $fetchMode The fetch mode must be one of the {@link FetchMode} constants.
* @param mixed $arg2
* @param mixed $arg3
*
* @return bool
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null);
/**
* Returns the next row of a result set.
*
* @deprecated Use fetchNumeric(), fetchAssociative() or fetchOne() instead.
*
* @param int|null $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link FetchMode} constants,
* defaulting to {@link FetchMode::MIXED}.
* @param int $cursorOrientation For a ResultStatement object representing a scrollable cursor,
* this value determines which row will be returned to the caller.
* This value must be one of the \PDO::FETCH_ORI_* constants,
* defaulting to \PDO::FETCH_ORI_NEXT. To request a scrollable
* cursor for your ResultStatement object, you must set the \PDO::ATTR_CURSOR
* attribute to \PDO::CURSOR_SCROLL when you prepare the SQL statement with
* \PDO::prepare().
* @param int $cursorOffset For a ResultStatement object representing a scrollable cursor for which the
* cursorOrientation parameter is set to \PDO::FETCH_ORI_ABS, this value
* specifies the absolute number of the row in the result set that shall be
* fetched.
* For a ResultStatement object representing a scrollable cursor for which the
* cursorOrientation parameter is set to \PDO::FETCH_ORI_REL, this value
* specifies the row to fetch relative to the cursor position before
* ResultStatement::fetch() was called.
*
* @return mixed The return value of this method on success depends on the fetch mode. In all cases, FALSE is
* returned on failure.
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0);
/**
* Returns an array containing all of the result set rows.
*
* @deprecated Use fetchAllNumeric(), fetchAllAssociative() or fetchFirstColumn() instead.
*
* @param int|null $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link FetchMode} constants,
* defaulting to {@link FetchMode::MIXED}.
* @param int|string|null $fetchArgument This argument has a different meaning depending on the value
* of the $fetchMode parameter:
* * {@link FetchMode::COLUMN}:
* Returns the indicated 0-indexed column.
* * {@link FetchMode::CUSTOM_OBJECT}:
* Returns instances of the specified class, mapping the columns of each row
* to named properties in the class.
* * {@link PDO::FETCH_FUNC}: Returns the results of calling
* the specified function, using each row's
* columns as parameters in the call.
* @param mixed[]|null $ctorArgs Controls how the next row will be returned to the caller.
* The value must be one of the {@link FetchMode} constants,
* defaulting to {@link FetchMode::MIXED}.
*
* @return mixed[]
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null);
/**
* Returns a single column from the next row of a result set or FALSE if there are no more rows.
*
* @deprecated Use fetchOne() instead.
*
* @param int $columnIndex 0-indexed number of the column you wish to retrieve from the row.
* If no value is supplied, fetches the first column.
*
* @return mixed|false A single column in the next row of a result set, or FALSE if there are no more rows.
*/
public function fetchColumn($columnIndex = 0);
}

View File

@@ -0,0 +1,104 @@
<?php
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver;
use Doctrine\DBAL\Exception;
use Doctrine\Deprecations\Deprecation;
use function array_keys;
use function array_map;
use function implode;
/**
* A Doctrine DBAL driver for the SAP Sybase SQL Anywhere PHP extension.
*
* @deprecated Support for SQLAnywhere will be removed in 3.0.
*/
class Driver extends AbstractSQLAnywhereDriver
{
/**
* {@inheritdoc}
*
* @throws Exception If there was a problem establishing the connection.
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
return new SQLAnywhereConnection(
$this->buildDsn(
$params['host'] ?? null,
$params['port'] ?? null,
$params['server'] ?? null,
$params['dbname'] ?? null,
$username,
$password,
$driverOptions
),
$params['persistent'] ?? false
);
} catch (SQLAnywhereException $e) {
throw Exception::driverException($this, $e);
}
}
/**
* {@inheritdoc}
*
* @deprecated
*/
public function getName()
{
Deprecation::trigger(
'doctrine/dbal',
'https://github.com/doctrine/dbal/issues/3580',
'Driver::getName() is deprecated'
);
return 'sqlanywhere';
}
/**
* Build the connection string for given connection parameters and driver options.
*
* @param string|null $host Host address to connect to.
* @param int|null $port Port to use for the connection (default to SQL Anywhere standard port 2638).
* @param string|null $server Database server name on the host to connect to.
* SQL Anywhere allows multiple database server instances on the same host,
* therefore specifying the server instance name to use is mandatory.
* @param string|null $dbname Name of the database on the server instance to connect to.
* @param string $username User name to use for connection authentication.
* @param string $password Password to use for connection authentication.
* @param mixed[] $driverOptions Additional parameters to use for the connection.
*
* @return string
*/
private function buildDsn(
$host,
$port,
$server,
$dbname,
$username = null,
$password = null,
array $driverOptions = []
) {
$host = $host ?: 'localhost';
$port = $port ?: 2638;
if (! empty($server)) {
$server = ';ServerName=' . $server;
}
return 'HOST=' . $host . ':' . $port .
$server .
';DBN=' . $dbname .
';UID=' . $username .
';PWD=' . $password .
';' . implode(
';',
array_map(static function ($key, $value) {
return $key . '=' . $value;
}, array_keys($driverOptions), $driverOptions)
);
}
}

Some files were not shown because too many files have changed in this diff Show More