Fixed Broken Shit

This commit is contained in:
2022-10-30 14:32:20 -07:00
parent 7335796263
commit 4dabf5a6bf
635 changed files with 74885 additions and 17688 deletions

100
includes/vendor/aura/sql/README.md vendored Normal file
View File

@ -0,0 +1,100 @@
# Aura.Sql
Provides an extension to the native [PDO](http://php.net/PDO) along with a
profiler and connection locator. Because _ExtendedPdo_ is an extension of the
native _PDO_, code already using the native _PDO_ or typehinted to the native
_PDO_ can use _ExtendedPdo_ without any changes.
Added functionality in _Aura.Sql_ over the native _PDO_ includes:
- **Lazy connection.** _ExtendedPdo_ connects to the database only on
method calls that require a connection. This means you can create an
instance and not incur the cost of a connection if you never make a query.
- **Decoration.** _DecoratedPdo_ can be used to decorate an existing PDO
instance. This means that a PDO instance can be "extended" **at runtime** to
provide the _ExtendedPdo_ behaviors.
- **Array quoting.** The `quote()` method will accept an array as input, and
return a string of comma-separated quoted values.
- **New `perform()` method.** The `perform()` method acts just like `query()`,
but binds values to a prepared statement as part of the call. In addition,
placeholders that represent array values will be replaced with comma-
separated quoted values. This means you can bind an array of values to a
placeholder used with an `IN (...)` condition when using `perform()`.
- **New `fetch*()` methods.** The new `fetch*()` methods provide for
commonly-used fetch actions. For example, you can call `fetchAll()` directly
on the instance instead of having to prepare a statement, bind values,
execute, and then fetch from the prepared statement. All of the `fetch*()`
methods take an array of values to bind to to the query statement, and use
the new `perform()` method internally.
- **New `yield*()` methods.** These are complements to the `fetch*()` methods
that `yield` results instead of `return`ing them.
- **Exceptions by default.** _ExtendedPdo_ starts in the `ERRMODE_EXCEPTION`
mode for error reporting instead of the `ERRMODE_SILENT` mode.
- **Profiler.** An optional query profiler is provided, along with an
interface for other implementations, that logs to any PSR-3 interface.
- **Connection locator.** A optional lazy-loading service locator is provided
for picking different database connections (default, read, and write).
## Installation and Autoloading
This package is installable and PSR-4 autoloadable via Composer as
[aura/sql][].
Alternatively, [download a release][], or clone this repository, then map the
`Aura\Sql\` namespace to the package `src/` directory.
## Dependencies
This package requires PHP 5.6 or later; it has also been tested on PHP 7 and
HHVM. We recommend using the latest available version of PHP as a matter of
principle.
Aura library packages may sometimes depend on external interfaces, but never on
external implementations. This allows compliance with community standards
without compromising flexibility. For specifics, please examine the package
[composer.json][] file.
## Quality
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/auraphp/Aura.Sql/badges/quality-score.png?b=3.x)](https://scrutinizer-ci.com/g/auraphp/Aura.Sql/)
[![Code Coverage](https://scrutinizer-ci.com/g/auraphp/Aura.Sql/badges/coverage.png?b=3.x)](https://scrutinizer-ci.com/g/auraphp/Aura.Sql/)
[![Build Status](https://travis-ci.org/auraphp/Aura.Sql.png?branch=3.x)](https://travis-ci.org/auraphp/Aura.Sql)
[![PDS Skeleton](https://img.shields.io/badge/pds-skeleton-blue.svg?style=flat-square)](https://github.com/php-pds/skeleton)
This project adheres to [Semantic Versioning](http://semver.org/).
To run the unit tests at the command line, issue `composer install` and then
`./vendor/bin/phpunit` at the package root. (This requires [Composer][] to be
available as `composer`.)
This package attempts to comply with [PSR-1][], [PSR-2][], and [PSR-4][]. If
you notice compliance oversights, please send a patch via pull request.
## Community
To ask questions, provide feedback, or otherwise communicate with other Aura
users, please join our [Google Group][], follow [@auraphp][], or chat with us
on Freenode in the #auraphp channel.
## Documentation
This package is fully documented [here](./docs/index.md).
[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md
[Composer]: http://getcomposer.org/
[Google Group]: http://groups.google.com/group/auraphp
[@auraphp]: http://twitter.com/auraphp
[download a release]: https://github.com/auraphp/Aura.Sql/releases
[aura/sql]: https://packagist.org/packages/aura/sql
[composer.json]: ./composer.json

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
/**
*
* Manages ExtendedPdo instances for default, read, and write connections.
*
* @package Aura.Sql
*
*/
class ConnectionLocator implements ConnectionLocatorInterface
{
/**
*
* A default ExtendedPdo connection factory/instance.
*
* @var callable
*
*/
protected $default;
/**
*
* A registry of ExtendedPdo "read" factories/instances.
*
* @var array
*
*/
protected $read = [];
/**
*
* A registry of ExtendedPdo "write" factories/instances.
*
* @var array
*
*/
protected $write = [];
/**
*
* Constructor.
*
* @param callable $default A callable to create a default connection.
*
* @param array $read An array of callables to create read connections.
*
* @param array $write An array of callables to create write connections.
*
*/
public function __construct(
$default = null,
array $read = [],
array $write = []
) {
if ($default) {
$this->setDefault($default);
}
foreach ($read as $name => $callable) {
$this->setRead($name, $callable);
}
foreach ($write as $name => $callable) {
$this->setWrite($name, $callable);
}
}
/**
*
* Sets the default connection factory.
*
* @param callable $callable The factory for the connection.
*
* @return null
*
*/
public function setDefault(callable $callable)
{
$this->default = $callable;
}
/**
*
* Returns the default connection object.
*
* @return ExtendedPdoInterface
*
*/
public function getDefault()
{
if (! $this->default instanceof ExtendedPdo) {
$this->default = call_user_func($this->default);
}
return $this->default;
}
/**
*
* Sets a read connection factory by name.
*
* @param string $name The name of the connection.
*
* @param callable $callable The factory for the connection.
*
* @return null
*
*/
public function setRead($name, callable $callable)
{
$this->read[$name] = $callable;
}
/**
*
* Returns a read connection by name; if no name is given, picks a
* random connection; if no read connections are present, returns the
* default connection.
*
* @param string $name The read connection name to return.
*
* @return ExtendedPdoInterface
*
*/
public function getRead($name = '')
{
return $this->getConnection('read', $name);
}
/**
*
* Sets a write connection factory by name.
*
* @param string $name The name of the connection.
*
* @param callable $callable The factory for the connection.
*
* @return null
*
*/
public function setWrite($name, callable $callable)
{
$this->write[$name] = $callable;
}
/**
*
* Returns a write connection by name; if no name is given, picks a
* random connection; if no write connections are present, returns the
* default connection.
*
* @param string $name The write connection name to return.
*
* @return ExtendedPdoInterface
*
*/
public function getWrite($name = '')
{
return $this->getConnection('write', $name);
}
/**
*
* Returns a connection by name.
*
* @param string $type The connection type ('read' or 'write').
*
* @param string $name The name of the connection.
*
* @return ExtendedPdoInterface
*
* @throws Exception\ConnectionNotFound
*
*/
protected function getConnection($type, $name)
{
$conn = &$this->{$type};
if (empty($conn)) {
return $this->getDefault();
}
if ($name === '') {
$name = array_rand($conn);
}
if (! isset($conn[$name])) {
throw new Exception\ConnectionNotFound("{$type}:{$name}");
}
if (! $conn[$name] instanceof ExtendedPdo) {
$conn[$name] = call_user_func($conn[$name]);
}
return $conn[$name];
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
/**
*
* Locates PDO connections for default, read, and write databases.
*
* @package Aura.Sql
*
*/
interface ConnectionLocatorInterface
{
/**
*
* Sets the default connection registry entry.
*
* @param callable $callable The registry entry.
*
* @return null
*
*/
public function setDefault(callable $callable);
/**
*
* Returns the default connection object.
*
* @return ExtendedPdoInterface
*
*/
public function getDefault();
/**
*
* Sets a read connection registry entry by name.
*
* @param string $name The name of the registry entry.
*
* @param callable $callable The registry entry.
*
* @return null
*
*/
public function setRead($name, callable $callable);
/**
*
* Returns a read connection by name; if no name is given, picks a
* random connection; if no read connections are present, returns the
* default connection.
*
* @param string $name The read connection name to return.
*
* @return ExtendedPdoInterface
*
*/
public function getRead($name = '');
/**
*
* Sets a write connection registry entry by name.
*
* @param string $name The name of the registry entry.
*
* @param callable $callable The registry entry.
*
* @return null
*
*/
public function setWrite($name, callable $callable);
/**
*
* Returns a write connection by name; if no name is given, picks a
* random connection; if no write connections are present, returns the
* default connection.
*
* @param string $name The write connection name to return.
*
* @return ExtendedPdoInterface
*
*/
public function getWrite($name = '');
}

View File

@ -0,0 +1,74 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
use Aura\Sql\Profiler\Profiler;
use Aura\Sql\Profiler\ProfilerInterface;
use PDO;
/**
*
* Decorates an existing PDO instance with the extended methods.
*
* @package Aura.Sql
*
*/
class DecoratedPdo extends AbstractExtendedPdo
{
/**
*
* Constructor.
*
* This overrides the parent so that it can take an existing PDO instance
* and decorate it with the extended methods.
*
* @param PDO $pdo An existing PDO instance to decorate.
*
* @param ProfilerInterface $profiler Tracks and logs query profiles.
*
*/
public function __construct(PDO $pdo, ProfilerInterface $profiler = null)
{
$this->pdo = $pdo;
if ($profiler === null) {
$profiler = new Profiler();
}
$this->setProfiler($profiler);
$driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
$this->setParser($this->newParser($driver));
$this->setQuoteName($driver);
}
/**
*
* Connects to the database.
*
* @return null
*
*/
public function connect()
{
// already connected
}
/**
*
* Disconnects from the database; disallowed with decorated PDO connections.
*
* @return null
*
*/
public function disconnect()
{
$message = "Cannot disconnect a DecoratedPdo instance.";
throw new Exception\CannotDisconnect($message);
}
}

View File

@ -0,0 +1,20 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
/**
*
* Base Exception class for Aura Sql
*
* @package Aura.Sql
*
*/
class Exception extends \Exception
{
}

View File

@ -0,0 +1,23 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Exception;
use Aura\Sql\Exception;
/**
*
* Could not bind a value to a placeholder in a statement, generally because
* the value is an array, object, or resource.
*
* @package Aura.Sql
*
*/
class CannotBindValue extends Exception
{
}

View File

@ -0,0 +1,23 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Exception;
use Aura\Sql\Exception;
/**
*
* ExtendedPdo could not disconnect; e.g., because its PDO connection was
* created externally and then injected.
*
* @package Aura.Sql
*
*/
class CannotDisconnect extends Exception
{
}

View File

@ -0,0 +1,22 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Exception;
use Aura\Sql\Exception;
/**
*
* Locator could not find a named connection.
*
* @package Aura.Sql
*
*/
class ConnectionNotFound extends Exception
{
}

View File

@ -0,0 +1,22 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Exception;
use Aura\Sql\Exception;
/**
*
* Missing a parameter in the values bound to a statement
*
* @package Aura.Sql
*
*/
class MissingParameter extends Exception
{
}

View File

@ -0,0 +1,163 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
use Aura\Sql\Profiler\Profiler;
use Aura\Sql\Profiler\ProfilerInterface;
use PDO;
/**
*
* A lazy-connecting PDO with extended methods.
*
* @package Aura.Sql
*
*/
class ExtendedPdo extends AbstractExtendedPdo
{
/**
*
* Constructor arguments for instantiating the PDO connection.
*
* @var array
*
*/
protected $args = [];
/**
*
* Constructor.
*
* This overrides the parent so that it can take connection attributes as a
* constructor parameter, and set them after connection.
*
* @param string $dsn The data source name for the connection.
*
* @param string $username The username for the connection.
*
* @param string $password The password for the connection.
*
* @param array $options Driver-specific options for the connection.
*
* @param array $queries Queries to execute after the connection.
*
* @param ProfilerInterface $profiler Tracks and logs query profiles.
*
* @see http://php.net/manual/en/pdo.construct.php
*
*/
public function __construct(
$dsn,
$username = null,
$password = null,
array $options = [],
array $queries = [],
ProfilerInterface $profiler = null
) {
// if no error mode is specified, use exceptions
if (! isset($options[PDO::ATTR_ERRMODE])) {
$options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
}
// retain the arguments for later
$this->args = [
$dsn,
$username,
$password,
$options,
$queries
];
// retain a profiler, instantiating a default one if needed
if ($profiler === null) {
$profiler = new Profiler();
}
$this->setProfiler($profiler);
// retain a query parser
$parts = explode(':', $dsn);
$parser = $this->newParser($parts[0]);
$this->setParser($parser);
// set quotes for identifier names
$this->setQuoteName($parts[0]);
}
/**
*
* Connects to the database.
*
* @return null
*
*/
public function connect()
{
if ($this->pdo) {
return;
}
// connect
$this->profiler->start(__FUNCTION__);
list($dsn, $username, $password, $options, $queries) = $this->args;
$this->pdo = new PDO($dsn, $username, $password, $options);
$this->profiler->finish();
// connection-time queries
foreach ($queries as $query) {
$this->exec($query);
}
}
/**
*
* Disconnects from the database.
*
* @return null
*
*/
public function disconnect()
{
$this->profiler->start(__FUNCTION__);
$this->pdo = null;
$this->profiler->finish();
}
/**
*
* The purpose of this method is to hide sensitive data from stack traces.
*
* @return array
*
*/
public function __debugInfo()
{
return [
'args' => [
$this->args[0],
'****',
'****',
$this->args[3],
$this->args[4],
]
];
}
/**
*
* Return the inner PDO (if any)
*
* @return \PDO
*
*/
public function getPdo()
{
$this->connect();
return $this->pdo;
}
}

View File

@ -0,0 +1,412 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
use Aura\Sql\Parser\ParserInterface;
use Aura\Sql\Profiler\ProfilerInterface;
use PDO;
/**
*
* An interface to the Aura.Sql extended PDO object.
*
* @package Aura.Sql
*
*/
interface ExtendedPdoInterface extends PdoInterface
{
/**
*
* Connects to the database.
*
*/
public function connect();
/**
*
* Disconnects from the database.
*
*/
public function disconnect();
/**
*
* Performs a statement and returns the number of affected rows.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return int
*
*/
public function fetchAffected($statement, array $values = []);
/**
*
* Fetches a sequential array of rows from the database; the rows
* are represented as associative arrays.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return array
*
*/
public function fetchAll($statement, array $values = []);
/**
*
* Fetches an associative array of rows from the database; the rows
* are represented as associative arrays. The array of rows is keyed
* on the first column of each row.
*
* N.b.: if multiple rows have the same first column value, the last
* row with that value will override earlier rows.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return array
*
*/
public function fetchAssoc($statement, array $values = []);
/**
*
* Fetches the first column of rows as a sequential array.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return array
*
*/
public function fetchCol($statement, array $values = []);
/**
*
* Fetches multiple from the database as an associative array.
* The first column will be the index
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @param int $style a fetch style defaults to PDO::FETCH_COLUMN for single
* values, use PDO::FETCH_NAMED when fetching a multiple columns
*
* @return array
*
*/
public function fetchGroup(
$statement,
array $values = [],
$style = PDO::FETCH_COLUMN
);
/**
*
* Fetches one row from the database as an object, mapping column values
* to object properties.
*
* Warning: PDO "injects property-values BEFORE invoking the constructor -
* in other words, if your class initializes property-values to defaults
* in the constructor, you will be overwriting the values injected by
* fetchObject() !"
* <http://www.php.net/manual/en/pdostatement.fetchobject.php#111744>
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @param string $class The name of the class to create.
*
* @param array $args Arguments to pass to the object constructor.
*
* @return object
*
*/
public function fetchObject(
$statement,
array $values = [],
$class = 'stdClass',
array $args = []
);
/**
*
* Fetches a sequential array of rows from the database; the rows
* are represented as objects, where the column values are mapped to
* object properties.
*
* Warning: PDO "injects property-values BEFORE invoking the constructor -
* in other words, if your class initializes property-values to defaults
* in the constructor, you will be overwriting the values injected by
* fetchObject() !"
* <http://www.php.net/manual/en/pdostatement.fetchobject.php#111744>
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @param string $class The name of the class to create from each
* row.
*
* @param array $args Arguments to pass to each object constructor.
*
* @return array
*
*/
public function fetchObjects(
$statement,
array $values = [],
$class = 'stdClass',
array $args = []
);
/**
*
* Fetches one row from the database as an associative array.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return array
*
*/
public function fetchOne($statement, array $values = []);
/**
*
* Fetches an associative array of rows as key-value pairs (first
* column is the key, second column is the value).
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return array
*
*/
public function fetchPairs($statement, array $values = []);
/**
*
* Fetches the very first value (i.e., first column of the first row).
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return mixed
*
*/
public function fetchValue($statement, array $values = []);
/**
*
* Returns the Parser instance.
*
* @return ParserInterface
*
*/
public function getParser();
/**
*
* Return the inner PDO (if any)
*
* @return \PDO
*
*/
public function getPdo();
/**
*
* Returns the Profiler instance.
*
* @return ProfilerInterface
*
*/
public function getProfiler();
/**
*
* Quotes a multi-part (dotted) identifier name.
*
* @param string $name The multi-part identifier name.
*
* @return string The multi-part identifier name, quoted.
*
*/
public function quoteName($name);
/**
*
* Quotes a single identifier name.
*
* @param string $name The identifier name.
*
* @return string The quoted identifier name.
*
*/
public function quoteSingleName($name);
/**
*
* Is the PDO connection active?
*
* @return bool
*
*/
public function isConnected();
/**
*
* Sets the Parser instance.
*
* @param ParserInterface $parser The Parser instance.
*
*/
public function setParser(ParserInterface $parser);
/**
*
* Sets the Profiler instance.
*
* @param ProfilerInterface $profiler The Profiler instance.
*
*/
public function setProfiler(ProfilerInterface $profiler);
/**
*
* Yields rows from the database
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return \Generator
*
*/
public function yieldAll($statement, array $values = []);
/**
*
* Yields rows from the database keyed on the first column of each row.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return \Generator
*
*/
public function yieldAssoc($statement, array $values = []);
/**
*
* Yields the first column of all rows
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return \Generator
*
*/
public function yieldCol($statement, array $values = []);
/**
*
* Yields objects where the column values are mapped to object properties.
*
* Warning: PDO "injects property-values BEFORE invoking the constructor -
* in other words, if your class initializes property-values to defaults
* in the constructor, you will be overwriting the values injected by
* fetchObject() !"
* <http://www.php.net/manual/en/pdostatement.fetchobject.php#111744>
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @param string $class The name of the class to create from each
* row.
*
* @param array $args Arguments to pass to each object constructor.
*
* @return \Generator
*
*/
public function yieldObjects(
$statement,
array $values = [],
$class = 'stdClass',
array $args = []
);
/**
*
* Yields key-value pairs (first column is the key, second column is the
* value).
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return \Generator
*
*/
public function yieldPairs($statement, array $values = []);
/**
*
* Performs a query after preparing the statement with bound values, then
* returns the result as a PDOStatement.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param array $values Values to bind to the query.
*
* @return \PDOStatement
*
*/
public function perform($statement, array $values = []);
/**
*
* Prepares an SQL statement with bound values.
*
* This method only binds values that have placeholders in the
* statement, thereby avoiding errors from PDO regarding too many bound
* values. It also binds all sequential (question-mark) placeholders.
*
* If a placeholder value is an array, the array is converted to a string
* of comma-separated quoted values; e.g., for an `IN (...)` condition.
* The quoted string is replaced directly into the statement instead of
* using `PDOStatement::bindValue()` proper.
*
* @param string $statement The SQL statement to prepare for execution.
*
* @param array $values The values to bind to the statement, if any.
*
* @return \PDOStatement
*
* @see http://php.net/manual/en/pdo.prepare.php
*
*/
public function prepareWithValues($statement, array $values = []);
}

View File

@ -0,0 +1,318 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
use Aura\Sql\Exception\MissingParameter;
/**
*
* Parsing/rebuilding functionality for all drivers.
*
* Note that this does not validate the syntax; it only replaces/rebuilds
* placeholders in the query.
*
* @package Aura.Sql
*
*/
abstract class AbstractParser implements ParserInterface
{
/**
*
* Split the query string on these regexes.
*
* @var array
*
*/
protected $split = [
// single-quoted string
"'(?:[^'\\\\]|\\\\'?)*'",
// double-quoted string
'"(?:[^"\\\\]|\\\\"?)*"',
];
/**
*
* Skip query parts matching this regex.
*
* @var string
*
*/
protected $skip = '/^(\'|\"|\:[^a-zA-Z_])/um';
/**
*
* The current numbered-placeholder in the original statement.
*
* @var int
*
*/
protected $num = 0;
/**
*
* How many times has a named placeholder been used?
*
* @var array
*
*/
protected $count = [
'__' => null,
];
/**
*
* The initial values to be bound.
*
* @var array
*
*/
protected $values = [];
/**
*
* Final placeholders and values to bind.
*
* @var array
*
*/
protected $final_values = [];
/**
*
* Rebuilds a statement with placeholders and bound values.
*
* @param string $statement The statement to rebuild.
*
* @param array $values The values to bind and/or replace into a statement.
*
* @return array An array where element 0 is the rebuilt statement and
* element 1 is the rebuilt array of values.
*
*/
public function rebuild($statement, array $values = [])
{
// match standard PDO execute() behavior of zero-indexed arrays
if (array_key_exists(0, $values)) {
array_unshift($values, null);
}
$this->values = $values;
$statement = $this->rebuildStatement($statement);
return [$statement, $this->final_values];
}
/**
*
* Given a statement, rebuilds it with array values embedded.
*
* @param string $statement The SQL statement.
*
* @return string The rebuilt statement.
*
*/
protected function rebuildStatement($statement)
{
$parts = $this->getParts($statement);
return $this->rebuildParts($parts);
}
/**
*
* Given an array of statement parts, rebuilds each part.
*
* @param array $parts The statement parts.
*
* @return string The rebuilt statement.
*
*/
protected function rebuildParts(array $parts)
{
$statement = '';
foreach ($parts as $part) {
$statement .= $this->rebuildPart($part);
}
return $statement;
}
/**
*
* Rebuilds a single statement part.
*
* @param string $part The statement part.
*
* @return string The rebuilt statement.
*
*/
protected function rebuildPart($part)
{
if (preg_match($this->skip, $part)) {
return $part;
}
// split into subparts by ":name" and "?"
$subs = preg_split(
"/(?<!:)(:[a-zA-Z_][a-zA-Z0-9_]*)|(\?)/um",
$part,
-1,
PREG_SPLIT_DELIM_CAPTURE
);
// check subparts to expand placeholders for bound arrays
return $this->prepareValuePlaceholders($subs);
}
/**
*
* Prepares the sub-parts of a query with placeholders.
*
* @param array $subs The query subparts.
*
* @return string The prepared subparts.
*
*/
protected function prepareValuePlaceholders(array $subs)
{
$str = '';
foreach ($subs as $i => $sub) {
$char = substr($sub, 0, 1);
if ($char == '?') {
$str .= $this->prepareNumberedPlaceholder();
} elseif ($char == ':') {
$str .= $this->prepareNamedPlaceholder($sub);
} else {
$str .= $sub;
}
}
return $str;
}
/**
*
* Bind or quote a numbered placeholder in a query subpart.
*
* @return string The prepared query subpart.
*
* @throws MissingParameter
*/
protected function prepareNumberedPlaceholder()
{
$this->num ++;
if (array_key_exists($this->num, $this->values) === false) {
throw new MissingParameter("Parameter {$this->num} is missing from the bound values");
}
$expanded = [];
$values = (array) $this->values[$this->num];
if (is_null($this->values[$this->num])) {
$values[] = null;
}
foreach ($values as $value) {
$count = ++ $this->count['__'];
$name = "__{$count}";
$expanded[] = ":{$name}";
$this->final_values[$name] = $value;
}
return implode(', ', $expanded);
}
/**
*
* Bind or quote a named placeholder in a query subpart.
*
* @param string $sub The query subpart.
*
* @return string The prepared query subpart.
*
*/
protected function prepareNamedPlaceholder($sub)
{
$orig = substr($sub, 1);
if (array_key_exists($orig, $this->values) === false) {
throw new MissingParameter("Parameter '{$orig}' is missing from the bound values");
}
$name = $this->getPlaceholderName($orig);
// is the corresponding data element an array?
$bind_array = is_array($this->values[$orig]);
if ($bind_array) {
// expand to multiple placeholders
return $this->expandNamedPlaceholder($name, $this->values[$orig]);
}
// not an array, retain the placeholder for later
$this->final_values[$name] = $this->values[$orig];
return ":$name";
}
/**
*
* Given an original placeholder name, return a replacement name.
*
* @param string $orig The original placeholder name.
*
* @return string
*
*/
protected function getPlaceholderName($orig)
{
if (! isset($this->count[$orig])) {
$this->count[$orig] = 0;
return $orig;
}
$count = ++ $this->count[$orig];
return "{$orig}__{$count}";
}
/**
*
* Given a named placeholder for an array, expand it for the array values,
* and bind those values to the expanded names.
*
* @param string $prefix The named placeholder.
*
* @param array $values The array values to be bound.
*
* @return string
*
*/
protected function expandNamedPlaceholder($prefix, array $values)
{
$i = 0;
$expanded = [];
foreach ($values as $value) {
$name = "{$prefix}_{$i}";
$expanded[] = ":{$name}";
$this->final_values[$name] = $value;
$i ++;
}
return implode(', ', $expanded);
}
/**
*
* Given a query string, split it into parts.
*
* @param string $statement The query string.
*
* @return array
*
*/
protected function getParts($statement)
{
$split = implode('|', $this->split);
return preg_split(
"/($split)/um",
$statement,
-1,
PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
);
}
}

View File

@ -0,0 +1,44 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
/**
*
* Parsing/rebuilding functionality for the mysql driver.
*
* @package Aura.Sql
*
*/
class MysqlParser extends AbstractParser
{
/**
*
* Split the query string on these regexes.
*
* @var array
*
*/
protected $split = [
// single-quoted string
"'(?:[^'\\\\]|\\\\'?)*'",
// double-quoted string
'"(?:[^"\\\\]|\\\\"?)*"',
// backtick-quoted string
'`(?:[^`\\\\]|\\\\`?)*`',
];
/**
*
* Skip query parts matching this regex.
*
* @var string
*
*/
protected $skip = '/^(\'|\"|\`)/um';
}

View File

@ -0,0 +1,36 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
/**
*
* A parser/rebuilder that does nothing at all; use this when your placeholders
* and bound-values are already perfectly matched.
*
* @package Aura.Sql
*
*/
class NullParser implements ParserInterface
{
/**
*
* Leaves the query and parameters alone.
*
* @param string $statement The query statement string.
*
* @param array $values Bind these values into the query.
*
* @return array
*
*/
public function rebuild($statement, array $values = [])
{
return [$statement, $values];
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
/**
*
* Interface for query parsing/rebuilding functionality.
*
* @package Aura.Sql
*
*/
interface ParserInterface
{
/**
*
* Rebuilds a query and its parameters to adapt it to PDO's limitations,
* and returns a list of queries.
*
* @param string $string The query statement string.
*
* @param array $parameters Bind these values into the query.
*
* @return array An array where element 0 is the rebuilt statement and
* element 1 is the rebuilt array of values.
*
*/
public function rebuild($string, array $parameters = []);
}

View File

@ -0,0 +1,46 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
/**
*
* Parsing/rebuilding functionality for the pgsl driver.
*
* @package Aura.Sql
*
*/
class PgsqlParser extends AbstractParser
{
/**
*
* Split the query string on these regexes.
*
* @var array
*
*/
protected $split = [
// single-quoted string
"'(?:[^'\\\\]|\\\\'?)*'",
// double-quoted string
'"(?:[^"\\\\]|\\\\"?)*"',
// double-dollar string (empty dollar-tag)
'\$\$(?:[^\$]?)*\$\$',
// dollar-tag string -- DOES NOT match tags properly
'\$[^\$]+\$.*\$[^\$]+\$',
];
/**
*
* Skip query parts matching this regex.
*
* @var string
*
*/
protected $skip = '/^(\'|\"|\$|\:[^a-zA-Z_])/um';
}

View File

@ -0,0 +1,36 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
/**
*
* Parsing/rebuilding functionality for the sqlite driver.
*
* @package Aura.Sql
*
*/
class SqliteParser extends AbstractParser
{
/**
* {@inheritDoc}
*/
protected $split = [
// single-quoted string
"'(?:[^'\\\\]|\\\\'?)*'",
// double-quoted string
'"(?:[^"\\\\]|\\\\"?)*"',
// backticked column names
'`(?:[^`\\\\]|\\\\`?)*`',
];
/**
* {@inheritDoc}
*/
protected $skip = '/^(\'|"|`|\:[^a-zA-Z_])/um';
}

View File

@ -0,0 +1,22 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Parser;
/**
*
* Parsing/rebuilding functionality for the sqlsrv driver.
*
* @package Aura.Sql
*
* @todo add $split and $skip for single quote, double quote, and square brackets
*
*/
class SqlsrvParser extends AbstractParser
{
}

View File

@ -0,0 +1,189 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql;
use PDO;
/**
*
* An interface to the native PDO object.
*
* @package Aura.Sql
*
*/
interface PdoInterface
{
/**
*
* Begins a transaction and turns off autocommit mode.
*
* @return bool True on success, false on failure.
*
* @see http://php.net/manual/en/pdo.begintransaction.php
*
*/
public function beginTransaction();
/**
*
* Commits the existing transaction and restores autocommit mode.
*
* @return bool True on success, false on failure.
*
* @see http://php.net/manual/en/pdo.commit.php
*
*/
public function commit();
/**
*
* Gets the most recent error code.
*
* @return mixed
*
*/
public function errorCode();
/**
*
* Gets the most recent error info.
*
* @return array
*
*/
public function errorInfo();
/**
*
* Executes an SQL statement and returns the number of affected rows.
*
* @param string $statement The SQL statement to execute.
*
* @return int The number of rows affected.
*
* @see http://php.net/manual/en/pdo.exec.php
*
*/
public function exec($statement);
/**
*
* Gets a PDO attribute value.
*
* @param mixed $attribute The PDO::ATTR_* constant.
*
* @return mixed The value for the attribute.
*
*/
public function getAttribute($attribute);
/**
*
* Is a transaction currently active?
*
* @return bool
*
* @see http://php.net/manual/en/pdo.intransaction.php
*
*/
public function inTransaction();
/**
*
* Returns the last inserted autoincrement sequence value.
*
* @param string $name The name of the sequence to check; typically needed
* only for PostgreSQL, where it takes the form of `<table>_<column>_seq`.
*
* @return string
*
* @see http://php.net/manual/en/pdo.lastinsertid.php
*
*/
public function lastInsertId($name = null);
/**
*
* Prepares an SQL statement for execution.
*
* @param string $statement The SQL statement to prepare for execution.
*
* @param array $options Set these attributes on the returned
* PDOStatement.
*
* @return \PDOStatement
*
* @see http://php.net/manual/en/pdo.prepare.php
*
*/
public function prepare($statement, $options = null);
/**
*
* Queries the database and returns a PDOStatement.
*
* @param string $statement The SQL statement to prepare and execute.
*
* @param mixed ...$fetch Optional fetch-related parameters.
*
* @return \PDOStatement
*
* @see http://php.net/manual/en/pdo.query.php
*
*/
public function query($statement, ...$fetch);
/**
*
* Quotes a value for use in an SQL statement.
*
* @param mixed $value The value to quote.
*
* @param int $parameter_type A data type hint for the database driver.
*
* @return string The quoted value.
*
* @see http://php.net/manual/en/pdo.quote.php
*
*/
public function quote($value, $parameter_type = PDO::PARAM_STR);
/**
*
* Rolls back the current transaction and restores autocommit mode.
*
* @return bool True on success, false on failure.
*
* @see http://php.net/manual/en/pdo.rollback.php
*
*/
public function rollBack();
/**
*
* Sets a PDO attribute value.
*
* @param mixed $attribute The PDO::ATTR_* constant.
*
* @param mixed $value The value for the attribute.
*
* @return bool
*
*/
public function setAttribute($attribute, $value);
/**
*
* Returns all currently available PDO drivers.
*
* @return array
*
*/
public static function getAvailableDrivers();
}

View File

@ -0,0 +1,64 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Profiler;
use Psr\Log\AbstractLogger;
/**
*
* A naive memory-based logger.
*
* @package Aura.Sql
*
*/
class MemoryLogger extends AbstractLogger
{
/**
*
* Log messages.
*
* @var array
*
*/
protected $messages = [];
/**
*
* Logs a message.
*
* @param mixed $level The log level (ignored).
*
* @param string $message The log message.
*
* @param array $context Data to interpolate into the message.
*
* @return null
*
*/
public function log($level, $message, array $context = [])
{
$replace = [];
foreach ($context as $key => $val) {
$replace['{' . $key . '}'] = $val;
}
$this->messages[] = strtr($message, $replace);
}
/**
*
* Returns the logged messages.
*
* @return array
*
*/
public function getMessages()
{
return $this->messages;
}
}

View File

@ -0,0 +1,229 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Profiler;
use Aura\Sql\Exception;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
*
* Sends query profiles to a logger.
*
* @package Aura.Sql
*
*/
class Profiler implements ProfilerInterface
{
/**
*
* The current profile information.
*
* @var array
*
*/
protected $context = [];
/**
*
* Log profile data through this interface.
*
* @var LoggerInterface
*
*/
protected $logger;
/**
*
* Turns profile logging off and on.
*
* @var bool
*
* @see setActive()
*
*/
protected $active = false;
/**
*
* The log level for all messages.
*
* @var string
*
* @see setLogLevel()
*
*/
protected $logLevel = LogLevel::DEBUG;
/**
*
* Sets the format for the log message, with placeholders.
*
* @var string
*
* @see setLogFormat()
*
*/
protected $logFormat = "{function} ({duration} seconds): {statement} {backtrace}";
/**
*
* Constructor.
*
* @param LoggerInterface $logger Record profiles through this interface.
*
*/
public function __construct(LoggerInterface $logger = null)
{
if ($logger === null) {
$logger = new MemoryLogger();
}
$this->logger = $logger;
}
/**
*
* Enable or disable profiler logging.
*
* @param bool $active
*
*/
public function setActive($active)
{
$this->active = (bool) $active;
}
/**
*
* Returns true if logging is active.
*
* @return bool
*
*/
public function isActive()
{
return $this->active;
}
/**
*
* Returns the underlying logger instance.
*
* @return \Psr\Log\LoggerInterface
*
*/
public function getLogger()
{
return $this->logger;
}
/**
*
* Returns the level at which to log profile messages.
*
* @return string
*
*/
public function getLogLevel()
{
return $this->logLevel;
}
/**
*
* Level at which to log profile messages.
*
* @param string $logLevel A PSR LogLevel constant.
*
* @return null
*
*/
public function setLogLevel($logLevel)
{
$this->logLevel = $logLevel;
}
/**
*
* Returns the log message format string, with placeholders.
*
* @return string
*
*/
public function getLogFormat()
{
return $this->logFormat;
}
/**
*
* Sets the log message format string, with placeholders.
*
* @param string $logFormat
*
* @return null
*
*/
public function setLogFormat($logFormat)
{
$this->logFormat = $logFormat;
}
/**
*
* Starts a profile entry.
*
* @param string $function The function starting the profile entry.
*
* @return null
*
*/
public function start($function)
{
if (! $this->active) {
return;
}
$this->context = [
'function' => $function,
'start' => microtime(true),
];
}
/**
*
* Finishes and logs a profile entry.
*
* @param string $statement The statement being profiled, if any.
*
* @param array $values The values bound to the statement, if any.
*
* @return null
*
*/
public function finish($statement = null, array $values = [])
{
if (! $this->active) {
return;
}
$finish = microtime(true);
$e = new Exception();
$this->context['finish'] = $finish;
$this->context['duration'] = $finish - $this->context['start'];
$this->context['statement'] = $statement;
$this->context['values'] = empty($values) ? '' : print_r($values, true);
$this->context['backtrace'] = $e->getTraceAsString();
$this->logger->log($this->logLevel, $this->logFormat, $this->context);
$this->context = [];
}
}

View File

@ -0,0 +1,110 @@
<?php
/**
*
* This file is part of Aura for PHP.
*
* @license https://opensource.org/licenses/MIT MIT
*
*/
namespace Aura\Sql\Profiler;
/**
*
* Interface to send query profiles to a logger.
*
* @package Aura.Sql
*
*/
interface ProfilerInterface
{
/**
*
* Enable or disable profiler logging.
*
* @param bool $active
*
*/
public function setActive($active);
/**
*
* Returns true if logging is active.
*
* @return bool
*
*/
public function isActive();
/**
*
* Returns the underlying logger instance.
*
* @return \Psr\Log\LoggerInterface
*
*/
public function getLogger();
/**
*
* Returns the level at which to log profile messages.
*
* @return string
*
*/
public function getLogLevel();
/**
*
* Level at which to log profile messages.
*
* @param string $logLevel A PSR LogLevel constant.
*
* @return null
*
*/
public function setLogLevel($logLevel);
/**
*
* Returns the log message format string, with placeholders.
*
* @return string
*
*/
public function getLogFormat();
/**
*
* Sets the log message format string, with placeholders.
*
* @param string $logFormat
*
* @return null
*
*/
public function setLogFormat($logFormat);
/**
*
* Starts a profile entry.
*
* @param string $function The function starting the profile entry.
*
* @return null
*
*/
public function start($function);
/**
*
* Finishes and logs a profile entry.
*
* @param string $statement The statement being profiled, if any.
*
* @param array $values The values bound to the statement, if any.
*
* @return null
*
*/
public function finish($statement = null, array $values = []);
}