1
0

提交代码

This commit is contained in:
2020-08-06 14:50:07 +08:00
parent 9d0d5f4be9
commit d7a848c824
11299 changed files with 1321854 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
{
"active": true,
"name": "Database Abstraction Layer",
"shortName": "DBAL",
"slug": "dbal",
"docsSlug": "doctrine-dbal",
"versions": [
{
"name": "3.0",
"branchName": "develop",
"slug": "latest",
"upcoming": true
},
{
"name": "2.9",
"branchName": "master",
"slug": "2.9",
"upcoming": true
},
{
"name": "2.8",
"branchName": "2.8",
"slug": "2.8",
"current": true,
"aliases": [
"current",
"stable"
]
},
{
"name": "2.7",
"branchName": "2.7",
"slug": "2.7",
"maintained": false
},
{
"name": "2.6",
"branchName": "2.6",
"slug": "2.6",
"maintained": false
},
{
"name": "2.5",
"branchName": "2.5",
"slug": "2.5",
"maintained": false
}
]
}

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.

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

@@ -0,0 +1,45 @@
# Doctrine DBAL
| [Master][Master] | [2.8][2.8] | [Develop][develop] |
|:----------------:|:----------:|:------------------:|
| [![Build status][Master image]][Master] | [![Build status][2.8 image]][2.8] | [![Build status][develop image]][develop] |
| [![Build Status][ContinuousPHP image]][ContinuousPHP] | [![Build Status][ContinuousPHP 2.8 image]][ContinuousPHP] | [![Build Status][ContinuousPHP develop image]][ContinuousPHP] |
| [![Code Coverage][Coverage image]][Scrutinizer Master] | [![Code Coverage][Coverage 2.8 image]][Scrutinizer 2.8] | [![Code Coverage][Coverage develop image]][Scrutinizer develop] |
| [![Code Quality][Quality image]][Scrutinizer Master] | [![Code Quality][Quality 2.8 image]][Scrutinizer 2.8] | [![Code Quality][Quality develop image]][Scrutinizer develop] |
| [![AppVeyor][AppVeyor master image]][AppVeyor master] | [![AppVeyor][AppVeyor 2.8 image]][AppVeyor 2.8] | [![AppVeyor][AppVeyor develop image]][AppVeyor develop] |
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)
[Master image]: https://img.shields.io/travis/doctrine/dbal/master.svg?style=flat-square
[Coverage image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/dbal/master.svg?style=flat-square
[Quality image]: https://img.shields.io/scrutinizer/g/doctrine/dbal/master.svg?style=flat-square
[ContinuousPHP image]: https://img.shields.io/continuousphp/git-hub/doctrine/dbal/master.svg?style=flat-square
[Master]: https://travis-ci.org/doctrine/dbal
[Scrutinizer Master]: https://scrutinizer-ci.com/g/doctrine/dbal/
[AppVeyor master]: https://ci.appveyor.com/project/doctrine/dbal/branch/master
[AppVeyor master image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/master?svg=true
[ContinuousPHP]: https://continuousphp.com/git-hub/doctrine/dbal
[2.8 image]: https://img.shields.io/travis/doctrine/dbal/2.8.svg?style=flat-square
[Coverage 2.8 image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/dbal/2.8.svg?style=flat-square
[Quality 2.8 image]: https://img.shields.io/scrutinizer/g/doctrine/dbal/2.8.svg?style=flat-square
[ContinuousPHP 2.8 image]: https://img.shields.io/continuousphp/git-hub/doctrine/dbal/2.8.svg?style=flat-square
[2.8]: https://github.com/doctrine/dbal/tree/2.8
[Scrutinizer 2.8]: https://scrutinizer-ci.com/g/doctrine/dbal/?branch=2.8
[AppVeyor 2.8]: https://ci.appveyor.com/project/doctrine/dbal/branch/2.8
[AppVeyor 2.8 image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/2.8?svg=true
[develop]: https://github.com/doctrine/dbal/tree/develop
[develop image]: https://img.shields.io/travis/doctrine/dbal/develop.svg?style=flat-square
[Coverage develop image]: https://img.shields.io/scrutinizer/coverage/g/doctrine/dbal/develop.svg?style=flat-square
[Quality develop image]: https://img.shields.io/scrutinizer/g/doctrine/dbal/develop.svg?style=flat-square
[ContinuousPHP develop image]: https://img.shields.io/continuousphp/git-hub/doctrine/dbal/develop.svg?style=flat-square
[develop]: https://github.com/doctrine/dbal/tree/develop
[Scrutinizer develop]: https://scrutinizer-ci.com/g/doctrine/dbal/?branch=develop
[AppVeyor develop]: https://ci.appveyor.com/project/doctrine/dbal/branch/develop
[AppVeyor develop image]: https://ci.appveyor.com/api/projects/status/i88kitq8qpbm0vie/branch/develop?svg=true

14
vendor/doctrine/dbal/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,14 @@
Security
========
The Doctrine library is operating very close to your database and as such needs
to handle and make assumptions about SQL injection vulnerabilities.
It is vital that you understand how Doctrine approaches security, because
we cannot protect you from SQL injection.
Please read the documentation chapter on Security in Doctrine DBAL to
understand the assumptions we make.
- [Latest security.rst page on Github](https://github.com/doctrine/dbal/blob/master/docs/en/reference/security.rst)
- [Security Page in rendered documentation](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/security.html)

359
vendor/doctrine/dbal/UPGRADE.md vendored Normal file
View File

@@ -0,0 +1,359 @@
# Upgrade to 2.9
## Deprecated `Statement::fetchColumn()` with an invalid index
Calls to `Statement::fetchColumn()` with an invalid column index currently return `NULL`. In the future, such calls will result in a exception.
## Deprecated `Configuration::getFilterSchemaAssetsExpression()`, `::setFilterSchemaAssetsExpression()` and `AbstractSchemaManager::getFilterSchemaAssetsExpression()`.
Regular expression-based filters are hard to extend by combining together. Instead, you may use callback-based filers via `::getSchemaAssetsFilter()` and `::getSchemaAssetsFilter()`. Callbacks can use regular expressions internally.
## Deprecated `Doctrine\DBAL\Types\Type::getDefaultLength()`
This method was never used by DBAL internally. It is now deprecated and will be removed in DBAL 3.0.
## Deprecated `Doctrine\DBAL\Types\Type::__toString()`
Relying on string representation is discouraged and will be removed in DBAL 3.0.
## Deprecated `NULL` value of `$offset` in LIMIT queries
The `NULL` value of the `$offset` argument in `AbstractPlatform::(do)?ModifyLimitQuery()` methods is deprecated. If explicitly used in the method call, the absence of the offset should be indicated with a `0`.
## Deprecated dbal:import CLI command
The `dbal:import` CLI command has been deprecated since it only works with PDO-based drivers by relying on a non-documented behavior of the extension, and it's impossible to make it work with other drivers.
Please use other database client applications for import, e.g.:
* For MySQL and MariaDB: `mysql [dbname] < data.sql`.
* For PostgreSQL: `psql [dbname] < data.sql`.
* For SQLite: `sqlite3 /path/to/file.db < data.sql`.
# Upgrade to 2.8
## Deprecated usage of DB-generated UUIDs
The format of DB-generated UUIDs is inconsistent across supported platforms and therefore is not portable. Some of the platforms produce UUIDv1, some produce UUIDv4, some produce the values which are not even UUID.
Unless UUIDs are used in stored procedures which DBAL doesn't support, there's no real benefit of DB-generated UUIDs comparing to the application-generated ones.
Use a PHP library (e.g. [ramsey/uuid](https://packagist.org/packages/ramsey/uuid)) to generate UUIDs on the application side.
## Deprecated usage of binary fields whose length exceeds the platform maximum
- The usage of binary fields whose length exceeds the maximum field size on a given platform is deprecated.
Use binary fields of a size which fits all target platforms, or use blob explicitly instead.
## Removed dependency on doctrine/common
The dependency on doctrine/common package has been removed.
DBAL now depends on doctrine/cache and doctrine/event-manager instead.
If you are using any other component from doctrine/common package,
you will have to add an explicit dependency to your composer.json.
## Corrected exception thrown by ``Doctrine\DBAL\Platforms\SQLAnywhere16Platform::getAdvancedIndexOptionsSQL()``
This method now throws SPL ``UnexpectedValueException`` instead of accidentally throwing ``Doctrine\Common\Proxy\Exception\UnexpectedValueException``.
# Upgrade to 2.7
## Doctrine\DBAL\Platforms\AbstractPlatform::DATE_INTERVAL_UNIT_* constants deprecated
``Doctrine\DBAL\Platforms\AbstractPlatform::DATE_INTERVAL_UNIT_*`` constants were moved into ``Doctrine\DBAL\Platforms\DateIntervalUnit`` class without the ``DATE_INTERVAL_UNIT_`` prefix.
## Doctrine\DBAL\Platforms\AbstractPlatform::TRIM_* constants deprecated
``Doctrine\DBAL\Platforms\AbstractPlatform::TRIM_*`` constants were moved into ``Doctrine\DBAL\Platforms\TrimMode`` class without the ``TRIM_`` prefix.
## Doctrine\DBAL\Connection::TRANSACTION_* constants deprecated
``Doctrine\DBAL\Connection::TRANSACTION_*`` were moved into ``Doctrine\DBAL\TransactionIsolationLevel`` class without the ``TRANSACTION_`` prefix.
## DEPRECATION: direct usage of the PDO APIs in the DBAL API
1. When calling `Doctrine\DBAL\Driver\Statement` methods, instead of `PDO::PARAM_*` constants, `Doctrine\DBAL\ParameterType` constants should be used.
2. When calling `Doctrine\DBAL\Driver\ResultStatement` methods, instead of `PDO::FETCH_*` constants, `Doctrine\DBAL\FetchMode` constants should be used.
3. When configuring `Doctrine\DBAL\Portability\Connection`, instead of `PDO::CASE_*` constants, `Doctrine\DBAL\ColumnCase` constants should be used.
4. Usage of `PDO::PARAM_INPUT_OUTPUT` in `Doctrine\DBAL\Driver\Statement::bindValue()` is deprecated.
5. Usage of `PDO::FETCH_FUNC` in `Doctrine\DBAL\Driver\ResultStatement::fetch()` is deprecated.
6. Calls to `\PDOStatement` methods on a `\Doctrine\DBAL\Driver\PDOStatement` instance (e.g. `fetchObject()`) are deprecated.
# Upgrade to 2.6
## MINOR BC BREAK: `fetch()` and `fetchAll()` method signatures in `Doctrine\DBAL\Driver\ResultStatement`
1. ``Doctrine\DBAL\Driver\ResultStatement::fetch()`` now has 3 arguments instead of 1, respecting
``PDO::fetch()`` signature.
Before:
Doctrine\DBAL\Driver\ResultStatement::fetch($fetchMode);
After:
Doctrine\DBAL\Driver\ResultStatement::fetch($fetchMode, $cursorOrientation, $cursorOffset);
2. ``Doctrine\DBAL\Driver\ResultStatement::fetchAll()`` now has 3 arguments instead of 1, respecting
``PDO::fetchAll()`` signature.
Before:
Doctrine\DBAL\Driver\ResultStatement::fetchAll($fetchMode);
After:
Doctrine\DBAL\Driver\ResultStatement::fetch($fetchMode, $fetchArgument, $ctorArgs);
## MINOR BC BREAK: URL-style DSN with percentage sign in password
URL-style DSNs (e.g. ``mysql://foo@bar:localhost/db``) are now assumed to be percent-encoded
in order to allow certain special characters in usernames, paswords and database names. If
you are using a URL-style DSN and have a username, password or database name containing a
percentage sign, you need to update your DSN. If your password is, say, ``foo%foo``, it
should be encoded as ``foo%25foo``.
# Upgrade to 2.5.1
## MINOR BC BREAK: Doctrine\DBAL\Schema\Table
When adding indexes to ``Doctrine\DBAL\Schema\Table`` via ``addIndex()`` or ``addUniqueIndex()``,
duplicate indexes are not silently ignored/dropped anymore (based on semantics, not naming!).
Duplicate indexes are considered indexes that pass ``isFullfilledBy()`` or ``overrules()``
in ``Doctrine\DBAL\Schema\Index``.
This is required to make the index renaming feature introduced in 2.5.0 work properly and avoid
issues in the ORM schema tool / DBAL schema manager which pretends users from updating
their schemas and migrate to DBAL 2.5.*.
Additionally it offers more flexibility in declaring indexes for the user and potentially fixes
related issues in the ORM.
With this change, the responsibility to decide which index is a "duplicate" is completely deferred
to the user.
Please also note that adding foreign key constraints to a table via ``addForeignKeyConstraint()``,
``addUnnamedForeignKeyConstraint()`` or ``addNamedForeignKeyConstraint()`` now first checks if an
appropriate index is already present and avoids adding an additional auto-generated one eventually.
# Upgrade to 2.5
## BC BREAK: time type resets date fields to UNIX epoch
When mapping `time` type field to PHP's `DateTime` instance all unused date fields are
reset to UNIX epoch (i.e. 1970-01-01). This might break any logic which relies on comparing
`DateTime` instances with date fields set to the current date.
Use `!` format prefix (see http://php.net/manual/en/datetime.createfromformat.php) for parsing
time strings to prevent having different date fields when comparing user input and `DateTime`
instances as mapped by Doctrine.
## BC BREAK: Doctrine\DBAL\Schema\Table
The methods ``addIndex()`` and ``addUniqueIndex()`` in ``Doctrine\DBAL\Schema\Table``
have an additional, optional parameter. If you override these methods, you should
add this new parameter to the declaration of your overridden methods.
## BC BREAK: Doctrine\DBAL\Connection
The visibility of the property ``$_platform`` in ``Doctrine\DBAL\Connection``
was changed from protected to private. If you have subclassed ``Doctrine\DBAL\Connection``
in your application and accessed ``$_platform`` directly, you have to change the code
portions to use ``getDatabasePlatform()`` instead to retrieve the underlying database
platform.
The reason for this change is the new automatic platform version detection feature,
which lazily evaluates the appropriate platform class to use for the underlying database
server version at runtime.
Please also note, that calling ``getDatabasePlatform()`` now needs to establish a connection
in order to evaluate the appropriate platform class if ``Doctrine\DBAL\Connection`` is not
already connected. Under the following circumstances, it is not possible anymore to retrieve
the platform instance from the connection object without having to do a real connect:
1. ``Doctrine\DBAL\Connection`` was instantiated without the ``platform`` connection parameter.
2. ``Doctrine\DBAL\Connection`` was instantiated without the ``serverVersion`` connection parameter.
3. The underlying driver is "version aware" and can provide different platform instances
for different versions.
4. The underlying driver connection is "version aware" and can provide the database server
version without having to query for it.
If one of the above conditions is NOT met, there is no need for ``Doctrine\DBAL\Connection``
to do a connect when calling ``getDatabasePlatform()``.
## datetime Type uses date_create() as fallback
Before 2.5 the DateTime type always required a specific format, defined in
`$platform->getDateTimeFormatString()`, which could cause quite some troubles
on platforms that had various microtime precision formats. Starting with 2.5
whenever the parsing of a date fails with the predefined platform format,
the `date_create()` function will be used to parse the date.
This could cause some troubles when your date format is weird and not parsed
correctly by `date_create`, however since databases are rather strict on dates
there should be no problem.
## Support for pdo_ibm driver removed
The ``pdo_ibm`` driver is buggy and does not work well with Doctrine. Therefore it will no
longer be supported and has been removed from the ``Doctrine\DBAL\DriverManager`` drivers
map. It is highly encouraged to to use `ibm_db2` driver instead if you want to connect
to an IBM DB2 database as it is much more stable and secure.
If for some reason you have to utilize the ``pdo_ibm`` driver you can still use the `driverClass`
connection parameter to explicitly specify the ``Doctrine\DBAL\Driver\PDOIbm\Driver`` class.
However be aware that you are doing this at your own risk and it will not be guaranteed that
Doctrine will work as expected.
# Upgrade to 2.4
## Doctrine\DBAL\Schema\Constraint
If you have custom classes that implement the constraint interface, you have to implement
an additional method ``getQuotedColumns`` now. This method is used to build proper constraint
SQL for columns that need to be quoted, like keywords reserved by the specific platform used.
The method has to return the same values as ``getColumns`` only that those column names that
need quotation have to be returned quoted for the given platform.
# Upgrade to 2.3
## Oracle Session Init now sets Numeric Character
Before 2.3 the Oracle Session Init did not care about the numeric character of the Session.
This could lead to problems on non english locale systems that required a comma as a floating
point seperator in Oracle. Since 2.3, using the Oracle Session Init on connection start the
client session will be altered to set the numeric character to ".,":
ALTER SESSION SET NLS_NUMERIC_CHARACTERS = '.,'
See [DBAL-345](http://www.doctrine-project.org/jira/browse/DBAL-345) for more details.
## Doctrine\DBAL\Connection and Doctrine\DBAL\Statement
The query related methods including but not limited to executeQuery, exec, query, and executeUpdate
now wrap the driver exceptions such as PDOException with DBALException to add more debugging
information such as the executed SQL statement, and any bound parameters.
If you want to retrieve the driver specific exception, you can retrieve it by calling the
``getPrevious()`` method on DBALException.
Before:
catch(\PDOException $ex) {
// ...
}
After:
catch(\Doctrine\DBAL\DBALException $ex) {
$pdoException = $ex->getPrevious();
// ...
}
## Doctrine\DBAL\Connection#setCharsetSQL() removed
This method only worked on MySQL and it is considered unsafe on MySQL to use SET NAMES UTF-8 instead
of setting the charset directly on connection already. Replace this behavior with the
connection charset option:
Before:
$conn = DriverManager::getConnection(array(..));
$conn->setCharset('UTF8');
After:
$conn = DriverManager::getConnection(array('charset' => 'UTF8', ..));
## Doctrine\DBAL\Schema\Table#renameColumn() removed
Doctrine\DBAL\Schema\Table#renameColumn() was removed, because it drops and recreates
the column instead. There is no fix available, because a schema diff
cannot reliably detect if a column was renamed or one column was created
and another one dropped.
You should use explicit SQL ALTER TABLE statements to change columns names.
## Schema Filter paths
The Filter Schema assets expression is not wrapped in () anymore for the regexp automatically.
Before:
$config->setFilterSchemaAssetsExpression('foo');
After:
$config->setFilterSchemaAssetsExpression('(foo)');
## Creating MySQL Tables now defaults to UTF-8
If you are creating a new MySQL Table through the Doctrine API, charset/collate are
now set to 'utf8'/'utf8_unicode_ci' by default. Previously the MySQL server defaults were used.
# Upgrade to 2.2
## Doctrine\DBAL\Connection#insert and Doctrine\DBAL\Connection#update
Both methods now accept an optional last parameter $types with binding types of the values passed.
This can potentially break child classes that have overwritten one of these methods.
## Doctrine\DBAL\Connection#executeQuery
Doctrine\DBAL\Connection#executeQuery() got a new last parameter "QueryCacheProfile $qcp"
## Doctrine\DBAL\Driver\Statement split
The Driver statement was split into a ResultStatement and the normal statement extending from it.
This separates the configuration and the retrieval API from a statement.
## MsSql Platform/SchemaManager renamed
The MsSqlPlatform was renamed to SQLServerPlatform, the MsSqlSchemaManager was renamed
to SQLServerSchemaManager.
## Cleanup SQLServer Platform version mess
DBAL 2.1 and before were actually only compatible to SQL Server 2008, not earlier versions.
Still other parts of the platform did use old features instead of newly introduced datatypes
in SQL Server 2005. Starting with DBAL 2.2 you can pick the Doctrine abstraction exactly
matching your SQL Server version.
The PDO SqlSrv driver now uses the new `SQLServer2008Platform` as default platform.
This platform uses new features of SQL Server as of version 2008. This also includes a switch
in the used fields for "text" and "blob" field types to:
"text" => "VARCHAR(MAX)"
"blob" => "VARBINARY(MAX)"
Additionally `SQLServerPlatform` in DBAL 2.1 and before used "DATE", "TIME" and "DATETIME2" for dates.
This types are only available since version 2008 and the introduction of an explicit
SQLServer 2008 platform makes this dependency explicit.
An `SQLServer2005Platform` was also introduced to differentiate the features between
versions 2003, earlier and 2005.
With this change the `SQLServerPlatform` now throws an exception for using limit queries
with an offset, since SQLServer 2003 and lower do not support this feature.
To use the old SQL Server Platform, because you are using SQL Server 2003 and below use
the following configuration code:
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Platforms\SQLServer2005Platform;
// You are using SQL Server 2003 or earlier
$conn = DriverManager::getConnection(array(
'driver' => 'pdo_sqlsrv',
'platform' => new SQLServerPlatform()
// .. additional parameters
));
// You are using SQL Server 2005
$conn = DriverManager::getConnection(array(
'driver' => 'pdo_sqlsrv',
'platform' => new SQLServer2005Platform()
// .. additional parameters
));
// You are using SQL Server 2008
$conn = DriverManager::getConnection(array(
'driver' => 'pdo_sqlsrv',
// 2008 is default platform
// .. additional parameters
));

View File

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

View File

@@ -0,0 +1,57 @@
<?php
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 = [];
$helperSet = require $configFile;
if (! $helperSet instanceof HelperSet) {
foreach ($GLOBALS as $helperSetCandidate) {
if ($helperSetCandidate instanceof HelperSet) {
$helperSet = $helperSetCandidate;
break;
}
}
}
ConsoleRunner::run($helperSet, $commands);

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

@@ -0,0 +1,56 @@
{
"name": "doctrine/dbal",
"type": "library",
"description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.",
"keywords": [
"php",
"mysql",
"pgsql",
"dbal",
"database",
"abstraction",
"persistence",
"queryobject"
],
"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",
"ext-pdo": "*",
"doctrine/cache": "^1.0",
"doctrine/event-manager": "^1.0"
},
"require-dev": {
"doctrine/coding-standard": "^5.0",
"jetbrains/phpstorm-stubs": "^2018.1.2",
"phpstan/phpstan": "^0.10.1",
"phpunit/phpunit": "^7.4",
"symfony/console": "^2.0.5|^3.0|^4.0",
"symfony/phpunit-bridge": "^3.4.5|^4.0.5"
},
"suggest": {
"symfony/console": "For helpful console commands such as SQL execution and import of files."
},
"bin": ["bin/doctrine-dbal"],
"config": {
"sort-packages": true
},
"autoload": {
"psr-4": { "Doctrine\\DBAL\\": "lib/Doctrine/DBAL" }
},
"autoload-dev": {
"psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" }
},
"extra": {
"branch-alias": {
"dev-master": "2.9.x-dev",
"dev-develop": "3.0.x-dev"
}
}
}

View File

@@ -0,0 +1,137 @@
<?php
namespace Doctrine\DBAL\Cache;
use ArrayIterator;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\FetchMode;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use function array_merge;
use function array_values;
use function count;
use function reset;
class ArrayStatement implements IteratorAggregate, ResultStatement
{
/** @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}
*/
public function closeCursor()
{
unset($this->data);
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return $this->columnCount;
}
/**
* {@inheritdoc}
*/
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}
*/
public function getIterator()
{
$data = $this->fetchAll();
return new ArrayIterator($data);
}
/**
* {@inheritdoc}
*/
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}
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$rows = [];
while ($row = $this->fetch($fetchMode)) {
$rows[] = $row;
}
return $rows;
}
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
}
}

View File

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

View File

@@ -0,0 +1,121 @@
<?php
namespace Doctrine\DBAL\Cache;
use Doctrine\Common\Cache\Cache;
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 $query
* @param mixed[] $params
* @param int[]|string[] $types
* @param mixed[] $connectionParams
*
* @return string[]
*/
public function generateCacheKeys($query, $params, $types, array $connectionParams = [])
{
$realCacheKey = 'query=' . $query .
'&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 \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function setResultCacheDriver(Cache $cache)
{
return new QueryCacheProfile($this->lifetime, $this->cacheKey, $cache);
}
/**
* @param string|null $cacheKey
*
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function setCacheKey($cacheKey)
{
return new QueryCacheProfile($this->lifetime, $cacheKey, $this->resultCacheDriver);
}
/**
* @param int $lifetime
*
* @return \Doctrine\DBAL\Cache\QueryCacheProfile
*/
public function setLifetime($lifetime)
{
return new QueryCacheProfile($lifetime, $this->cacheKey, $this->resultCacheDriver);
}
}

View File

@@ -0,0 +1,201 @@
<?php
namespace Doctrine\DBAL\Cache;
use ArrayIterator;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Driver\ResultStatement;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\FetchMode;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
use function array_merge;
use function array_values;
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.
*/
class ResultCacheStatement implements IteratorAggregate, ResultStatement
{
/** @var Cache */
private $resultCache;
/** @var string */
private $cacheKey;
/** @var string */
private $realKey;
/** @var int */
private $lifetime;
/** @var Statement */
private $statement;
/**
* Did we reach the end of the statement?
*
* @var bool
*/
private $emptied = false;
/** @var mixed[] */
private $data;
/** @var int */
private $defaultFetchMode = FetchMode::MIXED;
/**
* @param string $cacheKey
* @param string $realKey
* @param int $lifetime
*/
public function __construct(Statement $stmt, Cache $resultCache, $cacheKey, $realKey, $lifetime)
{
$this->statement = $stmt;
$this->resultCache = $resultCache;
$this->cacheKey = $cacheKey;
$this->realKey = $realKey;
$this->lifetime = $lifetime;
}
/**
* {@inheritdoc}
*/
public function closeCursor()
{
$this->statement->closeCursor();
if (! $this->emptied || $this->data === null) {
return true;
}
$data = $this->resultCache->fetch($this->cacheKey);
if (! $data) {
$data = [];
}
$data[$this->realKey] = $this->data;
$this->resultCache->save($this->cacheKey, $data, $this->lifetime);
unset($this->data);
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return $this->statement->columnCount();
}
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
$data = $this->fetchAll();
return new ArrayIterator($data);
}
/**
* {@inheritdoc}
*/
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->emptied = true;
return false;
}
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$this->data = $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs);
$this->emptied = true;
return $this->data;
}
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
// TODO: verify that return false is the correct behavior
return $row[$columnIndex] ?? false;
}
/**
* 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 The number of rows.
*/
public function rowCount()
{
return $this->statement->rowCount();
}
}

