diff options
Diffstat (limited to 'includes/vendor/aura')
20 files changed, 3510 insertions, 0 deletions
diff --git a/includes/vendor/aura/sql/README.md b/includes/vendor/aura/sql/README.md new file mode 100644 index 0000000..d9b1193 --- /dev/null +++ b/includes/vendor/aura/sql/README.md @@ -0,0 +1,536 @@ +# 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.** _ExtendedPdo_ 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. (Note that lazy connection is not + possible in this case, as the PDO instance being decorated has already + connected.) + +- **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.** The equivalent of various `fetch*()` methods, the + `yield*()` methods return an iterator instead of a complete result set. Using + the iterator to fetch one result at a time can help reduce memory usage with + very large result sets. + +- **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. + +- **Connection locator.** A optional lazy-loading service locator is provided + for picking different database connections (default, read, and write). + + +## Foreword + +### Installation + +This library requires PHP 5.3 or later; we recommend using the latest available version of PHP as a matter of principle. It has no userland dependencies. + +It is installable and autoloadable via Composer as [aura/sql](https://packagist.org/packages/aura/sql). + +Alternatively, [download a release](https://github.com/auraphp/Aura.Sql/releases) or clone this repository, then require or include its _autoload.php_ file. + +### Quality + +[](https://scrutinizer-ci.com/g/auraphp/Aura.Sql/) +[](https://scrutinizer-ci.com/g/auraphp/Aura.Sql/) +[](https://travis-ci.org/auraphp/Aura.Sql) + +To run the [PHPUnit](http://phpunit.de/manual/) unit tests at the command line, issue `composer install` and then `vendor/bin/phpunit` at the package root. (This requires [Composer](http://getcomposer.org/) to be available as `composer`.) + + +This library attempts to comply with [PSR-1][], [PSR-2][], and [PSR-4][]. If +you notice compliance oversights, please send a patch via pull request. + +[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 + +### Community + +To ask questions, provide feedback, or otherwise communicate with the Aura community, please join our [Google Group](http://groups.google.com/group/auraphp), follow [@auraphp on Twitter](http://twitter.com/auraphp), or chat with us on #auraphp on Freenode. + +## Getting Started + +### Instantiation + +You can instantiate _ExtendedPdo_ so that it uses lazy connection, or you can use it to decorate an existing _PDO_ instance. + +#### Lazy Connection Instance + +Instantiation is the same as with the native _PDO_ class: pass a data source +name, username, password, and driver options. There is one additional +parameter that allows you to pass attributes to be set after the connection is +made. + +```php +use Aura\Sql\ExtendedPdo; + +$pdo = new ExtendedPdo( + 'mysql:host=localhost;dbname=test', + 'username', + 'password', + array(), // driver options as key-value pairs + array() // attributes as key-value pairs +); +``` + +> N.b.: The `sqlsrv` extension will fail to connect when using error mode `PDO::ERRMODE_EXCEPTION`. To connect, you will need to explicitly pass `array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING)` (or `PDO::ERRMODE_SILENT`) when using `sqlsrv`. + +Whereas the native _PDO_ connects on instantiation, _ExtendedPdo_ does not connect immediately. Instead, it connects only when you call a method that actually needs the connection to the database; e.g., on `query()`. + +If you want to force a connection, call the `connect()` method. + +```php +// does not connect to the database +$pdo = new ExtendedPdo( + 'mysql:host=localhost;dbname=test', + 'username', + 'password' +); + +// automatically connects +$pdo->exec('SELECT * FROM test'); + +// explicitly forces a connection +$pdo->connect(); +``` + +If you want to explicitly force a disconnect, call the `disconnect()` method. + +```php +// explicitly forces disconnection +$pdo->disconnect(); +``` + +Doing so will close the connection by unsetting the internal _PDO_ instance. However, calling an _ExtendedPdo_ method that implicitly establishes a connection, such as `query()` or one of the `fetch*()` methods, will automatically re-connect to the database. + +#### Decorator Instance + +The _ExtendedPdo_ class can be used to decorate an existing PDO connection as well. To do so, instantiate _ExtendedPdo_ by passing an existing PDO connection: + +```php +use Aura\Sql\ExtendedPdo; + +$pdo = new PDO(...); +$extended_pdo = new ExtendedPdo($pdo); +``` + +The decorated _PDO_ instance now provides all the _ExtendedPdo_ functionality (aside from lazy connection, which is not possible since the _PDO_ instance by definition has already connected). + +Decoration of this kind can be useful when you have access to an existing _PDO_ connection managed elsewhere in your application. + +> N.b.: The `disconnect()` method **will not work** on decorated _PDO_ connections, since _ExtendedPdo_ did not create the connection itself. You will need to manage the decorated _PDO_ instance yourself in that case. + +### Array Quoting + +The native _PDO_ `quote()` method will not quote arrays. This makes it +difficult to bind an array to something like an `IN (...)` condition in SQL. +However, _ExtendedPdo_ recognizes arrays and converts them into comma- +separated quoted strings. + +```php +// the array to be quoted +$array = array('foo', 'bar', 'baz'); + +// the native PDO way: +// "Warning: PDO::quote() expects parameter 1 to be string, array given" +$pdo = new PDO(...); +$cond = 'IN (' . $pdo->quote($array) . ')'; + +// the ExtendedPdo way: +// "IN ('foo', 'bar', 'baz')" +$pdo = new ExtendedPdo(...); +$cond = 'IN (' . $pdo->quote($array) . ')'; +``` + +### The `perform()` Method + +The new `perform()` method will prepare a query with bound values in a single +step. Also, because the native _PDO_ does not deal with bound array values, +`perform()` modifies the query string to replace array-bound placeholders with +the quoted array. Note that this is *not* the same thing as binding: +the query string itself is modified before passing to the database for value +binding. + +```php +// the array to be quoted +$array = array('foo', 'bar', 'baz'); + +// the statement to prepare +$stm = 'SELECT * FROM test WHERE foo IN (:foo)' + +// the native PDO way does not work (PHP Notice: Array to string conversion) +$pdo = new ExtendedPdo(...); +$sth = $pdo->prepare($stm); +$sth->bindValue('foo', $array); + +// the ExtendedPdo way allows a single call to prepare and execute the query. +// it quotes the array and replaces the array placeholder directly in the +// query string +$pdo = new ExtendedPdo(...); +$bind_values = array('foo' => $array); +$sth = $pdo->perform($stm, $bind_values); +echo $sth->queryString; +// the query string has been modified by ExtendedPdo to become +// "SELECT * FROM test WHERE foo IN ('foo', 'bar', 'baz')" +``` + +Finally, note that array quoting works only via the `perform()` method, +not on returned _PDOStatement_ instances. + + +### New `fetch*()` Methods + +_ExtendedPdo_ comes with `fetch*()` methods to help reduce boilerplate code. +Instead of issuing `prepare()`, a series of `bindValue()` calls, `execute()`, +and then `fetch*()` on a _PDOStatement_, you can bind values and fetch results +in one call on _ExtendedPdo_ directly. (The `fetch*()` methods use `perform()` +internally, so quoting-and-replacement of array placeholders is supported.) + +```php +$stm = 'SELECT * FROM test WHERE foo = :foo AND bar = :bar'; +$bind = array('foo' => 'baz', 'bar' => 'dib'); + +// the native PDO way to "fetch all" where the result is a sequential array +// of rows, and the row arrays are keyed on the column names +$pdo = new PDO(...); +$sth = $pdo->prepare($stm); +$sth->execute($bind); +$result = $sth->fetchAll(PDO::FETCH_ASSOC); + +// the ExtendedPdo way to do the same kind of "fetch all" +$pdo = new ExtendedPdo(...); +$result = $pdo->fetchAll($stm, $bind); + +// fetchAssoc() returns an associative array of all rows where the key is the +// first column, and the row arrays are keyed on the column names +$result = $pdo->fetchAssoc($stm, $bind); + +// fetchGroup() is like fetchAssoc() except that the values aren't wrapped in +// arrays. Instead, single column values are returned as a single dimensional +// array and multiple columns are returned as an array of arrays +// Set style to PDO::FETCH_NAMED when values are an array +// (i.e. there are more than two columns in the select) +$result = $pdo->fetchGroup($stm, $bind, $style = PDO::FETCH_COLUMN) + +// fetchObject() returns the first row as an object of your choosing; the +// columns are mapped to object properties. an optional 4th parameter array +// provides constructor arguments when instantiating the object. +$result = $pdo->fetchObject($stm, $bind, 'ClassName', array('ctor_arg_1')); + +// fetchObjects() returns an array of objects of your choosing; the +// columns are mapped to object properties. an optional 4th parameter array +// provides constructor arguments when instantiating the object. +$result = $pdo->fetchObjects($stm, $bind, 'ClassName', array('ctor_arg_1')); + +// fetchOne() returns the first row as an associative array where the keys +// are the column names +$result = $pdo->fetchOne($stm, $bind); + +// fetchPairs() returns an associative array where each key is the first +// column and each value is the second column +$result = $pdo->fetchPairs($stm, $bind); + +// fetchValue() returns the value of the first row in the first column +$result = $pdo->fetchValue($stm, $bind); + +// fetchAffected() returns the number of affected rows +$stm = "UPDATE test SET incr = incr + 1 WHERE foo = :foo AND bar = :bar"; +$row_count = $pdo->fetchAffected($stm, $bind); +``` + +The methods `fetchAll()`, `fetchAssoc()`, `fetchCol()`, and `fetchPairs()` +take an optional third parameter, a callable, to apply to each row of the +results before returning. + +```php +$result = $pdo->fetchAssoc($stm, $bind, function (&$row) { + // add a column to the row + $row['my_new_col'] = 'Added this column from the callable.'; +}); +``` + +### New `yield*()` Methods + +_ExtendedPdo_ comes with `yield*()` methods to help reduce memory usage. Whereas +many `fetch*()` methods collect all the query result rows before returning them +all at once, the equivalent `yield*()` methods return an iterator that generates +one result row at a time. For example: + +```php +$stm = 'SELECT * FROM test WHERE foo = :foo AND bar = :bar'; +$bind = array('foo' => 'baz', 'bar' => 'dib'); + +// like fetchAll(), each row is an associative array +foreach ($pdo->yieldAll($stm, $bind) as $row) { + // ... +} + +// like fetchAssoc(), each key is the first column, +// and the row is an associative array +foreach ($pdo->yieldAssoc($stm, $bind) as $key => $row) { + // ... +} + +// like fetchCol(), each result is a value from the first column +foreach ($pdo->yieldCol($stm, $bind) as $val) { + // ... +} + +// like fetchObjects(), each result is an object; pass an optional +// class name and optional array of constructor arguments. +$class = 'ClassName'; +$args = ['arg0', 'arg1', 'arg2']; +foreach ($pdo->yieldObjects($stm, $bind, $class, $args) as $object) { + // ... +} + +// like fetchPairs(), each result is a key-value pair from the +// first and second columns +foreach ($pdo->yieldPairs($stm, $bind) as $key => $val) { + // ... +} +``` + +## Profiler + +When debugging, it is often useful to see what queries have been executed, +where they were issued from in the codebase, and how long they took to +complete. _ExtendedPdo_ comes with an optional profiler that you can use to +discover that information. + +```php +use Aura\Sql\ExtendedPdo; +use Aura\Sql\Profiler; + +$pdo = new ExtendedPdo(...); +$pdo->setProfiler(new Profiler); + +// ... +// query(), fetch(), beginTransaction()/commit()/rollback() etc. +// ... + +// now retrieve the profile information: +$profiles = $pdo->getProfiler()->getProfiles(); +``` + +Each profile entry will have these keys: + +- `duration`: How long the query took to complete, in seconds. + +- `function`: The method that was called on _ExtendedPdo_ that created the + profile entry. + +- `statement`: The query string that was issued, if any. (Methods like + `connect()` and `rollBack()` do not send query strings.) + +- `bind_values`: Any values that were bound to the query. + +- `trace`: An exception stack trace indicating where the query was issued from + in the codebase. + +Note that an entry is made into the profile for each call to underlying _ExtendedPDO_ methods. For example, in a simple query using a bind value, there will be two entries, one for the call to `prepare` and one for the call to `perform`. + +Setting the _Profiler_ into the _ExtendedPdo_ instance is optional. Once it +is set, you can activate and deactivate it as you wish using the +`Profiler::setActive()` method. When not active, query profiles will not be +retained. + +```php +$pdo = new ExtendedPdo(...); +$pdo->setProfiler(new Profiler); + +// deactivate, issue a query, and reactivate; +// the query will not show up in the profiles +$pdo->getProfiler()->setActive(false); +$pdo->fetchAll('SELECT * FROM foo'); +$pdo->getProfiler()->setActive(true); +``` + +## Connection Locator + +Frequently, high-traffic PHP applications use multiple database servers, +generally one for writes, and one or more for reads. The _ConnectionLocator_ +allows you to define multiple _ExtendedPdo_ objects for lazy-loaded read and +write connections. It will create the connections only when they are when +called. The creation logic is wrapped in a callable. + +First, create the _ConnectionLocator_: + +```php +use Aura\Sql\ExtendedPdo; +use Aura\Sql\ConnectionLocator; + +$connectionLocator = new ConnectionLocator; +``` + +Now add a default connection; this will be used when a read or write +connection is not defined. (This is also useful for setting up connection +location in advance of actually having multiple database servers.) + +```php +$connectionLocator->setDefault(function () { + return new ExtendedPdo( + 'mysql:host=default.db.localhost;dbname=database', + 'username', + 'password' + ); +}); +``` + +Next, add as many named read and write connections as you like: + +```php +// the write (master) server +$connectionLocator->setWrite('master', function () { + return new ExtendedPdo( + 'mysql:host=master.db.localhost;dbname=database', + 'username', + 'password' + ); +}); + +// read (slave) #1 +$connectionLocator->setRead('slave1', function () { + return new ExtendedPdo( + 'mysql:host=slave1.db.localhost;dbname=database', + 'username', + 'password' + ); +}); + +// read (slave) #2 +$connectionLocator->setRead('slave2', function () { + return new ExtendedPdo( + 'mysql:host=slave2.db.localhost;dbname=database', + 'username', + 'password' + ); +}); + +// read (slave) #3 +$connectionLocator->setRead('slave3', function () { + return new ExtendedPdo( + 'mysql:host=slave3.db.localhost;dbname=database', + 'username', + 'password' + ); +}); +``` + +Finally, retrieve a connection from the locator when you need it. This will +create the connection (if needed) and then return it. + +- `getDefault()` will return the default connection. + +- `getRead()` will return a named read connection; if no name is specified, it + will return a random read connection. If no read connections are defined, it + will return the default connection. + +- `getWrite()` will return a named write connection; if no name is specified, + it will return a random write connection. If no write connections are + defined, it will return the default connection. + +```php +$read = $connectionLocator->getRead(); +$results = $read->fetchAll('SELECT * FROM table_name LIMIT 10'); +``` + +### Construction-Time Configuration + +The _ConnectionLocator_ can be configured with all its connections at +construction time; this is useful with dependency injection mechanisms. + +```php +use Aura\Sql\ConnectionLocator; +use Aura\Sql\ExtendedPdo; + +// default connection +$default = function () { + return new ExtendedPdo( + 'mysql:host=default.db.localhost;dbname=database', + 'username', + 'password' + ); +}; + +// read connections +$read = array( + 'slave1' => function () { + return new ExtendedPdo( + 'mysql:host=slave1.db.localhost;dbname=database', + 'username', + 'password' + ); + }, + 'slave2' => function () { + return new ExtendedPdo( + 'mysql:host=slave2.db.localhost;dbname=database', + 'username', + 'password' + ); + }, + 'slave3' => function () { + return new ExtendedPdo( + 'mysql:host=slave3.db.localhost;dbname=database', + 'username', + 'password' + ); + }, +); + +// write connection +$write = array( + 'master' => function () { + return new ExtendedPdo( + 'mysql:host=master.db.localhost;dbname=database', + 'username', + 'password' + ); + }, +); + +// configure locator at construction time +$connectionLocator = new ConnectionLocator($default, $read, $write); +``` + +### Profiler + +You can turn profiling on and off for all connections in the locator using the +`setProfiling()` method. (If no profiler has been set on a connection, the +locator will set a default profiler into it automatically.) To get all the +profiled queries using the `getProfiles()` method. + +```php +$connectionLocator->setProfiling(true); +// perform queries, then: +$profiles = $connectionLocator->getProfiles(); +``` + diff --git a/includes/vendor/aura/sql/src/ConnectionLocator.php b/includes/vendor/aura/sql/src/ConnectionLocator.php new file mode 100644 index 0000000..5afaf51 --- /dev/null +++ b/includes/vendor/aura/sql/src/ConnectionLocator.php @@ -0,0 +1,315 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +/** + * + * Manages PDO connection objects for default, read, and write connections. + * + * @package Aura.Sql + * + */ +class ConnectionLocator implements ConnectionLocatorInterface +{ + /** + * + * A registry of PDO connection entries. + * + * @var array + * + */ + protected $registry = array( + 'default' => null, + 'read' => array(), + 'write' => array(), + ); + + /** + * + * Whether or not registry entries have been converted to objects. + * + * @var array + * + */ + protected $converted = array( + 'default' => false, + 'read' => array(), + 'write' => array(), + ); + + /** + * + * Whether or not to turn on profiling when retrieving a connection. + * + * @var bool + * + */ + protected $profiling = false; + + /** + * + * 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(), + array $write = array() + ) { + 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 registry entry. + * + * @param callable $callable The registry entry. + * + * @return null + * + */ + public function setDefault($callable) + { + $this->registry['default'] = $callable; + $this->converted['default'] = false; + } + + /** + * + * Returns the default connection object. + * + * @return ExtendedPdoInterface + * + */ + public function getDefault() + { + if (! $this->converted['default']) { + $callable = $this->registry['default']; + $this->registry['default'] = call_user_func($callable); + $this->converted['default'] = true; + } + + $connection = $this->registry['default']; + $this->setProfiler($connection); + return $connection; + } + + /** + * + * 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) + { + $this->registry['read'][$name] = $callable; + $this->converted['read'][$name] = false; + } + + /** + * + * 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 = null) + { + return $this->getConnection('read', $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) + { + $this->registry['write'][$name] = $callable; + $this->converted['write'][$name] = false; + } + + /** + * + * 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 = null) + { + 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) + { + if (! $this->registry[$type]) { + return $this->getDefault(); + } + + if (! $name) { + $name = array_rand($this->registry[$type]); + } + + if (! isset($this->registry[$type][$name])) { + throw new Exception\ConnectionNotFound("{$type}:{$name}"); + } + + if (! $this->converted[$type][$name]) { + $callable = $this->registry[$type][$name]; + $this->registry[$type][$name] = call_user_func($callable); + $this->converted[$type][$name] = true; + } + + $connection = $this->registry[$type][$name]; + $this->setProfiler($connection); + return $connection; + } + + /** + * + * Given a connection, enable or disable profiling on it. If a profiler has + * not been set into the connection, this will instantiate and set one. + * + * @param ExtendedPdo $connection The connection. + * + * @return null + * + */ + protected function setProfiler(ExtendedPdo $connection) + { + $profiler = $connection->getProfiler(); + + if (! $this->profiling && ! $profiler) { + return; + } + + if (! $profiler) { + $profiler = new Profiler(); + $connection->setProfiler($profiler); + } + + $profiler->setActive($this->profiling); + } + + /** + * + * Set profiling on all connections at retrieval time? + * + * @param bool $profiling True to enable, or false to disable, profiling on + * each connection as it is retrieved. + * + * @return null + * + */ + public function setProfiling($profiling = true) + { + $this->profiling = (bool) $profiling; + } + + /** + * + * Gets the profiles from all connections. + * + * @return array + * + */ + public function getProfiles() + { + $profiles = array(); + + if ($this->converted['default']) { + $connection = $this->registry['default']; + $this->addProfiles('default', $connection, $profiles); + } + + foreach (array('read', 'write') as $type) { + foreach ($this->registry[$type] as $name) { + if ($this->converted[$type][$name]) { + $connection = $this->registry[$type][$name]; + $this->addProfiles("{$type}:{$name}", $connection, $profiles); + } + } + } + + ksort($profiles); + return $profiles; + } + + /** + * + * Adds profiles from a connection, with a label for the connection name. + * + * @param string $label The connection label. + * + * @param ExtendedPdo $connection The connection. + * + * @param array &$profiles Add the connection profiles to this array, in + * place. + * + * @return null + */ + protected function addProfiles($label, ExtendedPdo $connection, &$profiles) + { + $profiler = $connection->getProfiler(); + if (! $profiler) { + return; + } + + foreach ($profiler->getProfiles() as $key => $profile) { + $profile = array('connection' => $label) + $profile; + $profiles[$key] = $profile; + } + } +} diff --git a/includes/vendor/aura/sql/src/ConnectionLocatorInterface.php b/includes/vendor/aura/sql/src/ConnectionLocatorInterface.php new file mode 100644 index 0000000..558a953 --- /dev/null +++ b/includes/vendor/aura/sql/src/ConnectionLocatorInterface.php @@ -0,0 +1,91 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +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); + + /** + * + * 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); + + /** + * + * 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 = null); + + /** + * + * 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); + + /** + * + * 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 = null); +} diff --git a/includes/vendor/aura/sql/src/Exception.php b/includes/vendor/aura/sql/src/Exception.php new file mode 100644 index 0000000..4430df2 --- /dev/null +++ b/includes/vendor/aura/sql/src/Exception.php @@ -0,0 +1,20 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +/** + * + * Base Exception class for Aura Sql + * + * @package Aura.Sql + * + */ +class Exception extends \Exception +{ +} diff --git a/includes/vendor/aura/sql/src/Exception/CannotBindValue.php b/includes/vendor/aura/sql/src/Exception/CannotBindValue.php new file mode 100644 index 0000000..336ddd2 --- /dev/null +++ b/includes/vendor/aura/sql/src/Exception/CannotBindValue.php @@ -0,0 +1,23 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +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 +{ +} diff --git a/includes/vendor/aura/sql/src/Exception/CannotDisconnect.php b/includes/vendor/aura/sql/src/Exception/CannotDisconnect.php new file mode 100644 index 0000000..3f1cf2d --- /dev/null +++ b/includes/vendor/aura/sql/src/Exception/CannotDisconnect.php @@ -0,0 +1,23 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +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 +{ +} diff --git a/includes/vendor/aura/sql/src/Exception/ConnectionNotFound.php b/includes/vendor/aura/sql/src/Exception/ConnectionNotFound.php new file mode 100644 index 0000000..b6a15a4 --- /dev/null +++ b/includes/vendor/aura/sql/src/Exception/ConnectionNotFound.php @@ -0,0 +1,22 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Exception; + +use Aura\Sql\Exception; + +/** + * + * Locator could not find a named connection. + * + * @package Aura.Sql + * + */ +class ConnectionNotFound extends Exception +{ +} diff --git a/includes/vendor/aura/sql/src/Exception/MissingParameter.php b/includes/vendor/aura/sql/src/Exception/MissingParameter.php new file mode 100644 index 0000000..2a16c9a --- /dev/null +++ b/includes/vendor/aura/sql/src/Exception/MissingParameter.php @@ -0,0 +1,22 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +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 +{ +} diff --git a/includes/vendor/aura/sql/src/ExtendedPdo.php b/includes/vendor/aura/sql/src/ExtendedPdo.php new file mode 100644 index 0000000..d3668ac --- /dev/null +++ b/includes/vendor/aura/sql/src/ExtendedPdo.php @@ -0,0 +1,1138 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +use Aura\Sql\Exception; +use PDO; +use PDOStatement; + +/** + * + * This extended decorator for PDO provides lazy connection, array quoting, a + * new `perform()` method, and new `fetch*()` methods. + * + * @package Aura.Sql + * + */ +class ExtendedPdo extends PDO implements ExtendedPdoInterface +{ + /** + * + * The PDO connection itself. + * + * @var PDO + * + */ + protected $pdo; + + /** + * + * The attributes for a lazy connection. + * + * @var array + * + */ + protected $attributes = array( + self::ATTR_ERRMODE => self::ERRMODE_EXCEPTION, + ); + + /** + * + * Was the PDO connection injected at construction time? + * + * @var PDO + * + */ + protected $injected = false; + + /** + * + * The DSN for a lazy connection. + * + * @var string + * + */ + protected $dsn; + + /** + * + * PDO options for a lazy connection. + * + * @var array + * + */ + protected $options = array(); + + /** + * + * The password for a lazy connection. + * + * @var string + * + */ + protected $password; + + /** + * + * The current profile information. + * + * @var array + * + */ + protected $profile = array(); + + /** + * + * A query profiler. + * + * @var ProfilerInterface + * + */ + protected $profiler; + + /** + * + * The username for a lazy connection. + * + * @var string + * + */ + protected $username; + + /** + * + * A specialized statement preparer. + * + * @var Rebuilder + * + */ + protected $rebuilder; + + /** + * + * This constructor is pseudo-polymorphic. You may pass a normal set of PDO + * constructor parameters, and ExtendedPdo will use them for a lazy + * connection. Alternatively, if the `$dsn` parameter is an existing PDO + * instance, that instance will be decorated by ExtendedPdo; the remaining + * parameters will be ignored. + * + * @param PDO|string $dsn The data source name for a lazy PDO connection, + * or an existing instance of PDO. If the latter, the remaining params are + * ignored. + * + * @param string $username The username for a lazy connection. + * + * @param string $password The password for a lazy connection. + * + * @param array $options Driver-specific options for a lazy connection. + * + * @param array $attributes Attributes to set after a lazy connection. + * + * @see http://php.net/manual/en/pdo.construct.php + * + */ + public function __construct( + $dsn, + $username = null, + $password = null, + array $options = array(), + array $attributes = array() + ) { + if ($dsn instanceof PDO) { + $this->pdo = $dsn; + $this->injected = true; + } else { + $this->dsn = $dsn; + $this->username = $username; + $this->password = $password; + $this->options = $options; + $this->attributes = array_replace($this->attributes, $attributes); + } + } + + /** + * + * 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() + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $result = $this->pdo->beginTransaction(); + $this->endProfile(); + return $result; + } + + /** + * + * 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() + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $result = $this->pdo->commit(); + $this->endProfile(); + return $result; + } + + /** + * + * Connects to the database and sets PDO attributes. + * + * @return null + * + * @throws \PDOException if the connection fails. + * + */ + public function connect() + { + // don't connect twice + if ($this->pdo) { + return; + } + + // connect to the database + $this->beginProfile(__FUNCTION__); + $this->pdo = new PDO( + $this->dsn, + $this->username, + $this->password, + $this->options + ); + $this->endProfile(); + + // set attributes + foreach ($this->attributes as $attribute => $value) { + $this->setAttribute($attribute, $value); + } + } + + /** + * + * Explicitly disconnect by unsetting the PDO instance; does not prevent + * later reconnection, whether implicit or explicit. + * + * @return null + * + * @throws Exception\CannotDisconnect when the PDO instance was injected + * for decoration; manage the lifecycle of that PDO instance elsewhere. + * + */ + public function disconnect() + { + if ($this->injected) { + $message = "Cannot disconnect an injected PDO instance."; + throw new Exception\CannotDisconnect($message); + } + $this->pdo = null; + } + + /** + * + * Gets the most recent error code. + * + * @return mixed + * + */ + public function errorCode() + { + $this->connect(); + return $this->pdo->errorCode(); + } + + /** + * + * Gets the most recent error info. + * + * @return array + * + */ + public function errorInfo() + { + $this->connect(); + return $this->pdo->errorInfo(); + } + + /** + * + * Executes an SQL statement and returns the number of affected rows. + * + * @param string $statement The SQL statement to prepare and execute. + * + * @return int The number of affected rows. + * + * @see http://php.net/manual/en/pdo.exec.php + * + */ + public function exec($statement) + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $affected_rows = $this->pdo->exec($statement); + $this->endProfile($statement); + return $affected_rows; + } + + /** + * + * 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 = array()) + { + $sth = $this->perform($statement, $values); + return $sth->rowCount(); + } + + /** + * + * Fetches a sequential array of rows from the database; the rows + * are returned as associative arrays. + * + * @param string $statement The SQL statement to prepare and execute. + * + * @param array $values Values to bind to the query. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchAll( + $statement, + array $values = array(), + $callable = null + ) { + return $this->fetchAllWithCallable( + self::FETCH_ASSOC, + $statement, + $values, + $callable + ); + } + + /** + * + * Support for fetchAll() and fetchCol(). + * + * @param string $fetch_type A PDO FETCH_* constant. + * + * @param string $statement The SQL statement to prepare and execute. + * + * @param array $values Values to bind to the query. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + protected function fetchAllWithCallable( + $fetch_type, + $statement, + array $values = array(), + $callable = null + ) { + $sth = $this->perform($statement, $values); + if ($fetch_type == self::FETCH_COLUMN) { + $data = $sth->fetchAll($fetch_type, 0); + } else { + $data = $sth->fetchAll($fetch_type); + } + return $this->applyCallableToFetchAll($callable, $data); + } + + /** + * + * Applies a callable to a data set. + * + * @param callable|null $callable The callable to apply, if any. + * + * @param array $data The data set. + * + * @return array + * + */ + protected function applyCallableToFetchAll($callable, $data) + { + if ($callable) { + foreach ($data as $key => $row) { + $data[$key] = call_user_func($callable, $row); + } + } + return $data; + } + + /** + * + * Fetches an associative array of rows from the database; the rows + * are returned as associative arrays, and 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchAssoc( + $statement, + array $values = array(), + $callable = null + ) { + $sth = $this->perform($statement, $values); + + if (! $callable) { + $callable = function ($row) { return $row; }; + } + + $data = array(); + while ($row = $sth->fetch(self::FETCH_ASSOC)) { + $key = current($row); + $data[$key] = call_user_func($callable, $row); + } + + return $data; + } + + /** + * + * 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchCol( + $statement, + array $values = array(), + $callable = null + ) { + return $this->fetchAllWithCallable( + self::FETCH_COLUMN, + $statement, + $values, + $callable + ); + } + + /** + * + * Fetches one row from the database as an object 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_name The name of the class to create. + * + * @param array $ctor_args Arguments to pass to the object constructor. + * + * @return object|false + * + */ + public function fetchObject( + $statement, + array $values = array(), + $class_name = 'StdClass', + array $ctor_args = array() + ) { + $sth = $this->perform($statement, $values); + + if ($ctor_args) { + return $sth->fetchObject($class_name, $ctor_args); + } + + return $sth->fetchObject($class_name); + } + + /** + * + * Fetches a sequential array of rows from the database; the rows + * are returned 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_name The name of the class to create from each + * row. + * + * @param array $ctor_args Arguments to pass to each object constructor. + * + * @return array + * + */ + public function fetchObjects( + $statement, + array $values = array(), + $class_name = 'StdClass', + array $ctor_args = array() + ) { + $sth = $this->perform($statement, $values); + + if ($ctor_args) { + return $sth->fetchAll(self::FETCH_CLASS, $class_name, $ctor_args); + } + + return $sth->fetchAll(self::FETCH_CLASS, $class_name); + } + + /** + * + * 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|false + * + */ + public function fetchOne($statement, array $values = array()) + { + $sth = $this->perform($statement, $values); + return $sth->fetch(self::FETCH_ASSOC); + } + + /** + * + * 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchPairs( + $statement, + array $values = array(), + $callable = null + ) { + $sth = $this->perform($statement, $values); + if ($callable) { + $data = array(); + while ($row = $sth->fetch(self::FETCH_NUM)) { + // apply the callback first so the key can be modified + $row = call_user_func($callable, $row); + // now retain the data + $data[$row[0]] = $row[1]; + } + } else { + $data = $sth->fetchAll(self::FETCH_KEY_PAIR); + } + return $data; + } + + /** + * + * 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 = array()) + { + $sth = $this->perform($statement, $values); + return $sth->fetchColumn(0); + } + + /** + * + * 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 = array(), + $style = self::FETCH_COLUMN + ) { + $sth = $this->perform($statement, $values); + return $sth->fetchAll(self::FETCH_GROUP | $style); + } + + /** + * + * Gets a PDO attribute value. + * + * @param mixed $attribute The PDO::ATTR_* constant. + * + * @return mixed The value for the attribute. + * + */ + public function getAttribute($attribute) + { + $this->connect(); + return $this->pdo->getAttribute($attribute); + } + + /** + * + * Returns the DSN for a lazy connection; if the underlying PDO instance + * was injected at construction time, this will be null. + * + * @return string|null + * + */ + public function getDsn() + { + return $this->dsn; + } + + /** + * + * Returns the underlying PDO connection object. + * + * @return PDO + * + */ + public function getPdo() + { + $this->connect(); + return $this->pdo; + } + + /** + * + * Returns the profiler object. + * + * @return ProfilerInterface + * + */ + public function getProfiler() + { + return $this->profiler; + } + + /** + * + * Is a transaction currently active? + * + * @return bool + * + * @see http://php.net/manual/en/pdo.intransaction.php + * + */ + public function inTransaction() + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $result = $this->pdo->inTransaction(); + $this->endProfile(); + return $result; + } + + /** + * + * Is this instance connected to a database? + * + * @return bool + * + */ + public function isConnected() + { + return isset($this->pdo); + } + + /** + * + * 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 int + * + * @see http://php.net/manual/en/pdo.lastinsertid.php + * + */ + public function lastInsertId($name = null) + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $result = $this->pdo->lastInsertId($name); + $this->endProfile(); + return $result; + } + + /** + * + * Performs a query with bound values and returns the resulting + * PDOStatement; array values will be passed through `quote()` and their + * respective placeholders will be replaced in the query string. + * + * @param string $statement The SQL statement to perform. + * + * @param array $values Values to bind to the query + * + * @return PDOStatement + * + * @see quote() + * + */ + public function perform($statement, array $values = array()) + { + $sth = $this->prepareWithValues($statement, $values); + $this->beginProfile(__FUNCTION__); + $sth->execute(); + $this->endProfile($statement, $values); + return $sth; + } + + /** + * + * 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 = array()) + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $sth = $this->pdo->prepare($statement, $options); + $this->endProfile($statement, $options); + return $sth; + } + + /** + * + * Queries the database and returns a PDOStatement. + * + * @param string $statement The SQL statement to prepare and execute. + * + * @param int $fetch_mode The `PDO::FETCH_*` type to set on the returned + * `PDOStatement::setFetchMode()`. + * + * @param mixed $fetch_arg1 The first additional argument to send to + * `PDOStatement::setFetchMode()`. + * + * @param mixed $fetch_arg2 The second additional argument to send to + * `PDOStatement::setFetchMode()`. + * + * @return PDOStatement + * + * @see http://php.net/manual/en/pdo.query.php + * + */ + public function query($statement) + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + + // remove empty constructor params list if it exists + $args = func_get_args(); + if (count($args) === 4 && $args[3] === array()) { + unset($args[3]); + } + + $sth = call_user_func_array(array($this->pdo, 'query'), $args); + + $this->endProfile($sth->queryString); + return $sth; + } + + /** + * + * Quotes a value for use in an SQL statement. + * + * This differs from `PDO::quote()` in that it will convert an array into + * a string of comma-separated quoted values. + * + * @param mixed $value The value to quote. + * + * @param int $parameter_type A data type hint for the database driver. + * + * @return mixed The quoted value. + * + * @see http://php.net/manual/en/pdo.quote.php + * + */ + public function quote($value, $parameter_type = self::PARAM_STR) + { + $this->connect(); + + // non-array quoting + if (! is_array($value)) { + return $this->pdo->quote($value, $parameter_type); + } + + // quote array values, not keys, then combine with commas + foreach ($value as $k => $v) { + $value[$k] = $this->pdo->quote($v, $parameter_type); + } + return implode(', ', $value); + } + + /** + * + * 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() + { + $this->connect(); + $this->beginProfile(__FUNCTION__); + $result = $this->pdo->rollBack(); + $this->endProfile(); + + return $result; + } + + /** + * + * Sets a PDO attribute value. + * + * @param mixed $attribute The PDO::ATTR_* constant. + * + * @param mixed $value The value for the attribute. + * + * @return bool True on success, false on failure. Note that if PDO has not + * not connected, all calls will be treated as successful. + * + */ + public function setAttribute($attribute, $value) + { + if ($this->pdo) { + return $this->pdo->setAttribute($attribute, $value); + } + + $this->attributes[$attribute] = $value; + return true; + } + + /** + * + * Sets the profiler object. + * + * @param ProfilerInterface $profiler + * + * @return null + * + */ + public function setProfiler(ProfilerInterface $profiler) + { + $this->profiler = $profiler; + } + + /** + * + * Begins a profile entry. + * + * @param string $function The function starting the profile entry. + * + * @return null + * + */ + protected function beginProfile($function) + { + // if there's no profiler, can't profile + if (! $this->profiler) { + return; + } + + // retain starting profile info + $this->profile['time'] = microtime(true); + $this->profile['function'] = $function; + } + + /** + * + * Ends and records 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 + * + */ + protected function endProfile($statement = null, array $values = array()) + { + // is there a profiler in place? + if ($this->profiler) { + // add an entry to the profiler + $this->profiler->addProfile( + microtime(true) - $this->profile['time'], + $this->profile['function'], + $statement, + $values + ); + } + + // clear the starting profile info + $this->profile = array(); + } + + /** + * + * 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 = array()) + { + // if there are no values to bind ... + if (! $values) { + // ... use the normal preparation + return $this->prepare($statement); + } + + // rebuild the statement and values + $rebuilder = new Rebuilder($this); + list($statement, $values) = $rebuilder->__invoke($statement, $values); + + // prepare the statement + $sth = $this->prepare($statement); + + // for the placeholders we found, bind the corresponding data values + foreach ($values as $key => $val) { + $this->bindValue($sth, $key, $val); + } + + // done + return $sth; + } + + /** + * + * 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 Iterator\AllIterator + * + */ + public function yieldAll($statement, array $values = array()) + { + $sth = $this->perform($statement, $values); + return new Iterator\AllIterator($sth); + } + + /** + * + * 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 Iterator\AssocIterator + * + */ + public function yieldAssoc($statement, array $values = array()) + { + $sth = $this->perform($statement, $values); + return new Iterator\AssocIterator($sth); + } + + /** + * + * Yields 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 Iterator\ColIterator + * + */ + public function yieldCol($statement, array $values = array()) + { + $sth = $this->perform($statement, $values); + return new Iterator\ColIterator($sth); + } + + /** + * + * 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_name The name of the class to create from each + * row. + * + * @param array $ctor_args Arguments to pass to each object constructor. + * + * @return Iterator\ObjectsIterator + * + */ + public function yieldObjects( + $statement, + array $values = array(), + $class_name = 'stdClass', + array $ctor_args = array() + ) { + $sth = $this->perform($statement, $values); + return new Iterator\ObjectsIterator($sth, $class_name, $ctor_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 Iterator\PairsIterator + * + */ + public function yieldPairs($statement, array $values = array()) + { + $sth = $this->perform($statement, $values); + return new Iterator\PairsIterator($sth); + } + + /** + * + * Bind a value using the proper PDO::PARAM_* type. + * + * @param PDOStatement $sth The statement to bind to. + * + * @param mixed $key The placeholder key. + * + * @param mixed $val The value to bind to the statement. + * + * @return null + * + * @throws Exception\CannotBindValue when the value to be bound is not + * bindable (e.g., array, object, or resource). + * + */ + protected function bindValue(PDOStatement $sth, $key, $val) + { + if (is_int($val)) { + return $sth->bindValue($key, $val, self::PARAM_INT); + } + + if (is_bool($val)) { + // bind booleans as string '1' or string '0'. + // cf. https://bugs.php.net/bug.php?id=49255 + $val = $val ? '1' : '0'; + return $sth->bindValue($key, $val, self::PARAM_BOOL); + } + + if (is_null($val)) { + return $sth->bindValue($key, $val, self::PARAM_NULL); + } + + if (! is_scalar($val)) { + $type = gettype($val); + throw new Exception\CannotBindValue( + "Cannot bind value of type '{$type}' to placeholder '{$key}'" + ); + } + + $sth->bindValue($key, $val); + } +} diff --git a/includes/vendor/aura/sql/src/ExtendedPdoInterface.php b/includes/vendor/aura/sql/src/ExtendedPdoInterface.php new file mode 100644 index 0000000..77e49b1 --- /dev/null +++ b/includes/vendor/aura/sql/src/ExtendedPdoInterface.php @@ -0,0 +1,267 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +/** + * + * An interface to the Aura.Sql extended PDO object. + * + * @package Aura.Sql + * + * @method Iterator\AllIterator yieldAll(string $statment, array $values = array()) Yields rows from the database keyed on the first column of each row. + * @method Iterator\AssocIterator yieldAssoc(string $statment, array $values = array()) Yields rows from the database keyed on the first column of each row. + * @method Iterator\ColIterator yieldCol(string $statment, array $values = array()) Yields the first column of each row. + * @method Iterator\ObjectsIterator yieldObjects(string $statment, array $values = array(), $class_name = 'stdClass', array $ctor_args = array()) Yields objects where the column values are mapped to object properties. + * @method Iterator\PairsIterator yieldPairs(string $statment, array $values = array()) Yields key-value pairs (first column is the key, second column is the value). + */ +interface ExtendedPdoInterface extends PdoInterface +{ + /** + * + * Connects to the database and sets PDO attributes. + * + * @return null + * + * @throws \PDOException if the connection fails. + * + */ + public function connect(); + + /** + * + * 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 array + * + */ + public function fetchAffected($statement, array $values = array()); + + /** + * + * 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchAll($statement, array $values = array(), $callable = null); + + /** + * + * 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchAssoc($statement, array $values = array(), $callable = null); + + /** + * + * 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchCol($statement, array $values = array(), $callable = null); + + /** + * + * 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_name The name of the class to create. + * + * @param array $ctor_args Arguments to pass to the object constructor. + * + * @return object|false + * + */ + public function fetchObject( + $statement, + array $values = array(), + $class_name = 'StdClass', + array $ctor_args = array() + ); + + /** + * + * 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_name The name of the class to create from each + * row. + * + * @param array $ctor_args Arguments to pass to each object constructor. + * + * @return array + * + */ + public function fetchObjects( + $statement, + array $values = array(), + $class_name = 'StdClass', + array $ctor_args = array() + ); + + /** + * + * 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|false + * + */ + public function fetchOne($statement, array $values = array()); + + /** + * + * 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. + * + * @param callable $callable A callable to be applied to each of the rows + * to be returned. + * + * @return array + * + */ + public function fetchPairs($statement, array $values = array(), $callable = null); + + /** + * + * 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 = array()); + + /** + * + * Returns the DSN for a lazy connection; if the underlying PDO instance + * was injected at construction time, this will be null. + * + * @return string|null + * + */ + public function getDsn(); + + /** + * + * Returns the underlying PDO connection object. + * + * @return PDO or Null if connection was manually disconnected + * + */ + public function getPdo(); + + /** + * + * Returns the profiler object. + * + * @return ProfilerInterface + * + */ + public function getProfiler(); + + /** + * + * Is the instance connected to a database? + * + * @return bool + * + */ + public function isConnected(); + + /** + * + * 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 = array()); + + /** + * + * Sets the profiler object. + * + * @param ProfilerInterface $profiler + * + * @return null + * + */ + public function setProfiler(ProfilerInterface $profiler); +} diff --git a/includes/vendor/aura/sql/src/Iterator/AbstractIterator.php b/includes/vendor/aura/sql/src/Iterator/AbstractIterator.php new file mode 100644 index 0000000..895fd4e --- /dev/null +++ b/includes/vendor/aura/sql/src/Iterator/AbstractIterator.php @@ -0,0 +1,114 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Iterator; + +use Iterator; +use PDOStatement; + +/** + * + * A base class for iterators. + * + * @package Aura.Sql + * + */ +abstract class AbstractIterator implements Iterator +{ + /** + * + * PDO statement. + * + * @var PDOStatement + * + */ + protected $statement; + + /** + * + * Current row value. + * + * @var mixed + * + */ + protected $row; + + /** + * + * Current key. + * + * @var mixed + * + */ + protected $key = -1; + + /** + * + * Frees memory when object is destroyed. + * + */ + public function __destruct() + { + $this->statement->closeCursor(); + unset($this->statement); + } + + /** + * + * Moves row set pointer to first element. + * + */ + public function rewind() + { + $this->key = -1; + $this->next(); + } + + /** + * + * Returns value at current position. + * + * @return mixed + * + */ + public function current() + { + return $this->row; + } + + /** + * + * Returns key at current position. + * + * @return mixed + * + */ + public function key() + { + return $this->key; + } + + /** + * + * Fetches next row from statement. + * + */ + abstract public function next(); + + /** + * + * Detects if iterator state is valid. + * + * @return bool + * + */ + public function valid() + { + return $this->row !== false; + } +} diff --git a/includes/vendor/aura/sql/src/Iterator/AllIterator.php b/includes/vendor/aura/sql/src/Iterator/AllIterator.php new file mode 100644 index 0000000..9d56a24 --- /dev/null +++ b/includes/vendor/aura/sql/src/Iterator/AllIterator.php @@ -0,0 +1,46 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Iterator; + +use PDO; +use PDOStatement; + +/** + * + * The iterator equivalent of `fetchAll()`. + * + * @package Aura.Sql + * + */ +class AllIterator extends AbstractIterator +{ + /** + * + * Constructor. + * + * @param PDOStatement $statement PDO statement. + * + */ + public function __construct(PDOStatement $statement) + { + $this->statement = $statement; + $this->statement->setFetchMode(PDO::FETCH_ASSOC); + } + + /** + * + * Fetches next row from statement. + * + */ + public function next() + { + $this->row = $this->statement->fetch(); + $this->key ++; + } +} diff --git a/includes/vendor/aura/sql/src/Iterator/AssocIterator.php b/includes/vendor/aura/sql/src/Iterator/AssocIterator.php new file mode 100644 index 0000000..ea77a7a --- /dev/null +++ b/includes/vendor/aura/sql/src/Iterator/AssocIterator.php @@ -0,0 +1,49 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Iterator; + +use PDO; +use PDOStatement; + +/** + * + * The iterator equivalent of `fetchAssoc()`. + * + * @package Aura.Sql + * + */ +class AssocIterator extends AbstractIterator +{ + /** + * + * Constructor. + * + * @param PDOStatement $statement PDO statement. + * + */ + public function __construct(PDOStatement $statement) + { + $this->statement = $statement; + $this->statement->setFetchMode(PDO::FETCH_ASSOC); + } + + /** + * + * Fetches next row from statement. + * + */ + public function next() + { + $this->row = $this->statement->fetch(); + $this->key = false; + if ($this->row !== false) { + $this->key = current($this->row); + } + } +} diff --git a/includes/vendor/aura/sql/src/Iterator/ColIterator.php b/includes/vendor/aura/sql/src/Iterator/ColIterator.php new file mode 100644 index 0000000..7be66e8 --- /dev/null +++ b/includes/vendor/aura/sql/src/Iterator/ColIterator.php @@ -0,0 +1,49 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Iterator; + +use PDO; +use PDOStatement; + +/** + * + * The iterator equivalent of `fetchCol()`. + * + * @package Aura.Sql + * + */ +class ColIterator extends AbstractIterator +{ + /** + * + * Constructor. + * + * @param PDOStatement $statement PDO statement. + * + */ + public function __construct(PDOStatement $statement) + { + $this->statement = $statement; + $this->statement->setFetchMode(PDO::FETCH_NUM); + } + + /** + * + * Fetches next row from statement. + * + */ + public function next() + { + $this->row = $this->statement->fetch(); + if ($this->row !== false) { + $this->row = $this->row[0]; + } + $this->key ++; + } +} diff --git a/includes/vendor/aura/sql/src/Iterator/ObjectsIterator.php b/includes/vendor/aura/sql/src/Iterator/ObjectsIterator.php new file mode 100644 index 0000000..bbd08fc --- /dev/null +++ b/includes/vendor/aura/sql/src/Iterator/ObjectsIterator.php @@ -0,0 +1,60 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Iterator; + +use PDO; +use PDOStatement; + +/** + * + * The iterator equivalent of `fetchObjects()`. + * + * @package Aura.Sql + * + */ +class ObjectsIterator extends AbstractIterator +{ + /** + * + * Constructor. + * + * @param PDOStatement $statement PDO statement. + * + * @param string $class_name The name of the class to create. + * + * @param array $ctor_args Arguments to pass to the object constructor. + * + */ + public function __construct( + PDOStatement $statement, + $class_name = 'StdClass', + array $ctor_args = array() + ) { + $this->statement = $statement; + $this->statement->setFetchMode(PDO::FETCH_CLASS, $class_name); + if ($ctor_args) { + $this->statement->setFetchMode( + PDO::FETCH_CLASS, + $class_name, + $ctor_args + ); + } + } + + /** + * + * Fetches next row from statement. + * + */ + public function next() + { + $this->row = $this->statement->fetch(); + $this->key ++; + } +} diff --git a/includes/vendor/aura/sql/src/Iterator/PairsIterator.php b/includes/vendor/aura/sql/src/Iterator/PairsIterator.php new file mode 100644 index 0000000..20c1a13 --- /dev/null +++ b/includes/vendor/aura/sql/src/Iterator/PairsIterator.php @@ -0,0 +1,50 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql\Iterator; + +use PDO; +use PDOStatement; + +/** + * + * The iterator equivalent of `fetchPairs()`. + * + * @package Aura.Sql + * + */ +class PairsIterator extends AbstractIterator +{ + /** + * + * Constructor. + * + * @param PDOStatement $statement PDO statement. + * + */ + public function __construct(PDOStatement $statement) + { + $this->statement = $statement; + $this->statement->setFetchMode(PDO::FETCH_NUM); + } + + /** + * + * Fetches next row from statement. + * + */ + public function next() + { + $this->key = false; + $this->row = $this->statement->fetch(); + if ($this->row !== false) { + $this->key = $this->row[0]; + $this->row = $this->row[1]; + } + } +} diff --git a/includes/vendor/aura/sql/src/PdoInterface.php b/includes/vendor/aura/sql/src/PdoInterface.php new file mode 100644 index 0000000..5987ee4 --- /dev/null +++ b/includes/vendor/aura/sql/src/PdoInterface.php @@ -0,0 +1,196 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +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 int + * + * @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 int $fetch_mode The `PDO::FETCH_*` type to set on the returned + * `PDOStatement::setFetchMode()`. + * + * @param mixed $fetch_arg1 The first additional argument to send to + * `PDOStatement::setFetchMode()`. + * + * @param mixed $fetch_arg2 The second additional argument to send to + * `PDOStatement::setFetchMode()`. + * + * @return \PDOStatement + * + * @see http://php.net/manual/en/pdo.query.php + * + */ + public function query($statement); + + /** + * + * 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 mixed 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(); +} diff --git a/includes/vendor/aura/sql/src/Profiler.php b/includes/vendor/aura/sql/src/Profiler.php new file mode 100644 index 0000000..93a1c9e --- /dev/null +++ b/includes/vendor/aura/sql/src/Profiler.php @@ -0,0 +1,129 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +/** + * + * Retains query profiles. + * + * @package Aura.Sql + * + */ +class Profiler implements ProfilerInterface +{ + /** + * + * Is the profiler active? + * + * @var bool + * + */ + protected $active = false; + + /** + * + * Retained profiles. + * + * @var array + * + */ + protected $profiles = array(); + + protected static $count = 0; + + /** + * + * Turns the profiler on and off. + * + * @param bool $active True to turn on, false to turn off. + * + * @return null + * + */ + public function setActive($active) + { + $this->active = (bool) $active; + } + + /** + * + * Is the profiler active? + * + * @return bool + * + */ + public function isActive() + { + return (bool) $this->active; + } + + /** + * + * Adds a profile entry. + * + * @param float $duration The query duration. + * + * @param string $function The PDO method that made the entry. + * + * @param string $statement The SQL query statement. + * + * @param array $bind_values The values bound to the statement. + * + * @return null + * + */ + public function addProfile( + $duration, + $function, + $statement, + array $bind_values = array() + ) { + if (! $this->isActive()) { + return; + } + + $e = new \Exception; + + // this allows for multiple profilers getting inter-sorted later + $k = self::$count ++; + + $this->profiles[$k] = array( + 'duration' => $duration, + 'function' => $function, + 'statement' => $statement, + 'bind_values' => $bind_values, + 'trace' => $e->getTraceAsString(), + ); + } + + /** + * + * Returns all the profile entries. + * + * @return array + * + */ + public function getProfiles() + { + return $this->profiles; + } + + /** + * + * Reset all the profiles + * + * @return null + * + */ + public function resetProfiles() + { + $this->profiles = array(); + self::$count = 0; + } +} diff --git a/includes/vendor/aura/sql/src/ProfilerInterface.php b/includes/vendor/aura/sql/src/ProfilerInterface.php new file mode 100644 index 0000000..f3ae0d8 --- /dev/null +++ b/includes/vendor/aura/sql/src/ProfilerInterface.php @@ -0,0 +1,80 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +/** + * + * Interface for query profilers. + * + * @package Aura.Sql + * + */ +interface ProfilerInterface +{ + /** + * + * Turns the profiler on and off. + * + * @param bool $active True to turn on, false to turn off. + * + * @return null + * + */ + public function setActive($active); + + /** + * + * Is the profiler active? + * + * @return bool + * + */ + public function isActive(); + + /** + * + * Adds a profile entry. + * + * @param float $duration The query duration. + * + * @param string $function The PDO method that made the entry. + * + * @param string $statement The SQL query statement. + * + * @param array $bind_values The values bound to the statement. + * + * @return null + * + */ + public function addProfile( + $duration, + $function, + $statement, + array $bind_values + ); + + /** + * + * Returns all the profiles. + * + * @return array + * + */ + public function getProfiles(); + + + /** + * + * Reset all the profiles + * + * @return null + * + */ + public function resetProfiles(); +} diff --git a/includes/vendor/aura/sql/src/Rebuilder.php b/includes/vendor/aura/sql/src/Rebuilder.php new file mode 100644 index 0000000..997beff --- /dev/null +++ b/includes/vendor/aura/sql/src/Rebuilder.php @@ -0,0 +1,280 @@ +<?php +/** + * + * This file is part of Aura for PHP. + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + */ +namespace Aura\Sql; + +use Aura\Sql\Exception\MissingParameter; + +/** + * + * This support class for ExtendedPdo rebuilds an SQL statement for automatic + * binding of values. + * + * @package Aura.Sql + * + */ +class Rebuilder +{ + /** + * + * The calling ExtendedPdo object. + * + * @var ExtendedPdo + * + */ + protected $xpdo; + + /** + * + * How many numbered placeholders in the original statement. + * + * @var int + * + */ + protected $num = 0; + + /** + * + * How many numbered placeholders to actually be bound; this may + * differ from 'num' in that some numbered placeholders may get + * replaced with quoted CSV strings + * + * @var int + * + */ + protected $count = 0; + + /** + * + * The initial values to be bound. + * + * @var array + * + */ + protected $values = array(); + + /** + * + * Named and numbered placeholders to bind at the end. + * + * @var array + * + */ + protected $final_values = array(); + + /** + * + * Constructor. + * + * @param ExtendedPdo $xpdo The calling ExtendedPdo object. + * + */ + public function __construct(ExtendedPdo $xpdo) + { + $this->xpdo = $xpdo; + } + + /** + * + * Rebuilds a statement with array values replaced into placeholders. + * + * @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 __invoke($statement, $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 array($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) + { + // find all parts not inside quotes or backslashed-quotes + $apos = "'"; + $quot = '"'; + $parts = preg_split( + "/(($apos+|$quot+|\\$apos+|\\$quot+).*?)\\2/m", + $statement, + -1, + PREG_SPLIT_DELIM_CAPTURE + ); + 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($parts) + { + // loop through the non-quoted parts (0, 3, 6, 9, etc.) + $k = count($parts); + for ($i = 0; $i <= $k; $i += 3) { + $parts[$i] = $this->rebuildPart($parts[$i]); + } + return implode('', $parts); + } + + /** + * + * Rebuilds a single statement part. + * + * @param string $part The statement part. + * + * @return string The rebuilt statement. + * + */ + protected function rebuildPart($part) + { + // split into subparts by ":name" and "?" + $subs = preg_split( + "/(:[a-zA-Z_][a-zA-Z0-9_]*)|(\?)/m", + $part, + -1, + PREG_SPLIT_DELIM_CAPTURE + ); + + // check subparts to convert bound arrays to quoted CSV strings + $subs = $this->prepareValuePlaceholders($subs); + + // reassemble + return implode('', $subs); + } + + /** + * + * Prepares the sub-parts of a query with placeholders. + * + * @param array $subs The query subparts. + * + * @return array The prepared subparts. + * + */ + protected function prepareValuePlaceholders(array $subs) + { + foreach ($subs as $i => $sub) { + + // is the subpart a ?-mark? + if ($sub == '?') { + // prepare as numbered placeholder + $subs[$i] = $this->prepareNumberedPlaceholder($sub); + } + + // is the subpart only a colon? + if ($sub == ':') { + // ignore it + continue; + } + + // does the subpart begin with a colon? + $char = substr($sub, 0, 1); + if ($char != ':') { + // no, so cannot be named placeholder + continue; + } + + // does previous subpart (if there is one) end in a colon? + if ($i > 0 && substr($subs[$i - 1], -1) == ':') { + // prev ended in ':', and current begins with ':', making it a + // double-colon, so ignore it + continue; + } + + // begins with colon, previous did not end with colon, so prepare + // as a named placeholder + $subs[$i] = $this->prepareNamedPlaceholder($sub); + } + + return $subs; + } + + /** + * + * Bind or quote a numbered placeholder in a query subpart. + * + * @param string $sub The query subpart. + * + * @return string The prepared query subpart. + * + * @throws MissingParameter + */ + protected function prepareNumberedPlaceholder($sub) + { + // what numbered placeholder is this in the original statement? + $this->num ++; + + // is the corresponding data element an array? + $bind_array = isset($this->values[$this->num]) + && is_array($this->values[$this->num]); + if ($bind_array) { + // PDO won't bind an array; quote and replace directly + $sub = $this->xpdo->quote($this->values[$this->num]); + } else { + // increase the count of numbered placeholders to be bound + $this->count ++; + if (array_key_exists($this->num, $this->values) === false) { + throw new MissingParameter('Parameter ' . $this->num . ' is missing from the bound values'); + } + $this->final_values[$this->count] = $this->values[$this->num]; + } + + return $sub; + } + + /** + * + * 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) + { + $name = substr($sub, 1); + + // is the corresponding data element an array? + $bind_array = isset($this->values[$name]) + && is_array($this->values[$name]); + if ($bind_array) { + // PDO won't bind an array; quote and replace directly + $sub = $this->xpdo->quote($this->values[$name]); + } else { + // not an array, retain the placeholder for later + $this->final_values[$name] = $this->values[$name]; + } + + return $sub; + } +}
\ No newline at end of file |