View File

@@ -0,0 +1,32 @@
<?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.
*/
private function __construct()
{
}
}

View File

@@ -0,0 +1,158 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Logging\SQLLogger;
use Doctrine\DBAL\Schema\AbstractAsset;
use function preg_match;
/**
* Configuration container for the Doctrine DBAL.
*
* @internal 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 $filterExpression
*
* @return void
*/
public function setFilterSchemaAssetsExpression($filterExpression)
{
$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()
{
return $this->_attributes['filterSchemaAssetsExpression'] ?? null;
}
/**
* @param string $filterExpression
*/
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.
*/
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,38 @@
<?php
namespace Doctrine\DBAL;
class ConnectionException extends DBALException
{
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function commitFailedRollbackOnly()
{
return new self('Transaction commit failed because the transaction has been marked for rollback only.');
}
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function noActiveTransaction()
{
return new self('There is no active transaction.');
}
/**
* @return \Doctrine\DBAL\ConnectionException
*/
public static function savepointsNotSupported()
{
return new self('Savepoints are not supported by this driver.');
}
/**
* @return \Doctrine\DBAL\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,375 @@
<?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\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use InvalidArgumentException;
use function array_rand;
use function count;
use function func_get_args;
/**
* Master-Slave Connection
*
* Connection can be used with master-slave setups.
*
* Important for the understanding of this connection should be how and when
* it picks the slave or master.
*
* 1. Slave if master was never picked before and ONLY if 'getWrappedConnection'
* or 'executeQuery' is used.
* 2. Master picked when 'exec', 'executeUpdate', 'insert', 'delete', 'update', 'createSavepoint',
* 'releaseSavepoint', 'beginTransaction', 'rollback', 'commit', 'query' or
* 'prepare' is called.
* 3. If master was picked once during the lifetime of the connection it will always get picked afterwards.
* 4. One slave connection is randomly picked ONCE during a request.
*
* ATTENTION: You can write to the slave 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.
*
* This connection is limited to slave 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 slave, which makes
* this restricted approach necessary.
*
* You can manually connect to the master at any time by calling:
*
* $conn->connect('master');
*
* Instantiation through the DriverManager looks like:
*
* @example
*
* $conn = DriverManager::getConnection(array(
* 'wrapperClass' => 'Doctrine\DBAL\Connections\MasterSlaveConnection',
* 'driver' => 'pdo_mysql',
* 'master' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
* 'slaves' => array(
* array('user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
* array('user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
* )
* ));
*
* You can also pass 'driverOptions' and any other documented option to each of this drivers to pass additional information.
*/
class MasterSlaveConnection extends Connection
{
/**
* Master and slave connection (one of the randomly picked slaves).
*
* @var DriverConnection[]|null[]
*/
protected $connections = ['master' => null, 'slave' => null];
/**
* You can keep the slave connection and then switch back to it
* during the request if you know what you are doing.
*
* @var bool
*/
protected $keepSlave = false;
/**
* Creates Master Slave Connection.
*
* @param mixed[] $params
*
* @throws InvalidArgumentException
*/
public function __construct(array $params, Driver $driver, ?Configuration $config = null, ?EventManager $eventManager = null)
{
if (! isset($params['slaves'], $params['master'])) {
throw new InvalidArgumentException('master or slaves configuration missing');
}
if (count($params['slaves']) === 0) {
throw new InvalidArgumentException('You have to configure at least one slaves.');
}
$params['master']['driver'] = $params['driver'];
foreach ($params['slaves'] as $slaveKey => $slave) {
$params['slaves'][$slaveKey]['driver'] = $params['driver'];
}
$this->keepSlave = (bool) ($params['keepSlave'] ?? false);
parent::__construct($params, $driver, $config, $eventManager);
}
/**
* Checks if the connection is currently towards the master or not.
*
* @return bool
*/
public function isConnectedToMaster()
{
return $this->_conn !== null && $this->_conn === $this->connections['master'];
}
/**
* {@inheritDoc}
*/
public function connect($connectionName = null)
{
$requestedConnectionChange = ($connectionName !== null);
$connectionName = $connectionName ?: 'slave';
if ($connectionName !== 'slave' && $connectionName !== 'master') {
throw new InvalidArgumentException('Invalid option to connect(), only master or slave 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 slave in case of "keepSlave" option enabled.
if (isset($this->_conn) && $this->_conn && ! $requestedConnectionChange) {
return false;
}
$forceMasterAsSlave = false;
if ($this->getTransactionNestingLevel() > 0) {
$connectionName = 'master';
$forceMasterAsSlave = true;
}
if (isset($this->connections[$connectionName]) && $this->connections[$connectionName]) {
$this->_conn = $this->connections[$connectionName];
if ($forceMasterAsSlave && ! $this->keepSlave) {
$this->connections['slave'] = $this->_conn;
}
return false;
}
if ($connectionName === 'master') {
$this->connections['master'] = $this->_conn = $this->connectTo($connectionName);
// Set slave connection to master to avoid invalid reads
if (! $this->keepSlave) {
$this->connections['slave'] = $this->connections['master'];
}
} else {
$this->connections['slave'] = $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 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 === 'master') {
return $params['master'];
}
$config = $params['slaves'][array_rand($params['slaves'])];
if (! isset($config['charset']) && isset($params['master']['charset'])) {
$config['charset'] = $params['master']['charset'];
}
return $config;
}
/**
* {@inheritDoc}
*/
public function executeUpdate($query, array $params = [], array $types = [])
{
$this->connect('master');
return parent::executeUpdate($query, $params, $types);
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
$this->connect('master');
parent::beginTransaction();
}
/**
* {@inheritDoc}
*/
public function commit()
{
$this->connect('master');
parent::commit();
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
$this->connect('master');
return parent::rollBack();
}
/**
* {@inheritDoc}
*/
public function delete($tableName, array $identifier, array $types = [])
{
$this->connect('master');
return parent::delete($tableName, $identifier, $types);
}
/**
* {@inheritDoc}
*/
public function close()
{
unset($this->connections['master'], $this->connections['slave']);
parent::close();
$this->_conn = null;
$this->connections = ['master' => null, 'slave' => null];
}
/**
* {@inheritDoc}
*/
public function update($tableName, array $data, array $identifier, array $types = [])
{
$this->connect('master');
return parent::update($tableName, $data, $identifier, $types);
}
/**
* {@inheritDoc}
*/
public function insert($tableName, array $data, array $types = [])
{
$this->connect('master');
return parent::insert($tableName, $data, $types);
}
/**
* {@inheritDoc}
*/
public function exec($statement)
{
$this->connect('master');
return parent::exec($statement);
}
/**
* {@inheritDoc}
*/
public function createSavepoint($savepoint)
{
$this->connect('master');
parent::createSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function releaseSavepoint($savepoint)
{
$this->connect('master');
parent::releaseSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function rollbackSavepoint($savepoint)
{
$this->connect('master');
parent::rollbackSavepoint($savepoint);
}
/**
* {@inheritDoc}
*/
public function query()
{
$this->connect('master');
$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($statement)
{
$this->connect('master');
return parent::prepare($statement);
}
}

View File

@@ -0,0 +1,285 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\DBAL\Driver\DriverException as DriverExceptionInterface;
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Exception;
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 sprintf;
use function str_split;
class DBALException extends Exception
{
/**
* @param string $method
*
* @return \Doctrine\DBAL\DBALException
*/
public static function notSupported($method)
{
return new self(sprintf("Operation '%s' is not supported by platform.", $method));
}
public static function invalidPlatformSpecified() : self
{
return new self(
"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 self(
sprintf(
"Option 'platform' must be a subtype of '%s', instance of '%s' given",
AbstractPlatform::class,
get_class($invalidPlatform)
)
);
}
return new self(
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 DBALException
*/
public static function invalidPlatformVersionSpecified($version, $expectedFormat)
{
return new self(
sprintf(
'Invalid platform version "%s" specified. ' .
'The platform version has to be specified in the format: "%s".',
$version,
$expectedFormat
)
);
}
/**
* @return \Doctrine\DBAL\DBALException
*/
public static function invalidPdoInstance()
{
return new self(
"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 \Doctrine\DBAL\DBALException
*/
public static function driverRequired($url = null)
{
if ($url) {
return new self(
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 self("The options 'driver' or 'driverClass' are mandatory if no PDO " .
'instance is given to DriverManager::getConnection().');
}
/**
* @param string $unknownDriverName
* @param string[] $knownDrivers
*
* @return \Doctrine\DBAL\DBALException
*/
public static function unknownDriver($unknownDriverName, array $knownDrivers)
{
return new self("The given 'driver' " . $unknownDriverName . ' is unknown, ' .
'Doctrine currently supports only the following drivers: ' . implode(', ', $knownDrivers));
}
/**
* @param Exception $driverEx
* @param string $sql
* @param mixed[] $params
*
* @return \Doctrine\DBAL\DBALException
*/
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 static::wrapException($driver, $driverEx, $msg);
}
/**
* @param Exception $driverEx
*
* @return \Doctrine\DBAL\DBALException
*/
public static function driverException(Driver $driver, Throwable $driverEx)
{
return static::wrapException($driver, $driverEx, 'An exception occurred in driver: ' . $driverEx->getMessage());
}
/**
* @param Exception $driverEx
*
* @return \Doctrine\DBAL\DBALException
*/
private static function wrapException(Driver $driver, Throwable $driverEx, $msg)
{
if ($driverEx instanceof DriverException) {
return $driverEx;
}
if ($driver instanceof ExceptionConverterDriver && $driverEx instanceof DriverExceptionInterface) {
return $driver->convertException($msg, $driverEx);
}
return new self($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 '"\x' . implode('\x', str_split(bin2hex($param), 2)) . '"';
}
return $json;
}, $params)) . ']';
}
/**
* @param string $wrapperClass
*
* @return \Doctrine\DBAL\DBALException
*/
public static function invalidWrapperClass($wrapperClass)
{
return new self("The given 'wrapperClass' " . $wrapperClass . ' has to be a ' .
'subtype of \Doctrine\DBAL\Connection.');
}
/**
* @param string $driverClass
*
* @return \Doctrine\DBAL\DBALException
*/
public static function invalidDriverClass($driverClass)
{
return new self("The given 'driverClass' " . $driverClass . ' has to implement the ' . Driver::class . ' interface.');
}
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\DBALException
*/
public static function invalidTableName($tableName)
{
return new self('Invalid table name specified: ' . $tableName);
}
/**
* @param string $tableName
*
* @return \Doctrine\DBAL\DBALException
*/
public static function noColumnsSpecifiedForTable($tableName)
{
return new self('No columns specified for table ' . $tableName);
}
/**
* @return \Doctrine\DBAL\DBALException
*/
public static function limitOffsetInvalid()
{
return new self('Invalid Offset in Limit Query, it has to be larger than or equal to 0.');
}
/**
* @param string $name
*
* @return \Doctrine\DBAL\DBALException
*/
public static function typeExists($name)
{
return new self('Type ' . $name . ' already exists.');
}
/**
* @param string $name
*
* @return \Doctrine\DBAL\DBALException
*/
public static function unknownColumnType($name)
{
return new self('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 \Doctrine\DBAL\DBALException
*/
public static function typeNotFound($name)
{
return new self('Type to be overwritten ' . $name . ' does not exist.');
}
}

View File

@@ -0,0 +1,55 @@
<?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.
*
* @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.
*
* @return string The name of the driver.
*/
public function getName();
/**
* Gets the name of the database connected to for this driver.
*
* @return string The name of the database.
*/
public function getDatabase(Connection $conn);
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Platforms\DB2Platform;
use Doctrine\DBAL\Schema\DB2SchemaManager;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for IBM DB2 based drivers.
*/
abstract class AbstractDB2Driver implements Driver
{
/**
* {@inheritdoc}
*/
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);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Doctrine\DBAL\Driver;
use Exception;
/**
* Abstract base implementation of the {@link DriverException} interface.
*/
abstract class AbstractDriverException extends Exception 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)
{
parent::__construct($message);
$this->errorCode = $errorCode;
$this->sqlState = $sqlState;
}
/**
* {@inheritdoc}
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* {@inheritdoc}
*/
public function getSQLState()
{
return $this->sqlState;
}
}

View File

@@ -0,0 +1,218 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception;
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 preg_match;
use function stripos;
use function version_compare;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for MySQL based drivers.
*/
abstract class AbstractMySQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-client.html
* @link http://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
*/
public function convertException($message, DriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1213':
return new Exception\DeadlockException($message, $exception);
case '1205':
return new Exception\LockWaitTimeoutException($message, $exception);
case '1050':
return new Exception\TableExistsException($message, $exception);
case '1051':
case '1146':
return new Exception\TableNotFoundException($message, $exception);
case '1216':
case '1217':
case '1451':
case '1452':
case '1701':
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case '1062':
case '1557':
case '1569':
case '1586':
return new Exception\UniqueConstraintViolationException($message, $exception);
case '1054':
case '1166':
case '1611':
return new Exception\InvalidFieldNameException($message, $exception);
case '1052':
case '1060':
case '1110':
return new Exception\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 Exception\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 Exception\ConnectionException($message, $exception);
case '1048':
case '1121':
case '1138':
case '1171':
case '1252':
case '1263':
case '1364':
case '1566':
return new Exception\NotNullConstraintViolationException($message, $exception);
}
return new Exception\DriverException($message, $exception);
}
/**
* {@inheritdoc}
*
* @throws DBALException
*/
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 DBALException
*/
private function getOracleMysqlVersionNumber(string $versionString) : string
{
if (! preg_match(
'/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/',
$versionString,
$versionParts
)) {
throw DBALException::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 DBALException
*/
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 DBALException::invalidPlatformVersionSpecified(
$versionString,
'^(?:5\.5\.5-)?(mariadb-)?<major_version>.<minor_version>.<patch_version>'
);
}
return $versionParts['major'] . '.' . $versionParts['minor'] . '.' . $versionParts['patch'];
}
/**
* {@inheritdoc}
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'] ?? $conn->query('SELECT DATABASE()')->fetchColumn();
}
/**
* {@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,97 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Driver\AbstractOracleDriver\EasyConnectString;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Schema\OracleSchemaManager;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for Oracle based drivers.
*/
abstract class AbstractOracleDriver implements Driver, ExceptionConverterDriver
{
/**
* {@inheritdoc}
*/
public function convertException($message, DriverException $exception)
{
switch ($exception->getErrorCode()) {
case '1':
case '2299':
case '38911':
return new Exception\UniqueConstraintViolationException($message, $exception);
case '904':
return new Exception\InvalidFieldNameException($message, $exception);
case '918':
case '960':
return new Exception\NonUniqueFieldNameException($message, $exception);
case '923':
return new Exception\SyntaxErrorException($message, $exception);
case '942':
return new Exception\TableNotFoundException($message, $exception);
case '955':
return new Exception\TableExistsException($message, $exception);
case '1017':
case '12545':
return new Exception\ConnectionException($message, $exception);
case '1400':
return new Exception\NotNullConstraintViolationException($message, $exception);
case '2266':
case '2291':
case '2292':
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
}
return new Exception\DriverException($message, $exception);
}
/**
* {@inheritdoc}
*/
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,138 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception;
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 preg_match;
use function strpos;
use function version_compare;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for PostgreSQL based drivers.
*/
abstract class AbstractPostgreSQLDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @link http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html
*/
public function convertException($message, DriverException $exception)
{
switch ($exception->getSQLState()) {
case '40001':
case '40P01':
return new Exception\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 Exception\ForeignKeyConstraintViolationException($message, $exception);
}
break;
case '23502':
return new Exception\NotNullConstraintViolationException($message, $exception);
case '23503':
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case '23505':
return new Exception\UniqueConstraintViolationException($message, $exception);
case '42601':
return new Exception\SyntaxErrorException($message, $exception);
case '42702':
return new Exception\NonUniqueFieldNameException($message, $exception);
case '42703':
return new Exception\InvalidFieldNameException($message, $exception);
case '42P01':
return new Exception\TableNotFoundException($message, $exception);
case '42P07':
return new Exception\TableExistsException($message, $exception);
case '7':
// In some case (mainly connection errors) the PDO exception does not provide a SQLSTATE via its code.
// The exception code is 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 Exception\ConnectionException($message, $exception);
}
break;
}
return new Exception\DriverException($message, $exception);
}
/**
* {@inheritdoc}
*/
public function createDatabasePlatformForVersion($version)
{
if (! preg_match('/^(?P<major>\d+)(?:\.(?P<minor>\d+)(?:\.(?P<patch>\d+))?)?/', $version, $versionParts)) {
throw DBALException::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}
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'] ?? $conn->query('SELECT CURRENT_DATABASE()')->fetchColumn();
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new PostgreSqlPlatform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new PostgreSqlSchemaManager($conn);
}
}

View File

@@ -0,0 +1,127 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception;
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 preg_match;
use function version_compare;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SAP Sybase SQL Anywhere based drivers.
*/
abstract class AbstractSQLAnywhereDriver implements Driver, ExceptionConverterDriver, VersionAwarePlatformDriver
{
/**
* {@inheritdoc}
*
* @link http://dcx.sybase.com/index.html#sa160/en/saerrors/sqlerror.html
*/
public function convertException($message, DriverException $exception)
{
switch ($exception->getErrorCode()) {
case '-306':
case '-307':
case '-684':
return new Exception\DeadlockException($message, $exception);
case '-210':
case '-1175':
case '-1281':
return new Exception\LockWaitTimeoutException($message, $exception);
case '-100':
case '-103':
case '-832':
return new Exception\ConnectionException($message, $exception);
case '-143':
return new Exception\InvalidFieldNameException($message, $exception);
case '-193':
case '-196':
return new Exception\UniqueConstraintViolationException($message, $exception);
case '-194':
case '-198':
return new Exception\ForeignKeyConstraintViolationException($message, $exception);
case '-144':
return new Exception\NonUniqueFieldNameException($message, $exception);
case '-184':
case '-195':
return new Exception\NotNullConstraintViolationException($message, $exception);
case '-131':
return new Exception\SyntaxErrorException($message, $exception);
case '-110':
return new Exception\TableExistsException($message, $exception);
case '-141':
case '-1041':
return new Exception\TableNotFoundException($message, $exception);
}
return new Exception\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 DBALException::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}
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'] ?? $conn->query('SELECT DB_NAME()')->fetchColumn();
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new SQLAnywhere12Platform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new SQLAnywhereSchemaManager($conn);
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver;
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 preg_match;
use function version_compare;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\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 DBALException::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}
*/
public function getDatabase(Connection $conn)
{
$params = $conn->getParams();
return $params['dbname'] ?? $conn->query('SELECT DB_NAME()')->fetchColumn();
}
/**
* {@inheritdoc}
*/
public function getDatabasePlatform()
{
return new SQLServer2008Platform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(Connection $conn)
{
return new SQLServerSchemaManager($conn);
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Schema\SqliteSchemaManager;
use function strpos;
/**
* Abstract base implementation of the {@link Doctrine\DBAL\Driver} interface for SQLite based drivers.
*/
abstract class AbstractSQLiteDriver implements Driver, ExceptionConverterDriver
{
/**
* {@inheritdoc}
*
* @link http://www.sqlite.org/c3ref/c_abort.html
*/
public function convertException($message, DriverException $exception)
{
if (strpos($exception->getMessage(), 'database is locked') !== false) {
return new Exception\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 Exception\UniqueConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'may not be NULL') !== false ||
strpos($exception->getMessage(), 'NOT NULL constraint failed') !== false
) {
return new Exception\NotNullConstraintViolationException($message, $exception);
}
if (strpos($exception->getMessage(), 'no such table:') !== false) {
return new Exception\TableNotFoundException($message, $exception);
}
if (strpos($exception->getMessage(), 'already exists') !== false) {
return new Exception\TableExistsException($message, $exception);
}
if (strpos($exception->getMessage(), 'has no column named') !== false) {
return new Exception\InvalidFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'ambiguous column name') !== false) {
return new Exception\NonUniqueFieldNameException($message, $exception);
}
if (strpos($exception->getMessage(), 'syntax error') !== false) {
return new Exception\SyntaxErrorException($message, $exception);
}
if (strpos($exception->getMessage(), 'attempt to write a readonly database') !== false) {
return new Exception\ReadOnlyException($message, $exception);
}
if (strpos($exception->getMessage(), 'unable to open database file') !== false) {
return new Exception\ConnectionException($message, $exception);
}
return new Exception\DriverException($message, $exception);
}
/**
* {@inheritdoc}
*/
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,93 @@
<?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 $prepareString
*
* @return Statement
*/
public function prepare($prepareString);
/**
* 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 $input
* @param int $type
*
* @return mixed
*/
public function quote($input, $type = ParameterType::STRING);
/**
* Executes an SQL statement and return the number of affected rows.
*
* @param string $statement
*
* @return int
*/
public function exec($statement);
/**
* Returns the ID of the last inserted row or sequence value.
*
* @param string|null $name
*
* @return string
*/
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.
*
* @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.
*
* @return mixed[]
*/
public function errorInfo();
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Doctrine\DBAL\Driver;
use Throwable;
/**
* Contract for a driver exception.
*
* Driver exceptions provide the SQLSTATE of the driver
* and the driver specific error code at the time the error occurred.
*/
interface DriverException extends Throwable
{
/**
* Returns the driver specific error code if available.
*
* 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 driver error message.
*
* @return string
*/
public function getMessage();
/**
* 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,21 @@
<?php
namespace Doctrine\DBAL\Driver\DrizzlePDOMySql;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\ParameterType;
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,57 @@
<?php
namespace Doctrine\DBAL\Driver\DrizzlePDOMySql;
use Doctrine\DBAL\Platforms\DrizzlePlatform;
use Doctrine\DBAL\Schema\DrizzleSchemaManager;
/**
* Drizzle driver using PDO MySql.
*/
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}
*/
public function getDatabasePlatform()
{
return new DrizzlePlatform();
}
/**
* {@inheritdoc}
*/
public function getSchemaManager(\Doctrine\DBAL\Connection $conn)
{
return new DrizzleSchemaManager($conn);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'drizzle_pdo_mysql';
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* Contract for a driver that is capable of converting DBAL driver exceptions into standardized DBAL driver exceptions.
*/
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 Doctrine\DBAL\Exception\DriverException} subclass.
*
* @param string $message The DBAL exception message to use.
* @param DriverException $exception The DBAL driver exception to convert.
*
* @return \Doctrine\DBAL\Exception\DriverException An instance of one of the DriverException subclasses.
*/
public function convertException($message, DriverException $exception);
}

View File

@@ -0,0 +1,183 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use stdClass;
use const DB2_AUTOCOMMIT_OFF;
use const DB2_AUTOCOMMIT_ON;
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 db2_stmt_errormsg;
use function func_get_args;
class DB2Connection implements Connection, ServerInfoAwareConnection
{
/** @var resource */
private $conn = null;
/**
* @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) {
$this->conn = db2_pconnect($params['dbname'], $username, $password, $driverOptions);
} else {
$this->conn = db2_connect($params['dbname'], $username, $password, $driverOptions);
}
if (! $this->conn) {
throw new DB2Exception(db2_conn_errormsg());
}
}
/**
* {@inheritdoc}
*/
public function getServerVersion()
{
/** @var stdClass $serverInfo */
$serverInfo = db2_server_info($this->conn);
return $serverInfo->DBMS_VER;
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
return false;
}
/**
* {@inheritdoc}
*/
public function prepare($sql)
{
$stmt = @db2_prepare($this->conn, $sql);
if (! $stmt) {
throw new DB2Exception(db2_stmt_errormsg());
}
return new DB2Statement($stmt);
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$sql = $args[0];
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt;
}
/**
* {@inheritdoc}
*/
public function quote($input, $type = ParameterType::STRING)
{
$input = db2_escape_string($input);
if ($type === ParameterType::INTEGER) {
return $input;
}
return "'" . $input . "'";
}
/**
* {@inheritdoc}
*/
public function exec($statement)
{
$stmt = @db2_exec($this->conn, $statement);
if ($stmt === false) {
throw new DB2Exception(db2_stmt_errormsg());
}
return db2_num_rows($stmt);
}
/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
return db2_last_insert_id($this->conn);
}
/**
* {@inheritdoc}
*/
public function beginTransaction()
{
db2_autocommit($this->conn, DB2_AUTOCOMMIT_OFF);
}
/**
* {@inheritdoc}
*/
public function commit()
{
if (! db2_commit($this->conn)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
}
db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
}
/**
* {@inheritdoc}
*/
public function rollBack()
{
if (! db2_rollback($this->conn)) {
throw new DB2Exception(db2_conn_errormsg($this->conn));
}
db2_autocommit($this->conn, DB2_AUTOCOMMIT_ON);
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return db2_conn_error($this->conn);
}
/**
* {@inheritdoc}
*/
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;
/**
* IBM DB2 Driver.
*/
class DB2Driver extends AbstractDB2Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
if (! isset($params['protocol'])) {
$params['protocol'] = 'TCPIP';
}
if ($params['host'] !== 'localhost' && $params['host'] !== '127.0.0.1') {
// if the host isn't localhost, use extended connection params
$params['dbname'] = 'DRIVER={IBM DB2 ODBC DRIVER}' .
';DATABASE=' . $params['dbname'] .
';HOSTNAME=' . $params['host'] .
';PROTOCOL=' . $params['protocol'] .
';UID=' . $username .
';PWD=' . $password . ';';
if (isset($params['port'])) {
$params['dbname'] .= 'PORT=' . $params['port'];
}
$username = null;
$password = null;
}
return new DB2Connection($params, $username, $password, $driverOptions);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'ibm_db2';
}
}

View File

@@ -0,0 +1,9 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Exception;
class DB2Exception extends Exception
{
}

View File

@@ -0,0 +1,466 @@
<?php
namespace Doctrine\DBAL\Driver\IBMDB2;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use PDO;
use ReflectionClass;
use ReflectionObject;
use ReflectionProperty;
use stdClass;
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;
use function array_change_key_case;
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_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;
class DB2Statement implements IteratorAggregate, Statement
{
/** @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;
/**
* @param resource $stmt
*/
public function __construct($stmt)
{
$this->stmt = $stmt;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
switch ($type) {
case ParameterType::INTEGER:
$this->bind($column, $variable, DB2_PARAM_IN, DB2_LONG);
break;
case ParameterType::LARGE_OBJECT:
if (isset($this->lobs[$column])) {
[, $handle] = $this->lobs[$column];
fclose($handle);
}
$handle = $this->createTemporaryFile();
$path = stream_get_meta_data($handle)['uri'];
$this->bind($column, $path, DB2_PARAM_FILE, DB2_BINARY);
$this->lobs[$column] = [&$variable, $handle];
break;
default:
$this->bind($column, $variable, DB2_PARAM_IN, DB2_CHAR);
break;
}
return true;
}
/**
* @param int|string $parameter Parameter position or name
* @param mixed $variable
*
* @throws DB2Exception
*/
private function bind($parameter, &$variable, int $parameterType, int $dataType) : void
{
$this->bindParam[$parameter] =& $variable;
if (! db2_bind_param($this->stmt, $parameter, 'variable', $parameterType, $dataType)) {
throw new DB2Exception(db2_stmt_errormsg());
}
}
/**
* {@inheritdoc}
*/
public function closeCursor()
{
if (! $this->stmt) {
return false;
}
$this->bindParam = [];
if (! db2_free_result($this->stmt)) {
return false;
}
$this->result = false;
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
if (! $this->stmt) {
return false;
}
return db2_num_fields($this->stmt);
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return db2_stmt_error();
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return [
db2_stmt_errormsg(),
db2_stmt_error(),
];
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if (! $this->stmt) {
return false;
}
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 new DB2Exception(db2_stmt_errormsg());
}
$this->result = true;
return $retval;
}
/**
* {@inheritdoc}
*/
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}
*/
public function getIterator()
{
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*/
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}
*/
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}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return @db2_num_rows($this->stmt) ? : 0;
}
/**
* Casts a stdClass object to the given class name mapping its' properties.
*
* @param stdClass $sourceObject Object to cast from.
* @param 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 new DB2Exception('Could not create temporary file: ' . error_get_last()['message']);
}
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 new DB2Exception('Could not copy source stream to temporary file: ' . error_get_last()['message']);
}
}
/**
* @param resource $target
*
* @throws DB2Exception
*/
private function writeStringToStream(string $string, $target) : void
{
if (@fwrite($target, $string) === false) {
throw new DB2Exception('Could not write string to temporary file: ' . error_get_last()['message']);
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
return new MysqliConnection($params, $username, $password, $driverOptions);
} catch (MysqliException $e) {
throw DBALException::driverException($this, $e);
}
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'mysqli';
}
}

View File

@@ -0,0 +1,303 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\PingableConnection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use mysqli;
use const MYSQLI_INIT_COMMAND;
use const MYSQLI_OPT_CONNECT_TIMEOUT;
use const MYSQLI_OPT_LOCAL_INFILE;
use const MYSQLI_READ_DEFAULT_FILE;
use const MYSQLI_READ_DEFAULT_GROUP;
use const MYSQLI_SERVER_PUBLIC_KEY;
use function defined;
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 restore_error_handler;
use function set_error_handler;
use function sprintf;
use function stripos;
class MysqliConnection implements Connection, PingableConnection, ServerInfoAwareConnection
{
/**
* Name of the option to set connection flags
*/
public const OPTION_FLAGS = 'flags';
/** @var mysqli */
private $conn;
/**
* @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] ?? null;
$this->conn = mysqli_init();
$this->setSecureConnection($params);
$this->setDriverOptions($driverOptions);
set_error_handler(static function () {
});
try {
if (! $this->conn->real_connect($params['host'], $username, $password, $dbname, $port, $socket, $flags)) {
throw new MysqliException($this->conn->connect_error, $this->conn->sqlstate ?? 'HY000', $this->conn->connect_errno);
}
} finally {
restore_error_handler();
}
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()
{
return false;
}
/**
* {@inheritdoc}
*/
public function prepare($prepareString)
{
return new MysqliStatement($this->conn, $prepareString);
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$sql = $args[0];
$stmt = $this->prepare($sql);
$stmt->execute();
return $stmt;
}
/**
* {@inheritdoc}
*/
public function quote($input, $type = ParameterType::STRING)
{
return "'" . $this->conn->escape_string($input) . "'";
}
/**
* {@inheritdoc}
*/
public function exec($statement)
{
if ($this->conn->query($statement) === false) {
throw new MysqliException($this->conn->error, $this->conn->sqlstate, $this->conn->errno);
}
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()
{
return $this->conn->commit();
}
/**
* {@inheritdoc}non-PHPdoc)
*/
public function rollBack()
{
return $this->conn->rollback();
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return $this->conn->errno;
}
/**
* {@inheritdoc}
*/
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 = [])
{
$supportedDriverOptions = [
MYSQLI_OPT_CONNECT_TIMEOUT,
MYSQLI_OPT_LOCAL_INFILE,
MYSQLI_INIT_COMMAND,
MYSQLI_READ_DEFAULT_FILE,
MYSQLI_READ_DEFAULT_GROUP,
];
if (defined('MYSQLI_SERVER_PUBLIC_KEY')) {
$supportedDriverOptions[] = 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 new MysqliException(
sprintf($exceptionMsg, 'Unsupported', $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`
*
* @return bool
*/
public function ping()
{
return $this->conn->ping();
}
/**
* Establish a secure connection
*
* @param mixed[] $params
*
* @throws MysqliException
*/
private function setSecureConnection(array $params)
{
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'] ?? null,
$params['ssl_cert'] ?? null,
$params['ssl_ca'] ?? null,
$params['ssl_capath'] ?? null,
$params['ssl_cipher'] ?? null
);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\AbstractDriverException;
/**
* Exception thrown in case the mysqli driver errors.
*/
class MysqliException extends AbstractDriverException
{
}

View File

@@ -0,0 +1,458 @@
<?php
namespace Doctrine\DBAL\Driver\Mysqli;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\Exception\InvalidArgumentException;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use mysqli;
use mysqli_stmt;
use PDO;
use stdClass;
use function array_combine;
use function array_fill;
use function count;
use function feof;
use function fread;
use function get_resource_type;
use function is_resource;
use function sprintf;
use function str_repeat;
class MysqliStatement implements IteratorAggregate, Statement
{
/** @var string[] */
protected static $_paramTypeMap = [
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[]|bool|null */
protected $_columnNames;
/** @var mixed[]|null */
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;
/**
* @param string $prepareString
*
* @throws MysqliException
*/
public function __construct(mysqli $conn, $prepareString)
{
$this->_conn = $conn;
$this->_stmt = $conn->prepare($prepareString);
if ($this->_stmt === false) {
throw new MysqliException($this->_conn->error, $this->_conn->sqlstate, $this->_conn->errno);
}
$paramCount = $this->_stmt->param_count;
if (0 >= $paramCount) {
return;
}
$this->types = str_repeat('s', $paramCount);
$this->_bindedValues = array_fill(1, $paramCount, null);
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
if ($type === null) {
$type = 's';
} else {
if (! isset(self::$_paramTypeMap[$type])) {
throw new MysqliException(sprintf("Unknown type: '%s'", $type));
}
$type = self::$_paramTypeMap[$type];
}
$this->_bindedValues[$column] =& $variable;
$this->types[$column - 1] = $type;
return true;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
if ($type === null) {
$type = 's';
} else {
if (! isset(self::$_paramTypeMap[$type])) {
throw new MysqliException(sprintf("Unknown type: '%s'", $type));
}
$type = self::$_paramTypeMap[$type];
}
$this->_values[$param] = $value;
$this->_bindedValues[$param] =& $this->_values[$param];
$this->types[$param - 1] = $type;
return true;
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if ($this->_bindedValues !== null) {
if ($params !== null) {
if (! $this->_bindValues($params)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->errno);
}
} else {
[$types, $values, $streams] = $this->separateBoundValues();
if (! $this->_stmt->bind_param($types, ...$values)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
$this->sendLongData($streams);
}
}
if (! $this->_stmt->execute()) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
if ($this->_columnNames === null) {
$meta = $this->_stmt->result_metadata();
if ($meta !== false) {
$columnNames = [];
foreach ($meta->fetch_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 field 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 new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
}
$this->result = true;
return true;
}
/**
* Split $this->_bindedValues into those values that need to be sent using mysqli::send_long_data()
* and those that can be bound the usual way.
*
* @return array<int, array<int|string, mixed>|string>
*/
private function separateBoundValues()
{
$streams = $values = [];
$types = $this->types;
foreach ($this->_bindedValues as $parameter => $value) {
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;
} else {
$types[$parameter - 1] = static::$_paramTypeMap[ParameterType::STRING];
}
}
$values[$parameter] = $value;
}
return [$types, $values, $streams];
}
/**
* Handle $this->_longData after regular query parameters have been bound
*
* @throws MysqliException
*/
private function sendLongData($streams)
{
foreach ($streams as $paramNr => $stream) {
while (! feof($stream)) {
$chunk = fread($stream, 8192);
if ($chunk === false) {
throw new MysqliException("Failed reading the stream resource for parameter offset ${paramNr}.");
}
if (! $this->_stmt->send_long_data($paramNr - 1, $chunk)) {
throw new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
}
}
}
/**
* Binds a array of values to bound parameters.
*
* @param mixed[] $values
*
* @return bool
*/
private function _bindValues($values)
{
$params = [];
$types = str_repeat('s', count($values));
foreach ($values as &$v) {
$params[] =& $v;
}
return $this->_stmt->bind_param($types, ...$params);
}
/**
* @return mixed[]|false
*/
private function _fetch()
{
$ret = $this->_stmt->fetch();
if ($ret === true) {
$values = [];
foreach ($this->_rowBindedValues as $v) {
$values[] = $v;
}
return $values;
}
return $ret;
}
/**
* {@inheritdoc}
*/
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 new MysqliException($this->_stmt->error, $this->_stmt->sqlstate, $this->_stmt->errno);
}
switch ($fetchMode) {
case FetchMode::NUMERIC:
return $values;
case FetchMode::ASSOCIATIVE:
return array_combine($this->_columnNames, $values);
case FetchMode::MIXED:
$ret = array_combine($this->_columnNames, $values);
$ret += $values;
return $ret;
case FetchMode::STANDARD_OBJECT:
$assoc = array_combine($this->_columnNames, $values);
$ret = new stdClass();
foreach ($assoc as $column => $value) {
$ret->$column = $value;
}
return $ret;
default:
throw new MysqliException(sprintf("Unknown fetch type '%s'", $fetchMode));
}
}
/**
* {@inheritdoc}
*/
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}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return $this->_stmt->errno;
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return $this->_stmt->error;
}
/**
* {@inheritdoc}
*/
public function closeCursor()
{
$this->_stmt->free_result();
$this->result = false;
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;
}
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->_defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new StatementIterator($this);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use const OCI_DEFAULT;
/**
* 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 OCI8Connection(
$username,
$password,
$this->_constructDsn($params),
$params['charset'] ?? null,
$params['sessionMode'] ?? OCI_DEFAULT,
$params['persistent'] ?? false
);
} catch (OCI8Exception $e) {
throw DBALException::driverException($this, $e);
}
}
/**
* Constructs the Oracle DSN.
*
* @param mixed[] $params
*
* @return string The DSN.
*/
protected function _constructDsn(array $params)
{
return $this->getEasyConnectString($params);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'oci8';
}
}

View File

@@ -0,0 +1,227 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use UnexpectedValueException;
use const OCI_COMMIT_ON_SUCCESS;
use const OCI_DEFAULT;
use const OCI_NO_AUTO_COMMIT;
use function addcslashes;
use function define;
use function defined;
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;
/**
* OCI8 implementation of the Connection interface.
*/
class OCI8Connection implements Connection, ServerInfoAwareConnection
{
/** @var resource */
protected $dbh;
/** @var int */
protected $executeMode = OCI_COMMIT_ON_SUCCESS;
/**
* Creates a Connection to an Oracle Database using oci8 extension.
*
* @param string $username
* @param string $password
* @param string $db
* @param string|null $charset
* @param int $sessionMode
* @param bool $persistent
*
* @throws OCI8Exception
*/
public function __construct($username, $password, $db, $charset = null, $sessionMode = OCI_DEFAULT, $persistent = false)
{
if (! defined('OCI_NO_AUTO_COMMIT')) {
define('OCI_NO_AUTO_COMMIT', 0);
}
$this->dbh = $persistent
? @oci_pconnect($username, $password, $db, $charset, $sessionMode)
: @oci_connect($username, $password, $db, $charset, $sessionMode);
if (! $this->dbh) {
throw OCI8Exception::fromErrorInfo(oci_error());
}
}
/**
* {@inheritdoc}
*
* @throws UnexpectedValueException If the version string returned by the database server
* does not contain a parsable version number.
*/
public function getServerVersion()
{
if (! preg_match('/\s+(\d+\.\d+\.\d+\.\d+\.\d+)\s+/', oci_server_version($this->dbh), $version)) {
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.',
oci_server_version($this->dbh)
)
);
}
return $version[1];
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
return false;
}
/**
* {@inheritdoc}
*/
public function prepare($prepareString)
{
return new OCI8Statement($this->dbh, $prepareString, $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($statement)
{
$stmt = $this->prepare($statement);
$stmt->execute();
return $stmt->rowCount();
}
/**
* {@inheritdoc}
*/
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 new OCI8Exception('lastInsertId failed: Query was executed but no result was returned.');
}
return (int) $result;
}
/**
* Returns the current execution mode.
*
* @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}
*/
public function errorCode()
{
$error = oci_error($this->dbh);
if ($error !== false) {
$error = $error['code'];
}
return $error;
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return oci_error($this->dbh);
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\AbstractDriverException;
class OCI8Exception extends AbstractDriverException
{
/**
* @param mixed[] $error
*
* @return \Doctrine\DBAL\Driver\OCI8\OCI8Exception
*/
public static function fromErrorInfo($error)
{
return new self($error['message'], null, $error['code']);
}
}

View File

@@ -0,0 +1,510 @@
<?php
namespace Doctrine\DBAL\Driver\OCI8;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use InvalidArgumentException;
use IteratorAggregate;
use PDO;
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;
use function array_key_exists;
use function count;
use function implode;
use function is_numeric;
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 sprintf;
use function substr;
/**
* The OCI8 implementation of the Statement interface.
*/
class OCI8Statement implements IteratorAggregate, Statement
{
/** @var resource */
protected $_dbh;
/** @var resource */
protected $_sth;
/** @var OCI8Connection */
protected $_conn;
/** @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.
*
* @param resource $dbh The connection handle.
* @param string $statement The SQL statement.
*/
public function __construct($dbh, $statement, OCI8Connection $conn)
{
[$statement, $paramMap] = self::convertPositionalToNamedPlaceholders($statement);
$this->_sth = oci_parse($dbh, $statement);
$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.
*
* @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) {
throw new OCI8Exception(sprintf(
'The statement contains non-terminated string literal starting at offset %d',
$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 string $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 string $tokenOffset The offset to start searching from
* @param string|null $currentLiteralDelimiter The delimiter of the current string literal
* or NULL if not currently in a literal
*
* @return bool Whether the token was found
*/
private static function findClosingQuote(
$statement,
&$tokenOffset,
&$currentLiteralDelimiter
) {
$token = self::findToken(
$statement,
$tokenOffset,
'/' . preg_quote($currentLiteralDelimiter, '/') . '/'
);
if (! $token) {
return false;
}
$currentLiteralDelimiter = false;
++$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 string $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($column, &$variable, $type = ParameterType::STRING, $length = null)
{
$column = $this->_paramMap[$column] ?? $column;
if ($type === ParameterType::LARGE_OBJECT) {
$lob = oci_new_descriptor($this->_dbh, OCI_D_LOB);
$lob->writeTemporary($variable, OCI_TEMP_BLOB);
$variable =& $lob;
}
$this->boundValues[$column] =& $variable;
return oci_bind_by_name(
$this->_sth,
$column,
$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}
*/
public function closeCursor()
{
// not having the result means there's nothing to close
if (! $this->result) {
return true;
}
oci_cancel($this->_sth);
$this->result = false;
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return oci_num_fields($this->_sth);
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
$error = oci_error($this->_sth);
if ($error !== false) {
$error = $error['code'];
}
return $error;
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return oci_error($this->_sth);
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if ($params) {
$hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) {
if ($hasZeroIndex && is_numeric($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}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->_defaultFetchMode = $fetchMode;
return true;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*/
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}
*/
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}
*/
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);
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\ParameterType;
use PDO;
use function count;
use function func_get_args;
/**
* PDO implementation of the Connection interface.
* Used by all PDO-based drivers.
*/
class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection
{
/**
* @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, $user, $password, $options);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [PDOStatement::class, []]);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function exec($statement)
{
try {
return parent::exec($statement);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function getServerVersion()
{
return PDO::getAttribute(PDO::ATTR_SERVER_VERSION);
}
/**
* {@inheritdoc}
*/
public function prepare($prepareString, $driverOptions = [])
{
try {
return parent::prepare($prepareString, $driverOptions);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$argsCount = count($args);
try {
if ($argsCount === 4) {
return parent::query($args[0], $args[1], $args[2], $args[3]);
}
if ($argsCount === 3) {
return parent::query($args[0], $args[1], $args[2]);
}
if ($argsCount === 2) {
return parent::query($args[0], $args[1]);
}
return parent::query($args[0]);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function quote($input, $type = ParameterType::STRING)
{
return parent::quote($input, $type);
}
/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
try {
return parent::lastInsertId($name);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
return false;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* Tiny wrapper for PDOException instances to implement the {@link DriverException} interface.
*/
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()
{
return $this->errorCode;
}
/**
* {@inheritdoc}
*/
public function getSQLState()
{
return $this->sqlState;
}
}

View File

@@ -0,0 +1,57 @@
<?php
namespace Doctrine\DBAL\Driver\PDOIbm;
use Doctrine\DBAL\Driver\AbstractDB2Driver;
use Doctrine\DBAL\Driver\PDOConnection;
/**
* Driver for the PDO IBM extension.
*/
class Driver extends AbstractDB2Driver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
return new PDOConnection(
$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}
*/
public function getName()
{
return 'pdo_ibm';
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Doctrine\DBAL\Driver\PDOMySql;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractMySQLDriver;
use Doctrine\DBAL\Driver\PDOConnection;
use PDOException;
/**
* PDO MySql driver.
*/
class Driver extends AbstractMySQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
$conn = new PDOConnection(
$this->constructPdoDsn($params),
$username,
$password,
$driverOptions
);
} catch (PDOException $e) {
throw DBALException::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}
*/
public function getName()
{
return 'pdo_mysql';
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Doctrine\DBAL\Driver\PDOOracle;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractOracleDriver;
use Doctrine\DBAL\Driver\PDOConnection;
use PDOException;
/**
* PDO Oracle driver.
*
* WARNING: This driver gives us segfaults in our testsuites on CLOB and other
* stuff. PDO Oracle is not maintained by Oracle or anyone in the PHP community,
* which leads us to the recommendation to use the "oci8" driver to connect
* to Oracle instead.
*/
class Driver extends AbstractOracleDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
return new PDOConnection(
$this->constructPdoDsn($params),
$username,
$password,
$driverOptions
);
} catch (PDOException $e) {
throw DBALException::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()
{
return 'pdo_oracle';
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace Doctrine\DBAL\Driver\PDOPgSql;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractPostgreSQLDriver;
use Doctrine\DBAL\Driver\PDOConnection;
use PDO;
use PDOException;
use function defined;
/**
* Driver that connects through pdo_pgsql.
*/
class Driver extends AbstractPostgreSQLDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
try {
$pdo = new PDOConnection(
$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 DBALException::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}
*/
public function getName()
{
return 'pdo_pgsql';
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlite;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractSQLiteDriver;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use PDOException;
use function array_merge;
/**
* The PDO Sqlite driver.
*/
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 PDOConnection(
$this->_constructPdoDsn($params),
$username,
$password,
$driverOptions
);
} catch (PDOException $ex) {
throw DBALException::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}
*/
public function getName()
{
return 'pdo_sqlite';
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\ParameterType;
use PDO;
use function strpos;
use function substr;
/**
* Sqlsrv Connection implementation.
*/
class Connection extends PDOConnection
{
/**
* {@inheritdoc}
*/
public function __construct($dsn, $user = null, $password = null, ?array $options = null)
{
parent::__construct($dsn, $user, $password, $options);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [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]);
return $stmt->fetchColumn();
}
/**
* {@inheritDoc}
*/
public function quote($value, $type = ParameterType::STRING)
{
$val = parent::quote($value, $type);
// Fix for a driver version terminating all values with null byte
if (strpos($val, "\0") !== false) {
$val = substr($val, 0, -1);
}
return $val;
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
use function is_int;
use function sprintf;
/**
* The PDO-based Sqlsrv driver.
*/
class Driver extends AbstractSQLServerDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
[$driverOptions, $connectionOptions] = $this->splitOptions($driverOptions);
return new Connection(
$this->_constructPdoDsn($params, $connectionOptions),
$username,
$password,
$driverOptions
);
}
/**
* 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']) && ! empty($params['port'])) {
$dsn .= ',' . $params['port'];
}
if (isset($params['dbname'])) {
$connectionOptions['Database'] = $params['dbname'];
}
if (isset($params['MultipleActiveResultSets'])) {
$connectionOptions['MultipleActiveResultSets'] = $params['MultipleActiveResultSets'] ? 'true' : 'false';
}
return $dsn . $this->getConnectionOptionsDsn($connectionOptions);
}
/**
* Separates a connection options from a driver options
*
* @param int[]|string[] $options
*
* @return int[][]|string[][]
*/
private function splitOptions(array $options) : array
{
$driverOptions = [];
$connectionOptions = [];
foreach ($options as $optionKey => $optionValue) {
if (is_int($optionKey)) {
$driverOptions[$optionKey] = $optionValue;
} else {
$connectionOptions[$optionKey] = $optionValue;
}
}
return [$driverOptions, $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}
*/
public function getName()
{
return 'pdo_sqlsrv';
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Doctrine\DBAL\Driver\PDOSqlsrv;
use Doctrine\DBAL\Driver\PDOStatement;
use Doctrine\DBAL\ParameterType;
use PDO;
/**
* PDO SQL Server Statement
*/
class Statement extends PDOStatement
{
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
if (($type === ParameterType::LARGE_OBJECT || $type === ParameterType::BINARY)
&& $driverOptions === null
) {
$driverOptions = PDO::SQLSRV_ENCODING_BINARY;
}
return parent::bindParam($column, $variable, $type, $length, $driverOptions);
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type);
}
}

View File

@@ -0,0 +1,231 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use PDO;
use const E_USER_DEPRECATED;
use function sprintf;
use function trigger_error;
/**
* The PDO implementation of the Statement interface.
* Used by all PDO-based drivers.
*/
class PDOStatement extends \PDOStatement implements Statement
{
private const PARAM_TYPE_MAP = [
ParameterType::NULL => PDO::PARAM_NULL,
ParameterType::INTEGER => PDO::PARAM_INT,
ParameterType::STRING => 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.
*/
protected function __construct()
{
}
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$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.
try {
if ($arg2 === null && $arg3 === null) {
return parent::setFetchMode($fetchMode);
}
if ($arg3 === null) {
return parent::setFetchMode($fetchMode, $arg2);
}
return parent::setFetchMode($fetchMode, $arg2, $arg3);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
$type = $this->convertParamType($type);
try {
return parent::bindValue($param, $value, $type);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
{
$type = $this->convertParamType($type);
try {
return parent::bindParam($column, $variable, $type, $length, $driverOptions);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
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}
*/
public function execute($params = null)
{
try {
return parent::execute($params);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
$fetchMode = $this->convertFetchMode($fetchMode);
try {
if ($fetchMode === null && $cursorOrientation === PDO::FETCH_ORI_NEXT && $cursorOffset === 0) {
return parent::fetch();
}
if ($cursorOrientation === PDO::FETCH_ORI_NEXT && $cursorOffset === 0) {
return parent::fetch($fetchMode);
}
if ($cursorOffset === 0) {
return parent::fetch($fetchMode, $cursorOrientation);
}
return parent::fetch($fetchMode, $cursorOrientation, $cursorOffset);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
{
$fetchMode = $this->convertFetchMode($fetchMode);
try {
if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) {
return parent::fetchAll();
}
if ($fetchArgument === null && $ctorArgs === null) {
return parent::fetchAll($fetchMode);
}
if ($ctorArgs === null) {
return parent::fetchAll($fetchMode, $fetchArgument);
}
return parent::fetchAll($fetchMode, $fetchArgument, $ctorArgs);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* {@inheritdoc}
*/
public function fetchColumn($columnIndex = 0)
{
try {
return parent::fetchColumn($columnIndex);
} catch (\PDOException $exception) {
throw new PDOException($exception);
}
}
/**
* 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
@trigger_error(sprintf(
'Using a PDO parameter type (%d given) is deprecated and will cause an error in Doctrine 3.0',
$type
), E_USER_DEPRECATED);
return $type;
}
return self::PARAM_TYPE_MAP[$type];
}
/**
* Converts DBAL fetch mode to PDO fetch mode
*
* @param int|null $fetchMode Fetch mode
*/
private function convertFetchMode(?int $fetchMode) : ?int
{
if ($fetchMode === null) {
return null;
}
if (! isset(self::FETCH_MODE_MAP[$fetchMode])) {
// TODO: next major: throw an exception
@trigger_error(sprintf(
'Using a PDO fetch mode or their combination (%d given)' .
' is deprecated and will cause an error in Doctrine 3.0',
$fetchMode
), E_USER_DEPRECATED);
return $fetchMode;
}
return self::FETCH_MODE_MAP[$fetchMode];
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* An interface for connections which support a "native" ping method.
*/
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,98 @@
<?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.
*
* @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.
*
* @param int $fetchMode The fetch mode must be one of the {@link \Doctrine\DBAL\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.
*
* @param int|null $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants,
* defaulting to {@link \Doctrine\DBAL\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.
*
* @param int|null $fetchMode Controls how the next row will be returned to the caller.
* The value must be one of the {@link \Doctrine\DBAL\FetchMode} constants,
* defaulting to {@link \Doctrine\DBAL\FetchMode::MIXED}.
* @param int|null $fetchArgument This argument has a different meaning depending on the value of the $fetchMode parameter:
* * {@link \Doctrine\DBAL\FetchMode::COLUMN}:
* Returns the indicated 0-indexed column.
* * {@link \Doctrine\DBAL\FetchMode::CUSTOM_OBJECT}:
* Returns instances of the specified class, mapping the columns of each row
* to named properties in the class.
* * \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 \Doctrine\DBAL\FetchMode} constants,
* defaulting to {@link \Doctrine\DBAL\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.
*
* @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,85 @@
<?php
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Driver\AbstractSQLAnywhereDriver;
use function array_keys;
use function array_map;
use function implode;
/**
* A Doctrine DBAL driver for the SAP Sybase SQL Anywhere PHP extension.
*/
class Driver extends AbstractSQLAnywhereDriver
{
/**
* {@inheritdoc}
*
* @throws DBALException 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 DBALException::driverException($this, $e);
}
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'sqlanywhere';
}
/**
* Build the connection string for given connection parameters and driver options.
*
* @param string $host Host address to connect to.
* @param int $port Port to use for the connection (default to SQL Anywhere standard port 2638).
* @param string $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 $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)
);
}
}

View File

@@ -0,0 +1,215 @@
<?php
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use function assert;
use function func_get_args;
use function is_float;
use function is_int;
use function is_resource;
use function is_string;
use function sasql_affected_rows;
use function sasql_commit;
use function sasql_connect;
use function sasql_error;
use function sasql_errorcode;
use function sasql_escape_string;
use function sasql_insert_id;
use function sasql_pconnect;
use function sasql_real_query;
use function sasql_rollback;
use function sasql_set_option;
/**
* SAP Sybase SQL Anywhere implementation of the Connection interface.
*/
class SQLAnywhereConnection implements Connection, ServerInfoAwareConnection
{
/** @var resource The SQL Anywhere connection resource. */
private $connection;
/**
* Connects to database with given connection string.
*
* @param string $dsn The connection string.
* @param bool $persistent Whether or not to establish a persistent connection.
*
* @throws SQLAnywhereException
*/
public function __construct($dsn, $persistent = false)
{
$this->connection = $persistent ? @sasql_pconnect($dsn) : @sasql_connect($dsn);
if (! is_resource($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError();
}
// Disable PHP warnings on error.
if (! sasql_set_option($this->connection, 'verbose_errors', false)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
// Enable auto committing by default.
if (! sasql_set_option($this->connection, 'auto_commit', 'on')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function beginTransaction()
{
if (! sasql_set_option($this->connection, 'auto_commit', 'off')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
return true;
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function commit()
{
if (! sasql_commit($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
$this->endTransaction();
return true;
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return sasql_errorcode($this->connection);
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return sasql_error($this->connection);
}
/**
* {@inheritdoc}
*/
public function exec($statement)
{
if (sasql_real_query($this->connection, $statement) === false) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
return sasql_affected_rows($this->connection);
}
/**
* {@inheritdoc}
*/
public function getServerVersion()
{
$version = $this->query("SELECT PROPERTY('ProductVersion')")->fetchColumn();
assert(is_string($version));
return $version;
}
/**
* {@inheritdoc}
*/
public function lastInsertId($name = null)
{
if ($name === null) {
return sasql_insert_id($this->connection);
}
return $this->query('SELECT ' . $name . '.CURRVAL')->fetchColumn();
}
/**
* {@inheritdoc}
*/
public function prepare($prepareString)
{
return new SQLAnywhereStatement($this->connection, $prepareString);
}
/**
* {@inheritdoc}
*/
public function query()
{
$args = func_get_args();
$stmt = $this->prepare($args[0]);
$stmt->execute();
return $stmt;
}
/**
* {@inheritdoc}
*/
public function quote($input, $type = ParameterType::STRING)
{
if (is_int($input) || is_float($input)) {
return $input;
}
return "'" . sasql_escape_string($this->connection, $input) . "'";
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
return true;
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function rollBack()
{
if (! sasql_rollback($this->connection)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
$this->endTransaction();
return true;
}
/**
* Ends transactional mode and enables auto commit again.
*
* @return bool Whether or not ending transactional mode succeeded.
*
* @throws SQLAnywhereException
*/
private function endTransaction()
{
if (! sasql_set_option($this->connection, 'auto_commit', 'on')) {
throw SQLAnywhereException::fromSQLAnywhereError($this->connection);
}
return true;
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\AbstractDriverException;
use InvalidArgumentException;
use function is_resource;
use function sasql_error;
use function sasql_errorcode;
use function sasql_sqlstate;
use function sasql_stmt_errno;
use function sasql_stmt_error;
/**
* SAP Sybase SQL Anywhere driver exception.
*/
class SQLAnywhereException extends AbstractDriverException
{
/**
* Helper method to turn SQL Anywhere error into exception.
*
* @param resource|null $conn The SQL Anywhere connection resource to retrieve the last error from.
* @param resource|null $stmt The SQL Anywhere statement resource to retrieve the last error from.
*
* @return SQLAnywhereException
*
* @throws InvalidArgumentException
*/
public static function fromSQLAnywhereError($conn = null, $stmt = null)
{
if ($conn !== null && ! is_resource($conn)) {
throw new InvalidArgumentException('Invalid SQL Anywhere connection resource given: ' . $conn);
}
if ($stmt !== null && ! is_resource($stmt)) {
throw new InvalidArgumentException('Invalid SQL Anywhere statement resource given: ' . $stmt);
}
$state = $conn ? sasql_sqlstate($conn) : sasql_sqlstate();
$code = null;
$message = null;
/**
* Try retrieving the last error from statement resource if given
*/
if ($stmt) {
$code = sasql_stmt_errno($stmt);
$message = sasql_stmt_error($stmt);
}
/**
* Try retrieving the last error from the connection resource
* if either the statement resource is not given or the statement
* resource is given but the last error could not be retrieved from it (fallback).
* Depending on the type of error, it is sometimes necessary to retrieve
* it from the connection resource even though it occurred during
* a prepared statement.
*/
if ($conn && ! $code) {
$code = sasql_errorcode($conn);
$message = sasql_error($conn);
}
/**
* Fallback mode if either no connection resource is given
* or the last error could not be retrieved from the given
* connection / statement resource.
*/
if (! $conn || ! $code) {
$code = sasql_errorcode();
$message = sasql_error();
}
if ($message) {
return new self('SQLSTATE [' . $state . '] [' . $code . '] ' . $message, $state, $code);
}
return new self('SQL Anywhere error occurred but no error message was retrieved from driver.', $state, $code);
}
}

View File

@@ -0,0 +1,362 @@
<?php
namespace Doctrine\DBAL\Driver\SQLAnywhere;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use PDO;
use ReflectionClass;
use ReflectionObject;
use stdClass;
use const SASQL_BOTH;
use function array_key_exists;
use function func_get_args;
use function func_num_args;
use function gettype;
use function is_array;
use function is_numeric;
use function is_object;
use function is_resource;
use function is_string;
use function sasql_fetch_array;
use function sasql_fetch_assoc;
use function sasql_fetch_object;
use function sasql_fetch_row;
use function sasql_prepare;
use function sasql_stmt_affected_rows;
use function sasql_stmt_bind_param_ex;
use function sasql_stmt_errno;
use function sasql_stmt_error;
use function sasql_stmt_execute;
use function sasql_stmt_field_count;
use function sasql_stmt_reset;
use function sasql_stmt_result_metadata;
use function sprintf;
/**
* SAP SQL Anywhere implementation of the Statement interface.
*/
class SQLAnywhereStatement implements IteratorAggregate, Statement
{
/** @var resource The connection resource. */
private $conn;
/** @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 Default fetch mode to use. */
private $defaultFetchMode = FetchMode::MIXED;
/** @var resource The result set resource to fetch. */
private $result;
/** @var resource The prepared SQL statement to execute. */
private $stmt;
/** @var mixed[] The references to bound parameter values. */
private $boundValues = [];
/**
* Prepares given statement for given connection.
*
* @param resource $conn The connection resource to use.
* @param string $sql The SQL statement to prepare.
*
* @throws SQLAnywhereException
*/
public function __construct($conn, $sql)
{
if (! is_resource($conn)) {
throw new SQLAnywhereException('Invalid SQL Anywhere connection resource: ' . $conn);
}
$this->conn = $conn;
$this->stmt = sasql_prepare($conn, $sql);
if (! is_resource($this->stmt)) {
throw SQLAnywhereException::fromSQLAnywhereError($conn);
}
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
switch ($type) {
case ParameterType::INTEGER:
case ParameterType::BOOLEAN:
$type = 'i';
break;
case ParameterType::LARGE_OBJECT:
$type = 'b';
break;
case ParameterType::NULL:
case ParameterType::STRING:
case ParameterType::BINARY:
$type = 's';
break;
default:
throw new SQLAnywhereException('Unknown type: ' . $type);
}
$this->boundValues[$column] =& $variable;
if (! sasql_stmt_bind_param_ex($this->stmt, $column - 1, $variable, $type, $variable === null)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
}
return true;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
return $this->bindParam($param, $value, $type);
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function closeCursor()
{
if (! sasql_stmt_reset($this->stmt)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
}
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return sasql_stmt_field_count($this->stmt);
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
return sasql_stmt_errno($this->stmt);
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return sasql_stmt_error($this->stmt);
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function execute($params = null)
{
if (is_array($params)) {
$hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) {
$key = $hasZeroIndex && is_numeric($key) ? $key + 1 : $key;
$this->bindValue($key, $val);
}
}
if (! sasql_stmt_execute($this->stmt)) {
throw SQLAnywhereException::fromSQLAnywhereError($this->conn, $this->stmt);
}
$this->result = sasql_stmt_result_metadata($this->stmt);
return true;
}
/**
* {@inheritdoc}
*
* @throws SQLAnywhereException
*/
public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
{
if (! is_resource($this->result)) {
return false;
}
$fetchMode = $fetchMode ?: $this->defaultFetchMode;
switch ($fetchMode) {
case FetchMode::COLUMN:
return $this->fetchColumn();
case FetchMode::ASSOCIATIVE:
return sasql_fetch_assoc($this->result);
case FetchMode::MIXED:
return sasql_fetch_array($this->result, SASQL_BOTH);
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 = sasql_fetch_object($this->result);
if ($result instanceof stdClass) {
$result = $this->castObject($result, $className, $ctorArgs);
}
return $result;
case FetchMode::NUMERIC:
return sasql_fetch_row($this->result);
case FetchMode::STANDARD_OBJECT:
return sasql_fetch_object($this->result);
default:
throw new SQLAnywhereException('Fetch mode is not supported: ' . $fetchMode);
}
}
/**
* {@inheritdoc}
*/
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}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return sasql_stmt_affected_rows($this->stmt);
}
/**
* {@inheritdoc}
*/
public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
{
$this->defaultFetchMode = $fetchMode;
$this->defaultFetchClass = $arg2 ?: $this->defaultFetchClass;
$this->defaultFetchClassCtorArgs = $arg3 ? (array) $arg3 : $this->defaultFetchClassCtorArgs;
}
/**
* Casts a stdClass object to the given class name mapping its' properties.
*
* @param stdClass $sourceObject Object to cast from.
* @param 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 SQLAnywhereException
*/
private function castObject(stdClass $sourceObject, $destinationClass, array $ctorArgs = [])
{
if (! is_string($destinationClass)) {
if (! is_object($destinationClass)) {
throw new SQLAnywhereException(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);
foreach ($sourceReflection->getProperties() as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
if ($destinationClassReflection->hasProperty($name)) {
$destinationProperty = $destinationClassReflection->getProperty($name);
$destinationProperty->setAccessible(true);
$destinationProperty->setValue($destinationClass, $value);
} else {
$destinationClass->$name = $value;
}
}
return $destinationClass;
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\AbstractSQLServerDriver;
/**
* Driver for ext/sqlsrv.
*/
class Driver extends AbstractSQLServerDriver
{
/**
* {@inheritdoc}
*/
public function connect(array $params, $username = null, $password = null, array $driverOptions = [])
{
if (! isset($params['host'])) {
throw new SQLSrvException("Missing 'host' in configuration for sqlsrv driver.");
}
$serverName = $params['host'];
if (isset($params['port'])) {
$serverName .= ', ' . $params['port'];
}
if (isset($params['dbname'])) {
$driverOptions['Database'] = $params['dbname'];
}
if (isset($params['charset'])) {
$driverOptions['CharacterSet'] = $params['charset'];
}
if ($username !== null) {
$driverOptions['UID'] = $username;
}
if ($password !== null) {
$driverOptions['PWD'] = $password;
}
if (! isset($driverOptions['ReturnDatesAsStrings'])) {
$driverOptions['ReturnDatesAsStrings'] = 1;
}
return new SQLSrvConnection($serverName, $driverOptions);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'sqlsrv';
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace Doctrine\DBAL\Driver\SQLSrv;
/**
* Last Id Data Container.
*/
class LastInsertId
{
/** @var int */
private $id;
/**
* @param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
use Doctrine\DBAL\ParameterType;
use const SQLSRV_ERR_ERRORS;
use function func_get_args;
use function is_float;
use function is_int;
use function sprintf;
use function sqlsrv_begin_transaction;
use function sqlsrv_commit;
use function sqlsrv_configure;
use function sqlsrv_connect;
use function sqlsrv_errors;
use function sqlsrv_query;
use function sqlsrv_rollback;
use function sqlsrv_rows_affected;
use function sqlsrv_server_info;
use function str_replace;
/**
* SQL Server implementation for the Connection interface.
*/
class SQLSrvConnection implements Connection, ServerInfoAwareConnection
{
/** @var resource */
protected $conn;
/** @var LastInsertId */
protected $lastInsertId;
/**
* @param string $serverName
* @param mixed[] $connectionOptions
*
* @throws SQLSrvException
*/
public function __construct($serverName, $connectionOptions)
{
if (! sqlsrv_configure('WarningsReturnAsErrors', 0)) {
throw SQLSrvException::fromSqlSrvErrors();
}
$this->conn = sqlsrv_connect($serverName, $connectionOptions);
if (! $this->conn) {
throw SQLSrvException::fromSqlSrvErrors();
}
$this->lastInsertId = new LastInsertId();
}
/**
* {@inheritdoc}
*/
public function getServerVersion()
{
$serverInfo = sqlsrv_server_info($this->conn);
return $serverInfo['SQLServerVersion'];
}
/**
* {@inheritdoc}
*/
public function requiresQueryForServerVersion()
{
return false;
}
/**
* {@inheritDoc}
*/
public function prepare($sql)
{
return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId);
}
/**
* {@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)
{
if (is_int($value)) {
return $value;
} elseif (is_float($value)) {
return sprintf('%F', $value);
}
return "'" . str_replace("'", "''", $value) . "'";
}
/**
* {@inheritDoc}
*/
public function exec($statement)
{
$stmt = sqlsrv_query($this->conn, $statement);
if ($stmt === false) {
throw SQLSrvException::fromSqlSrvErrors();
}
return sqlsrv_rows_affected($stmt);
}
/**
* {@inheritDoc}
*/
public function lastInsertId($name = null)
{
if ($name !== null) {
$stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
$stmt->execute([$name]);
} else {
$stmt = $this->query('SELECT @@IDENTITY');
}
return $stmt->fetchColumn();
}
/**
* {@inheritDoc}
*/
public function beginTransaction()
{
if (! sqlsrv_begin_transaction($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function commit()
{
if (! sqlsrv_commit($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function rollBack()
{
if (! sqlsrv_rollback($this->conn)) {
throw SQLSrvException::fromSqlSrvErrors();
}
}
/**
* {@inheritDoc}
*/
public function errorCode()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
if ($errors) {
return $errors[0]['code'];
}
return false;
}
/**
* {@inheritDoc}
*/
public function errorInfo()
{
return sqlsrv_errors(SQLSRV_ERR_ERRORS);
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\AbstractDriverException;
use const SQLSRV_ERR_ERRORS;
use function rtrim;
use function sqlsrv_errors;
class SQLSrvException extends AbstractDriverException
{
/**
* Helper method to turn sql server errors into exception.
*
* @return \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException
*/
public static function fromSqlSrvErrors()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
$message = '';
$sqlState = null;
$errorCode = null;
foreach ($errors as $error) {
$message .= 'SQLSTATE [' . $error['SQLSTATE'] . ', ' . $error['code'] . ']: ' . $error['message'] . "\n";
if ($sqlState === null) {
$sqlState = $error['SQLSTATE'];
}
if ($errorCode !== null) {
continue;
}
$errorCode = $error['code'];
}
if (! $message) {
$message = 'SQL Server error occurred but no error message was retrieved from driver.';
}
return new self(rtrim($message), $sqlState, $errorCode);
}
}

View File

@@ -0,0 +1,411 @@
<?php
namespace Doctrine\DBAL\Driver\SQLSrv;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Driver\StatementIterator;
use Doctrine\DBAL\FetchMode;
use Doctrine\DBAL\ParameterType;
use IteratorAggregate;
use PDO;
use const SQLSRV_ENC_BINARY;
use const SQLSRV_ERR_ERRORS;
use const SQLSRV_FETCH_ASSOC;
use const SQLSRV_FETCH_BOTH;
use const SQLSRV_FETCH_NUMERIC;
use const SQLSRV_PARAM_IN;
use function array_key_exists;
use function count;
use function func_get_args;
use function in_array;
use function is_numeric;
use function sqlsrv_errors;
use function sqlsrv_execute;
use function sqlsrv_fetch;
use function sqlsrv_fetch_array;
use function sqlsrv_fetch_object;
use function sqlsrv_get_field;
use function sqlsrv_next_result;
use function sqlsrv_num_fields;
use function SQLSRV_PHPTYPE_STREAM;
use function SQLSRV_PHPTYPE_STRING;
use function sqlsrv_prepare;
use function sqlsrv_rows_affected;
use function SQLSRV_SQLTYPE_VARBINARY;
use function stripos;
/**
* SQL Server Statement.
*/
class SQLSrvStatement implements IteratorAggregate, Statement
{
/**
* The SQLSRV Resource.
*
* @var resource
*/
private $conn;
/**
* The SQL statement to execute.
*
* @var string
*/
private $sql;
/**
* The SQLSRV statement resource.
*
* @var resource|null
*/
private $stmt;
/**
* References to the variables bound as statement parameters.
*
* @var mixed
*/
private $variables = [];
/**
* Bound parameter types.
*
* @var int[]
*/
private $types = [];
/**
* Translations.
*
* @var int[]
*/
private static $fetchMap = [
FetchMode::MIXED => SQLSRV_FETCH_BOTH,
FetchMode::ASSOCIATIVE => SQLSRV_FETCH_ASSOC,
FetchMode::NUMERIC => SQLSRV_FETCH_NUMERIC,
];
/**
* The name of the default class to instantiate when fetching class instances.
*
* @var string
*/
private $defaultFetchClass = '\stdClass';
/**
* The constructor arguments for the default class to instantiate when fetching class instances.
*
* @var mixed[]
*/
private $defaultFetchClassCtorArgs = [];
/**
* The fetch style.
*
* @var int
*/
private $defaultFetchMode = FetchMode::MIXED;
/**
* The last insert ID.
*
* @var LastInsertId|null
*/
private $lastInsertId;
/**
* Indicates whether the statement is in the state when fetching results is possible
*
* @var bool
*/
private $result = false;
/**
* Append to any INSERT query to retrieve the last insert id.
*/
public const LAST_INSERT_ID_SQL = ';SELECT SCOPE_IDENTITY() AS LastInsertId;';
/**
* @param resource $conn
* @param string $sql
*/
public function __construct($conn, $sql, ?LastInsertId $lastInsertId = null)
{
$this->conn = $conn;
$this->sql = $sql;
if (stripos($sql, 'INSERT INTO ') !== 0) {
return;
}
$this->sql .= self::LAST_INSERT_ID_SQL;
$this->lastInsertId = $lastInsertId;
}
/**
* {@inheritdoc}
*/
public function bindValue($param, $value, $type = ParameterType::STRING)
{
if (! is_numeric($param)) {
throw new SQLSrvException(
'sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead.'
);
}
$this->variables[$param] = $value;
$this->types[$param] = $type;
}
/**
* {@inheritdoc}
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null)
{
if (! is_numeric($column)) {
throw new SQLSrvException('sqlsrv does not support named parameters to queries, use question mark (?) placeholders instead.');
}
$this->variables[$column] =& $variable;
$this->types[$column] = $type;
// unset the statement resource if it exists as the new one will need to be bound to the new variable
$this->stmt = null;
}
/**
* {@inheritdoc}
*/
public function closeCursor()
{
// not having the result means there's nothing to close
if (! $this->result) {
return true;
}
// emulate it by fetching and discarding rows, similarly to what PDO does in this case
// @link http://php.net/manual/en/pdostatement.closecursor.php
// @link https://github.com/php/php-src/blob/php-7.0.11/ext/pdo/pdo_stmt.c#L2075
// deliberately do not consider multiple result sets, since doctrine/dbal doesn't support them
while (sqlsrv_fetch($this->stmt)) {
}
$this->result = false;
return true;
}
/**
* {@inheritdoc}
*/
public function columnCount()
{
return sqlsrv_num_fields($this->stmt);
}
/**
* {@inheritdoc}
*/
public function errorCode()
{
$errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
if ($errors) {
return $errors[0]['code'];
}
return false;
}
/**
* {@inheritdoc}
*/
public function errorInfo()
{
return sqlsrv_errors(SQLSRV_ERR_ERRORS);
}
/**
* {@inheritdoc}
*/
public function execute($params = null)
{
if ($params) {
$hasZeroIndex = array_key_exists(0, $params);
foreach ($params as $key => $val) {
$key = $hasZeroIndex && is_numeric($key) ? $key + 1 : $key;
$this->bindValue($key, $val);
}
}
if (! $this->stmt) {
$this->stmt = $this->prepare();
}
if (! sqlsrv_execute($this->stmt)) {
throw SQLSrvException::fromSqlSrvErrors();
}
if ($this->lastInsertId) {
sqlsrv_next_result($this->stmt);
sqlsrv_fetch($this->stmt);
$this->lastInsertId->setId(sqlsrv_get_field($this->stmt, 0));
}
$this->result = true;
}
/**
* Prepares SQL Server statement resource
*
* @return resource
*
* @throws SQLSrvException
*/
private function prepare()
{
$params = [];
foreach ($this->variables as $column => &$variable) {
switch ($this->types[$column]) {
case ParameterType::LARGE_OBJECT:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY),
SQLSRV_SQLTYPE_VARBINARY('max'),
];
break;
case ParameterType::BINARY:
$params[$column - 1] = [
&$variable,
SQLSRV_PARAM_IN,
SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_BINARY),
];
break;
default:
$params[$column - 1] =& $variable;
break;
}
}
$stmt = sqlsrv_prepare($this->conn, $this->sql, $params);
if (! $stmt) {
throw SQLSrvException::fromSqlSrvErrors();
}
return $stmt;
}
/**
* {@inheritdoc}
*/
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}
*/
public function getIterator()
{
return new StatementIterator($this);
}
/**
* {@inheritdoc}
*
* @throws SQLSrvException
*/
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;
}
$args = func_get_args();
$fetchMode = $fetchMode ?: $this->defaultFetchMode;
if ($fetchMode === FetchMode::COLUMN) {
return $this->fetchColumn();
}
if (isset(self::$fetchMap[$fetchMode])) {
return sqlsrv_fetch_array($this->stmt, self::$fetchMap[$fetchMode]) ?: false;
}
if (in_array($fetchMode, [FetchMode::STANDARD_OBJECT, FetchMode::CUSTOM_OBJECT], true)) {
$className = $this->defaultFetchClass;
$ctorArgs = $this->defaultFetchClassCtorArgs;
if (count($args) >= 2) {
$className = $args[1];
$ctorArgs = $args[2] ?? [];
}
return sqlsrv_fetch_object($this->stmt, $className, $ctorArgs) ?: false;
}
throw new SQLSrvException('Fetch mode is not supported!');
}
/**
* {@inheritdoc}
*/
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}
*/
public function fetchColumn($columnIndex = 0)
{
$row = $this->fetch(FetchMode::NUMERIC);
if ($row === false) {
return false;
}
return $row[$columnIndex] ?? null;
}
/**
* {@inheritdoc}
*/
public function rowCount()
{
return sqlsrv_rows_affected($this->stmt);
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace Doctrine\DBAL\Driver;
/**
* Contract for a connection that is able to provide information about the server it is connected to.
*/
interface ServerInfoAwareConnection
{
/**
* Returns the version number of the database server connected to.
*
* @return string
*/
public function getServerVersion();
/**
* Checks whether a query is required to retrieve the database server version.
*
* @return bool True if a query is required to retrieve the database server version, false otherwise.
*/
public function requiresQueryForServerVersion();
}

View File

@@ -0,0 +1,105 @@
<?php
namespace Doctrine\DBAL\Driver;
use Doctrine\DBAL\ParameterType;
/**
* Statement interface.
* Drivers must implement this interface.
*
* This resembles (a subset of) the PDOStatement interface.
*/
interface Statement extends ResultStatement
{
/**
* Binds a value to a corresponding named (not supported by mysqli driver, see comment below) or positional
* placeholder in the SQL statement that was used to prepare the statement.
*
* As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(),
* fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine.
*
* @param mixed $param Parameter identifier. For a prepared statement using named placeholders,
* this will be a parameter name of the form :name. For a prepared statement
* using question mark placeholders, this will be the 1-indexed position of the parameter.
* @param mixed $value The value to bind to the parameter.
* @param int $type Explicit data type for the parameter using the {@link \Doctrine\DBAL\ParameterType}
* constants.
*
* @return bool TRUE on success or FALSE on failure.
*/
public function bindValue($param, $value, $type = ParameterType::STRING);
/**
* Binds a PHP variable to a corresponding named (not supported by mysqli driver, see comment below) or question
* mark placeholder in the SQL statement that was use to prepare the statement. Unlike PDOStatement->bindValue(),
* the variable is bound as a reference and will only be evaluated at the time
* that PDOStatement->execute() is called.
*
* As mentioned above, the named parameters are not natively supported by the mysqli driver, use executeQuery(),
* fetchAll(), fetchArray(), fetchColumn(), fetchAssoc() methods to have the named parameter emulated by doctrine.
*
* Most parameters are input parameters, that is, parameters that are
* used in a read-only fashion to build up the query. Some drivers support the invocation
* of stored procedures that return data as output parameters, and some also as input/output
* parameters that both send in data and are updated to receive it.
*
* @param mixed $column Parameter identifier. For a prepared statement using named placeholders,
* this will be a parameter name of the form :name. For a prepared statement using
* question mark placeholders, this will be the 1-indexed position of the parameter.
* @param mixed $variable Name of the PHP variable to bind to the SQL statement parameter.
* @param int|null $type Explicit data type for the parameter using the {@link \Doctrine\DBAL\ParameterType}
* constants. To return an INOUT parameter from a stored procedure, use the bitwise
* OR operator to set the PDO::PARAM_INPUT_OUTPUT bits for the data_type parameter.
* @param int|null $length You must specify maxlength when using an OUT bind
* so that PHP allocates enough memory to hold the returned value.
*
* @return bool TRUE on success or FALSE on failure.
*/
public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null);
/**
* Fetches the SQLSTATE associated with the last operation on the statement handle.
*
* @see Doctrine_Adapter_Interface::errorCode()
*
* @return string|int|bool The error code string.
*/
public function errorCode();
/**
* Fetches extended error information associated with the last operation on the statement handle.
*
* @return mixed[] The error info array.
*/
public function errorInfo();
/**
* Executes a prepared statement
*
* If the prepared statement included parameter markers, you must either:
* call PDOStatement->bindParam() to bind PHP variables to the parameter markers:
* bound variables pass their value as input and receive the output value,
* if any, of their associated parameter markers or pass an array of input-only
* parameter values.
*
* @param mixed[]|null $params An array of values with as many elements as there are
* bound parameters in the SQL statement being executed.
*
* @return bool TRUE on success or FALSE on failure.
*/
public function execute($params = null);
/**
* 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 The number of rows.
*/
public function rowCount();
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Doctrine\DBAL\Driver;
use IteratorAggregate;
class StatementIterator implements IteratorAggregate
{
/** @var Statement */
private $statement;
public function __construct(Statement $statement)
{
$this->statement = $statement;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
while (($result = $this->statement->fetch()) !== false) {
yield $result;
}
}
}

View File

@@ -0,0 +1,435 @@
<?php
namespace Doctrine\DBAL;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Driver\DrizzlePDOMySql\Driver as DrizzlePDOMySQLDriver;
use Doctrine\DBAL\Driver\IBMDB2\DB2Driver;
use Doctrine\DBAL\Driver\Mysqli\Driver as MySQLiDriver;
use Doctrine\DBAL\Driver\OCI8\Driver as OCI8Driver;
use Doctrine\DBAL\Driver\PDOMySql\Driver as PDOMySQLDriver;
use Doctrine\DBAL\Driver\PDOOracle\Driver as PDOOCIDriver;
use Doctrine\DBAL\Driver\PDOPgSql\Driver as PDOPgSQLDriver;
use Doctrine\DBAL\Driver\PDOSqlite\Driver as PDOSQLiteDriver;
use Doctrine\DBAL\Driver\PDOSqlsrv\Driver as PDOSQLSrvDriver;
use Doctrine\DBAL\Driver\SQLAnywhere\Driver as SQLAnywhereDriver;
use Doctrine\DBAL\Driver\SQLSrv\Driver as SQLSrvDriver;
use PDO;
use function array_keys;
use function array_map;
use function array_merge;
use function class_implements;
use function in_array;
use function is_subclass_of;
use function parse_str;
use function parse_url;
use function preg_replace;
use function str_replace;
use function strpos;
use function substr;
/**
* Factory for creating Doctrine\DBAL\Connection instances.
*/
final class DriverManager
{
/**
* List of supported drivers and their mappings to the driver classes.
*
* To add your own driver use the 'driverClass' parameter to
* {@link DriverManager::getConnection()}.
*
* @var string[]
*/
private static $_driverMap = [
'pdo_mysql' => PDOMySQLDriver::class,
'pdo_sqlite' => PDOSQLiteDriver::class,
'pdo_pgsql' => PDOPgSQLDriver::class,
'pdo_oci' => PDOOCIDriver::class,
'oci8' => OCI8Driver::class,
'ibm_db2' => DB2Driver::class,
'pdo_sqlsrv' => PDOSQLSrvDriver::class,
'mysqli' => MySQLiDriver::class,
'drizzle_pdo_mysql' => DrizzlePDOMySQLDriver::class,
'sqlanywhere' => SQLAnywhereDriver::class,
'sqlsrv' => SQLSrvDriver::class,
];
/**
* List of URL schemes from a database URL and their mappings to driver.
*
* @var string[]
*/
private static $driverSchemeAliases = [
'db2' => 'ibm_db2',
'mssql' => 'pdo_sqlsrv',
'mysql' => 'pdo_mysql',
'mysql2' => 'pdo_mysql', // Amazon RDS, for some weird reason
'postgres' => 'pdo_pgsql',
'postgresql' => 'pdo_pgsql',
'pgsql' => 'pdo_pgsql',
'sqlite' => 'pdo_sqlite',
'sqlite3' => 'pdo_sqlite',
];
/**
* Private constructor. This class cannot be instantiated.
*/
private function __construct()
{
}
/**
* Creates a connection object based on the specified parameters.
* This method returns a Doctrine\DBAL\Connection which wraps the underlying
* driver connection.
*
* $params must contain at least one of the following.
*
* Either 'driver' with one of the following values:
*
* pdo_mysql
* pdo_sqlite
* pdo_pgsql
* pdo_oci (unstable)
* pdo_sqlsrv
* pdo_sqlsrv
* mysqli
* sqlanywhere
* sqlsrv
* ibm_db2 (unstable)
* drizzle_pdo_mysql
*
* OR 'driverClass' that contains the full class name (with namespace) of the
* driver class to instantiate.
*
* Other (optional) parameters:
*
* <b>user (string)</b>:
* The username to use when connecting.
*
* <b>password (string)</b>:
* The password to use when connecting.
*
* <b>driverOptions (array)</b>:
* Any additional driver-specific options for the driver. These are just passed
* through to the driver.
*
* <b>pdo</b>:
* You can pass an existing PDO instance through this parameter. The PDO
* instance will be wrapped in a Doctrine\DBAL\Connection.
*
* <b>wrapperClass</b>:
* You may specify a custom wrapper class through the 'wrapperClass'
* parameter but this class MUST inherit from Doctrine\DBAL\Connection.
*
* <b>driverClass</b>:
* The driver class to use.
*
* @param mixed[] $params The parameters.
* @param Configuration|null $config The configuration to use.
* @param EventManager|null $eventManager The event manager to use.
*
* @throws DBALException
*/
public static function getConnection(
array $params,
?Configuration $config = null,
?EventManager $eventManager = null
) : Connection {
// create default config and event manager, if not set
if (! $config) {
$config = new Configuration();
}
if (! $eventManager) {
$eventManager = new EventManager();
}
$params = self::parseDatabaseUrl($params);
// URL support for MasterSlaveConnection
if (isset($params['master'])) {
$params['master'] = self::parseDatabaseUrl($params['master']);
}
if (isset($params['slaves'])) {
foreach ($params['slaves'] as $key => $slaveParams) {
$params['slaves'][$key] = self::parseDatabaseUrl($slaveParams);
}
}
// URL support for PoolingShardConnection
if (isset($params['global'])) {
$params['global'] = self::parseDatabaseUrl($params['global']);
}
if (isset($params['shards'])) {
foreach ($params['shards'] as $key => $shardParams) {
$params['shards'][$key] = self::parseDatabaseUrl($shardParams);
}
}
// check for existing pdo object
if (isset($params['pdo']) && ! $params['pdo'] instanceof PDO) {
throw DBALException::invalidPdoInstance();
} elseif (isset($params['pdo'])) {
$params['pdo']->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$params['driver'] = 'pdo_' . $params['pdo']->getAttribute(PDO::ATTR_DRIVER_NAME);
} else {
self::_checkParams($params);
}
$className = $params['driverClass'] ?? self::$_driverMap[$params['driver']];
$driver = new $className();
$wrapperClass = Connection::class;
if (isset($params['wrapperClass'])) {
if (! is_subclass_of($params['wrapperClass'], $wrapperClass)) {
throw DBALException::invalidWrapperClass($params['wrapperClass']);
}
$wrapperClass = $params['wrapperClass'];
}
return new $wrapperClass($params, $driver, $config, $eventManager);
}
/**
* Returns the list of supported drivers.
*
* @return string[]
*/
public static function getAvailableDrivers() : array
{
return array_keys(self::$_driverMap);
}
/**
* Checks the list of parameters.
*
* @param mixed[] $params The list of parameters.
*
* @throws DBALException
*/
private static function _checkParams(array $params) : void
{
// check existence of mandatory parameters
// driver
if (! isset($params['driver']) && ! isset($params['driverClass'])) {
throw DBALException::driverRequired();
}
// check validity of parameters
// driver
if (isset($params['driver']) && ! isset(self::$_driverMap[$params['driver']])) {
throw DBALException::unknownDriver($params['driver'], array_keys(self::$_driverMap));
}
if (isset($params['driverClass']) && ! in_array(Driver::class, class_implements($params['driverClass'], true))) {
throw DBALException::invalidDriverClass($params['driverClass']);
}
}
/**
* Normalizes the given connection URL path.
*
* @return string The normalized connection URL path
*/
private static function normalizeDatabaseUrlPath(string $urlPath) : string
{
// Trim leading slash from URL path.
return substr($urlPath, 1);
}
/**
* Extracts parts from a database URL, if present, and returns an
* updated list of parameters.
*
* @param mixed[] $params The list of parameters.
*
* @return mixed[] A modified list of parameters with info from a database
* URL extracted into indidivual parameter parts.
*
* @throws DBALException
*/
private static function parseDatabaseUrl(array $params) : array
{
if (! isset($params['url'])) {
return $params;
}
// (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
$url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $params['url']);
$url = parse_url($url);
if ($url === false) {
throw new DBALException('Malformed parameter "url".');
}
$url = array_map('rawurldecode', $url);
// If we have a connection URL, we have to unset the default PDO instance connection parameter (if any)
// as we cannot merge connection details from the URL into the PDO instance (URL takes precedence).
unset($params['pdo']);
$params = self::parseDatabaseUrlScheme($url, $params);
if (isset($url['host'])) {
$params['host'] = $url['host'];
}
if (isset($url['port'])) {
$params['port'] = $url['port'];
}
if (isset($url['user'])) {
$params['user'] = $url['user'];
}
if (isset($url['pass'])) {
$params['password'] = $url['pass'];
}
$params = self::parseDatabaseUrlPath($url, $params);
$params = self::parseDatabaseUrlQuery($url, $params);
return $params;
}
/**
* Parses the given connection URL and resolves the given connection parameters.
*
* Assumes that the connection URL scheme is already parsed and resolved into the given connection parameters
* via {@link parseDatabaseUrlScheme}.
*
* @see parseDatabaseUrlScheme
*
* @param mixed[] $url The URL parts to evaluate.
* @param mixed[] $params The connection parameters to resolve.
*
* @return mixed[] The resolved connection parameters.
*/
private static function parseDatabaseUrlPath(array $url, array $params) : array
{
if (! isset($url['path'])) {
return $params;
}
$url['path'] = self::normalizeDatabaseUrlPath($url['path']);
// If we do not have a known DBAL driver, we do not know any connection URL path semantics to evaluate
// and therefore treat the path as regular DBAL connection URL path.
if (! isset($params['driver'])) {
return self::parseRegularDatabaseUrlPath($url, $params);
}
if (strpos($params['driver'], 'sqlite') !== false) {
return self::parseSqliteDatabaseUrlPath($url, $params);
}
return self::parseRegularDatabaseUrlPath($url, $params);
}
/**
* Parses the query part of the given connection URL and resolves the given connection parameters.
*
* @param mixed[] $url The connection URL parts to evaluate.
* @param mixed[] $params The connection parameters to resolve.
*
* @return mixed[] The resolved connection parameters.
*/
private static function parseDatabaseUrlQuery(array $url, array $params) : array
{
if (! isset($url['query'])) {
return $params;
}
$query = [];
parse_str($url['query'], $query); // simply ingest query as extra params, e.g. charset or sslmode
return array_merge($params, $query); // parse_str wipes existing array elements
}
/**
* Parses the given regular connection URL and resolves the given connection parameters.
*
* Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}.
*
* @see normalizeDatabaseUrlPath
*
* @param mixed[] $url The regular connection URL parts to evaluate.
* @param mixed[] $params The connection parameters to resolve.
*
* @return mixed[] The resolved connection parameters.
*/
private static function parseRegularDatabaseUrlPath(array $url, array $params) : array
{
$params['dbname'] = $url['path'];
return $params;
}
/**
* Parses the given SQLite connection URL and resolves the given connection parameters.
*
* Assumes that the "path" URL part is already normalized via {@link normalizeDatabaseUrlPath}.
*
* @see normalizeDatabaseUrlPath
*
* @param mixed[] $url The SQLite connection URL parts to evaluate.
* @param mixed[] $params The connection parameters to resolve.
*
* @return mixed[] The resolved connection parameters.
*/
private static function parseSqliteDatabaseUrlPath(array $url, array $params) : array
{
if ($url['path'] === ':memory:') {
$params['memory'] = true;
return $params;
}
$params['path'] = $url['path']; // pdo_sqlite driver uses 'path' instead of 'dbname' key
return $params;
}
/**
* Parses the scheme part from given connection URL and resolves the given connection parameters.
*
* @param mixed[] $url The connection URL parts to evaluate.
* @param mixed[] $params The connection parameters to resolve.
*
* @return mixed[] The resolved connection parameters.
*
* @throws DBALException If parsing failed or resolution is not possible.
*/
private static function parseDatabaseUrlScheme(array $url, array $params) : array
{
if (isset($url['scheme'])) {
// The requested driver from the URL scheme takes precedence
// over the default custom driver from the connection parameters (if any).
unset($params['driverClass']);
// URL schemes must not contain underscores, but dashes are ok
$driver = str_replace('-', '_', $url['scheme']);
// The requested driver from the URL scheme takes precedence over the
// default driver from the connection parameters. If the driver is
// an alias (e.g. "postgres"), map it to the actual name ("pdo-pgsql").
// Otherwise, let checkParams decide later if the driver exists.
$params['driver'] = self::$driverSchemeAliases[$driver] ?? $driver;
return $params;
}
// If a schemeless connection URL is given, we require a default driver or default custom driver
// as connection parameter.
if (! isset($params['driverClass']) && ! isset($params['driver'])) {
throw DBALException::driverRequired($params['url']);
}
return $params;
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\Common\EventArgs;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
/**
* Event Arguments used when a Driver connection is established inside Doctrine\DBAL\Connection.
*/
class ConnectionEventArgs extends EventArgs
{
/** @var Connection */
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
/**
* @return Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* @return Driver
*/
public function getDriver()
{
return $this->connection->getDriver();
}
/**
* @return AbstractPlatform
*/
public function getDatabasePlatform()
{
return $this->connection->getDatabasePlatform();
}
/**
* @return AbstractSchemaManager
*/
public function getSchemaManager()
{
return $this->connection->getSchemaManager();
}
}

View File

@@ -0,0 +1,58 @@
<?php
namespace Doctrine\DBAL\Event\Listeners;
use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
/**
* MySQL Session Init Event Subscriber which allows to set the Client Encoding of the Connection.
*
* @deprecated Use "charset" option to PDO MySQL Connection instead.
*/
class MysqlSessionInit implements EventSubscriber
{
/**
* The charset.
*
* @var string
*/
private $charset;
/**
* The collation, or FALSE if no collation.
*
* @var string|bool
*/
private $collation;
/**
* Configure Charset and Collation options of MySQL Client for each Connection.
*
* @param string $charset The charset.
* @param string|bool $collation The collation, or FALSE if no collation.
*/
public function __construct($charset = 'utf8', $collation = false)
{
$this->charset = $charset;
$this->collation = $collation;
}
/**
* @return void
*/
public function postConnect(ConnectionEventArgs $args)
{
$collation = $this->collation ? ' COLLATE ' . $this->collation : '';
$args->getConnection()->executeUpdate('SET NAMES ' . $this->charset . $collation);
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return [Events::postConnect];
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace Doctrine\DBAL\Event\Listeners;
use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
use const CASE_UPPER;
use function array_change_key_case;
use function array_merge;
use function count;
use function implode;
/**
* Should be used when Oracle Server default environment does not match the Doctrine requirements.
*
* The following environment variables are required for the Doctrine default date format:
*
* NLS_TIME_FORMAT="HH24:MI:SS"
* NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS"
* NLS_TIMESTAMP_FORMAT="YYYY-MM-DD HH24:MI:SS"
* NLS_TIMESTAMP_TZ_FORMAT="YYYY-MM-DD HH24:MI:SS TZH:TZM"
*/
class OracleSessionInit implements EventSubscriber
{
/** @var string[] */
protected $_defaultSessionVars = [
'NLS_TIME_FORMAT' => 'HH24:MI:SS',
'NLS_DATE_FORMAT' => 'YYYY-MM-DD HH24:MI:SS',
'NLS_TIMESTAMP_FORMAT' => 'YYYY-MM-DD HH24:MI:SS',
'NLS_TIMESTAMP_TZ_FORMAT' => 'YYYY-MM-DD HH24:MI:SS TZH:TZM',
'NLS_NUMERIC_CHARACTERS' => '.,',
];
/**
* @param string[] $oracleSessionVars
*/
public function __construct(array $oracleSessionVars = [])
{
$this->_defaultSessionVars = array_merge($this->_defaultSessionVars, $oracleSessionVars);
}
/**
* @return void
*/
public function postConnect(ConnectionEventArgs $args)
{
if (! count($this->_defaultSessionVars)) {
return;
}
array_change_key_case($this->_defaultSessionVars, CASE_UPPER);
$vars = [];
foreach ($this->_defaultSessionVars as $option => $value) {
if ($option === 'CURRENT_SCHEMA') {
$vars[] = $option . ' = ' . $value;
} else {
$vars[] = $option . " = '" . $value . "'";
}
}
$sql = 'ALTER SESSION SET ' . implode(' ', $vars);
$args->getConnection()->executeUpdate($sql);
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return [Events::postConnect];
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace Doctrine\DBAL\Event\Listeners;
use Doctrine\Common\EventSubscriber;
use Doctrine\DBAL\Event\ConnectionEventArgs;
use Doctrine\DBAL\Events;
/**
* Session init listener for executing a single SQL statement right after a connection is opened.
*/
class SQLSessionInit implements EventSubscriber
{
/** @var string */
protected $sql;
/**
* @param string $sql
*/
public function __construct($sql)
{
$this->sql = $sql;
}
/**
* @return void
*/
public function postConnect(ConnectionEventArgs $args)
{
$conn = $args->getConnection();
$conn->exec($this->sql);
}
/**
* {@inheritdoc}
*/
public function getSubscribedEvents()
{
return [Events::postConnect];
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\TableDiff;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for adding table columns are generated inside Doctrine\DBAL\Platform\*Platform.
*/
class SchemaAlterTableAddColumnEventArgs extends SchemaEventArgs
{
/** @var Column */
private $column;
/** @var TableDiff */
private $tableDiff;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform)
{
$this->column = $column;
$this->tableDiff = $tableDiff;
$this->platform = $platform;
}
/**
* @return Column
*/
public function getColumn()
{
return $this->column;
}
/**
* @return TableDiff
*/
public function getTableDiff()
{
return $this->tableDiff;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableAddColumnEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\ColumnDiff;
use Doctrine\DBAL\Schema\TableDiff;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for changing table columns are generated inside Doctrine\DBAL\Platform\*Platform.
*/
class SchemaAlterTableChangeColumnEventArgs extends SchemaEventArgs
{
/** @var ColumnDiff */
private $columnDiff;
/** @var TableDiff */
private $tableDiff;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
public function __construct(ColumnDiff $columnDiff, TableDiff $tableDiff, AbstractPlatform $platform)
{
$this->columnDiff = $columnDiff;
$this->tableDiff = $tableDiff;
$this->platform = $platform;
}
/**
* @return ColumnDiff
*/
public function getColumnDiff()
{
return $this->columnDiff;
}
/**
* @return TableDiff
*/
public function getTableDiff()
{
return $this->tableDiff;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableChangeColumnEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\TableDiff;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\*Platform.
*/
class SchemaAlterTableEventArgs extends SchemaEventArgs
{
/** @var TableDiff */
private $tableDiff;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
public function __construct(TableDiff $tableDiff, AbstractPlatform $platform)
{
$this->tableDiff = $tableDiff;
$this->platform = $platform;
}
/**
* @return TableDiff
*/
public function getTableDiff()
{
return $this->tableDiff;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\TableDiff;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for removing table columns are generated inside Doctrine\DBAL\Platform\*Platform.
*/
class SchemaAlterTableRemoveColumnEventArgs extends SchemaEventArgs
{
/** @var Column */
private $column;
/** @var TableDiff */
private $tableDiff;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
public function __construct(Column $column, TableDiff $tableDiff, AbstractPlatform $platform)
{
$this->column = $column;
$this->tableDiff = $tableDiff;
$this->platform = $platform;
}
/**
* @return Column
*/
public function getColumn()
{
return $this->column;
}
/**
* @return TableDiff
*/
public function getTableDiff()
{
return $this->tableDiff;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableRemoveColumnEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\TableDiff;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for renaming table columns are generated inside Doctrine\DBAL\Platform\*Platform.
*/
class SchemaAlterTableRenameColumnEventArgs extends SchemaEventArgs
{
/** @var string */
private $oldColumnName;
/** @var Column */
private $column;
/** @var TableDiff */
private $tableDiff;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
/**
* @param string $oldColumnName
*/
public function __construct($oldColumnName, Column $column, TableDiff $tableDiff, AbstractPlatform $platform)
{
$this->oldColumnName = $oldColumnName;
$this->column = $column;
$this->tableDiff = $tableDiff;
$this->platform = $platform;
}
/**
* @return string
*/
public function getOldColumnName()
{
return $this->oldColumnName;
}
/**
* @return Column
*/
public function getColumn()
{
return $this->column;
}
/**
* @return TableDiff
*/
public function getTableDiff()
{
return $this->tableDiff;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaAlterTableRenameColumnEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Column;
/**
* Event Arguments used when the portable column definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager.
*/
class SchemaColumnDefinitionEventArgs extends SchemaEventArgs
{
/** @var Column|null */
private $column = null;
/**
* Raw column data as fetched from the database.
*
* @var mixed[]
*/
private $tableColumn;
/** @var string */
private $table;
/** @var string */
private $database;
/** @var Connection */
private $connection;
/**
* @param mixed[] $tableColumn
* @param string $table
* @param string $database
*/
public function __construct(array $tableColumn, $table, $database, Connection $connection)
{
$this->tableColumn = $tableColumn;
$this->table = $table;
$this->database = $database;
$this->connection = $connection;
}
/**
* Allows to clear the column which means the column will be excluded from
* tables column list.
*
* @return \Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs
*/
public function setColumn(?Column $column = null)
{
$this->column = $column;
return $this;
}
/**
* @return Column|null
*/
public function getColumn()
{
return $this->column;
}
/**
* @return mixed[]
*/
public function getTableColumn()
{
return $this->tableColumn;
}
/**
* @return string
*/
public function getTable()
{
return $this->table;
}
/**
* @return string
*/
public function getDatabase()
{
return $this->database;
}
/**
* @return Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* @return AbstractPlatform
*/
public function getDatabasePlatform()
{
return $this->connection->getDatabasePlatform();
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Table;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for creating table columns are generated inside Doctrine\DBAL\Platform\AbstractPlatform.
*/
class SchemaCreateTableColumnEventArgs extends SchemaEventArgs
{
/** @var Column */
private $column;
/** @var Table */
private $table;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
public function __construct(Column $column, Table $table, AbstractPlatform $platform)
{
$this->column = $column;
$this->table = $table;
$this->platform = $platform;
}
/**
* @return Column
*/
public function getColumn()
{
return $this->column;
}
/**
* @return Table
*/
public function getTable()
{
return $this->table;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaCreateTableColumnEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Table;
use function array_merge;
use function is_array;
/**
* Event Arguments used when SQL queries for creating tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform.
*/
class SchemaCreateTableEventArgs extends SchemaEventArgs
{
/** @var Table */
private $table;
/** @var Column[] */
private $columns;
/** @var mixed[] */
private $options;
/** @var AbstractPlatform */
private $platform;
/** @var string[] */
private $sql = [];
/**
* @param Column[] $columns
* @param mixed[] $options
*/
public function __construct(Table $table, array $columns, array $options, AbstractPlatform $platform)
{
$this->table = $table;
$this->columns = $columns;
$this->options = $options;
$this->platform = $platform;
}
/**
* @return Table
*/
public function getTable()
{
return $this->table;
}
/**
* @return Column[]
*/
public function getColumns()
{
return $this->columns;
}
/**
* @return mixed[]
*/
public function getOptions()
{
return $this->options;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string|string[] $sql
*
* @return \Doctrine\DBAL\Event\SchemaCreateTableEventArgs
*/
public function addSql($sql)
{
if (is_array($sql)) {
$this->sql = array_merge($this->sql, $sql);
} else {
$this->sql[] = $sql;
}
return $this;
}
/**
* @return string[]
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Table;
use InvalidArgumentException;
use function is_string;
/**
* Event Arguments used when the SQL query for dropping tables are generated inside Doctrine\DBAL\Platform\AbstractPlatform.
*/
class SchemaDropTableEventArgs extends SchemaEventArgs
{
/** @var string|Table */
private $table;
/** @var AbstractPlatform */
private $platform;
/** @var string|null */
private $sql = null;
/**
* @param string|Table $table
*
* @throws InvalidArgumentException
*/
public function __construct($table, AbstractPlatform $platform)
{
if (! $table instanceof Table && ! is_string($table)) {
throw new InvalidArgumentException('SchemaDropTableEventArgs expects $table parameter to be string or \Doctrine\DBAL\Schema\Table.');
}
$this->table = $table;
$this->platform = $platform;
}
/**
* @return string|Table
*/
public function getTable()
{
return $this->table;
}
/**
* @return AbstractPlatform
*/
public function getPlatform()
{
return $this->platform;
}
/**
* @param string $sql
*
* @return \Doctrine\DBAL\Event\SchemaDropTableEventArgs
*/
public function setSql($sql)
{
$this->sql = $sql;
return $this;
}
/**
* @return string|null
*/
public function getSql()
{
return $this->sql;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\Common\EventArgs;
/**
* Base class for schema related events.
*/
class SchemaEventArgs extends EventArgs
{
/** @var bool */
private $preventDefault = false;
/**
* @return \Doctrine\DBAL\Event\SchemaEventArgs
*/
public function preventDefault()
{
$this->preventDefault = true;
return $this;
}
/**
* @return bool
*/
public function isDefaultPrevented()
{
return $this->preventDefault;
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Doctrine\DBAL\Event;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Schema\Index;
/**
* Event Arguments used when the portable index definition is generated inside Doctrine\DBAL\Schema\AbstractSchemaManager.
*/
class SchemaIndexDefinitionEventArgs extends SchemaEventArgs
{
/** @var Index|null */
private $index = null;
/**
* Raw index data as fetched from the database.
*
* @var mixed[]
*/
private $tableIndex;
/** @var string */
private $table;
/** @var Connection */
private $connection;
/**
* @param mixed[] $tableIndex
* @param string $table
*/
public function __construct(array $tableIndex, $table, Connection $connection)
{
$this->tableIndex = $tableIndex;
$this->table = $table;
$this->connection = $connection;
}
/**
* Allows to clear the index which means the index will be excluded from tables index list.
*
* @return SchemaIndexDefinitionEventArgs
*/
public function setIndex(?Index $index = null)
{
$this->index = $index;
return $this;
}
/**
* @return Index|null
*/
public function getIndex()
{
return $this->index;
}
/**
* @return mixed[]
*/
public function getTableIndex()
{
return $this->tableIndex;
}
/**
* @return string
*/
public function getTable()
{
return $this->table;
}
/**
* @return Connection
*/
public function getConnection()
{
return $this->connection;
}
/**
* @return AbstractPlatform
*/
public function getDatabasePlatform()
{
return $this->connection->getDatabasePlatform();
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Doctrine\DBAL;
/**
* Container for all DBAL events.
*
* This class cannot be instantiated.
*/
final class Events
{
/**
* Private constructor. This class cannot be instantiated.
*/
private function __construct()
{
}
public const postConnect = 'postConnect';
public const onSchemaCreateTable = 'onSchemaCreateTable';
public const onSchemaCreateTableColumn = 'onSchemaCreateTableColumn';
public const onSchemaDropTable = 'onSchemaDropTable';
public const onSchemaAlterTable = 'onSchemaAlterTable';
public const onSchemaAlterTableAddColumn = 'onSchemaAlterTableAddColumn';
public const onSchemaAlterTableRemoveColumn = 'onSchemaAlterTableRemoveColumn';
public const onSchemaAlterTableChangeColumn = 'onSchemaAlterTableChangeColumn';
public const onSchemaAlterTableRenameColumn = 'onSchemaAlterTableRenameColumn';
public const onSchemaColumnDefinition = 'onSchemaColumnDefinition';
public const onSchemaIndexDefinition = 'onSchemaIndexDefinition';
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Base class for all connection related errors detected in the driver.
*/
class ConnectionException extends DriverException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Base class for all constraint violation related errors detected in the driver.
*/
class ConstraintViolationException extends ServerException
{
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Base class for all already existing database object related errors detected in the driver.
*
* A database object is considered any asset that can be created in a database
* such as schemas, tables, views, sequences, triggers, constraints, indexes,
* functions, stored procedures etc.
*/
class DatabaseObjectExistsException extends ServerException
{
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Base class for all unknown database object related errors detected in the driver.
*
* A database object is considered any asset that can be created in a database
* such as schemas, tables, views, sequences, triggers, constraints, indexes,
* functions, stored procedures etc.
*/
class DatabaseObjectNotFoundException extends ServerException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for a deadlock error of a transaction detected in the driver.
*/
class DeadlockException extends ServerException implements RetryableException
{
}

View File

@@ -0,0 +1,60 @@
<?php
namespace Doctrine\DBAL\Exception;
use Doctrine\DBAL\DBALException;
use Exception;
/**
* Base class for all errors detected in the driver.
*/
class DriverException extends DBALException
{
/**
* The previous DBAL driver exception.
*
* @var \Doctrine\DBAL\Driver\DriverException
*/
private $driverException;
/**
* @param string $message The exception message.
* @param \Doctrine\DBAL\Driver\DriverException $driverException The DBAL driver exception to chain.
*/
public function __construct($message, \Doctrine\DBAL\Driver\DriverException $driverException)
{
$exception = null;
if ($driverException instanceof Exception) {
$exception = $driverException;
}
parent::__construct($message, 0, $exception);
$this->driverException = $driverException;
}
/**
* Returns the driver specific error code if given.
*
* Returns null if no error code was given by the driver.
*
* @return int|string|null
*/
public function getErrorCode()
{
return $this->driverException->getErrorCode();
}
/**
* Returns the SQLSTATE the driver was in at the time the error occurred, if given.
*
* Returns null if no SQLSTATE was given by the driver.
*
* @return string|null
*/
public function getSQLState()
{
return $this->driverException->getSQLState();
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for a foreign key constraint violation detected in the driver.
*/
class ForeignKeyConstraintViolationException extends ConstraintViolationException
{
}

View File

@@ -0,0 +1,19 @@
<?php
namespace Doctrine\DBAL\Exception;
use Doctrine\DBAL\DBALException;
/**
* Exception to be thrown when invalid arguments are passed to any DBAL API
*/
class InvalidArgumentException extends DBALException
{
/**
* @return self
*/
public static function fromEmptyCriteria()
{
return new self('Empty criteria was used, expected non-empty criteria');
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for an invalid specified field name in a statement detected in the driver.
*/
class InvalidFieldNameException extends ServerException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for a lock wait timeout error of a transaction detected in the driver.
*/
class LockWaitTimeoutException extends ServerException implements RetryableException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for a non-unique/ambiguous specified field name in a statement detected in the driver.
*/
class NonUniqueFieldNameException extends ServerException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for a NOT NULL constraint violation detected in the driver.
*/
class NotNullConstraintViolationException extends ConstraintViolationException
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Doctrine\DBAL\Exception;
/**
* Exception for a write operation attempt on a read-only database element detected in the driver.
*/
class ReadOnlyException extends ServerException
{
}

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