Vendoring deprecated composer libs
This commit is contained in:
86
libs/picodb/lib/PicoDb/Builder/BaseBuilder.php
Normal file
86
libs/picodb/lib/PicoDb/Builder/BaseBuilder.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Builder;
|
||||
|
||||
use PicoDb\Database;
|
||||
|
||||
/**
|
||||
* Class InsertBuilder
|
||||
*
|
||||
* @package PicoDb\Builder
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class BaseBuilder
|
||||
{
|
||||
/**
|
||||
* @var Database
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var ConditionBuilder
|
||||
*/
|
||||
protected $conditionBuilder;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $table = '';
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $columns = array();
|
||||
|
||||
/**
|
||||
* InsertBuilder constructor
|
||||
*
|
||||
* @param Database $db
|
||||
* @param ConditionBuilder $condition
|
||||
*/
|
||||
public function __construct(Database $db, ConditionBuilder $condition)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->conditionBuilder = $condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param Database $db
|
||||
* @param ConditionBuilder $condition
|
||||
* @return static
|
||||
*/
|
||||
public static function getInstance(Database $db, ConditionBuilder $condition)
|
||||
{
|
||||
return new static($db, $condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set table name
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @return $this
|
||||
*/
|
||||
public function withTable($table)
|
||||
{
|
||||
$this->table = $table;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set columns name
|
||||
*
|
||||
* @access public
|
||||
* @param string[] $columns
|
||||
* @return $this
|
||||
*/
|
||||
public function withColumns(array $columns)
|
||||
{
|
||||
$this->columns = $columns;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
377
libs/picodb/lib/PicoDb/Builder/ConditionBuilder.php
Normal file
377
libs/picodb/lib/PicoDb/Builder/ConditionBuilder.php
Normal file
@@ -0,0 +1,377 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Builder;
|
||||
|
||||
use PicoDb\Database;
|
||||
use PicoDb\Table;
|
||||
|
||||
/**
|
||||
* Handle SQL conditions
|
||||
*
|
||||
* @package PicoDb\Builder
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ConditionBuilder
|
||||
{
|
||||
/**
|
||||
* Database instance
|
||||
*
|
||||
* @access private
|
||||
* @var Database
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Condition values
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $values = array();
|
||||
|
||||
/**
|
||||
* SQL AND conditions
|
||||
*
|
||||
* @access private
|
||||
* @var string[]
|
||||
*/
|
||||
private $conditions = array();
|
||||
|
||||
/**
|
||||
* SQL OR conditions
|
||||
*
|
||||
* @access private
|
||||
* @var OrConditionBuilder[]
|
||||
*/
|
||||
private $orConditions = array();
|
||||
|
||||
/**
|
||||
* SQL condition offset
|
||||
*
|
||||
* @access private
|
||||
* @var int
|
||||
*/
|
||||
private $orConditionOffset = 0;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the SQL condition
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return empty($this->conditions) ? '' : ' WHERE '.implode(' AND ', $this->conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get condition values
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is some conditions
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasCondition()
|
||||
{
|
||||
return ! empty($this->conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add custom condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql
|
||||
*/
|
||||
public function addCondition($sql)
|
||||
{
|
||||
if ($this->orConditionOffset > 0) {
|
||||
$this->orConditions[$this->orConditionOffset]->withCondition($sql);
|
||||
}
|
||||
else {
|
||||
$this->conditions[] = $sql;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start OR condition
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beginOr()
|
||||
{
|
||||
$this->orConditionOffset++;
|
||||
$this->orConditions[$this->orConditionOffset] = new OrConditionBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close OR condition
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function closeOr()
|
||||
{
|
||||
$condition = $this->orConditions[$this->orConditionOffset]->build();
|
||||
$this->orConditionOffset--;
|
||||
|
||||
if ($this->orConditionOffset > 0) {
|
||||
$this->orConditions[$this->orConditionOffset]->withCondition($condition);
|
||||
} else {
|
||||
$this->conditions[] = $condition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Equal condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function eq($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' = ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Not equal condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function neq($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' != ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* IN condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
*/
|
||||
public function in($column, array $values)
|
||||
{
|
||||
if (! empty($values)) {
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' IN ('.implode(', ', array_fill(0, count($values), '?')).')');
|
||||
$this->values = array_merge($this->values, $values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IN condition with a subquery
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param Table $subquery
|
||||
*/
|
||||
public function inSubquery($column, Table $subquery)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' IN ('.$subquery->buildSelectQuery().')');
|
||||
$this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* NOT IN condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param array $values
|
||||
*/
|
||||
public function notIn($column, array $values)
|
||||
{
|
||||
if (! empty($values)) {
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.implode(', ', array_fill(0, count($values), '?')).')');
|
||||
$this->values = array_merge($this->values, $values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NOT IN condition with a subquery
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param Table $subquery
|
||||
*/
|
||||
public function notInSubquery($column, Table $subquery)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' NOT IN ('.$subquery->buildSelectQuery().')');
|
||||
$this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* LIKE condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function like($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('LIKE').' ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* ILIKE condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function ilike($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' '.$this->db->getDriver()->getOperator('ILIKE').' ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater than condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function gt($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' > ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater than condition with subquery
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param Table $subquery
|
||||
*/
|
||||
public function gtSubquery($column, Table $subquery)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' > ('.$subquery->buildSelectQuery().')');
|
||||
$this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower than condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function lt($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' < ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower than condition with subquery
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param Table $subquery
|
||||
*/
|
||||
public function ltSubquery($column, Table $subquery)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' < ('.$subquery->buildSelectQuery().')');
|
||||
$this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater than or equals condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function gte($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' >= ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Greater than or equal condition with subquery
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param Table $subquery
|
||||
*/
|
||||
public function gteSubquery($column, Table $subquery)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' >= ('.$subquery->buildSelectQuery().')');
|
||||
$this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower than or equals condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function lte($column, $value)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' <= ?');
|
||||
$this->values[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lower than or equal condition with subquery
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param Table $subquery
|
||||
*/
|
||||
public function lteSubquery($column, Table $subquery)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' <= ('.$subquery->buildSelectQuery().')');
|
||||
$this->values = array_merge($this->values, $subquery->getConditionBuilder()->getValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* IS NULL condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
*/
|
||||
public function isNull($column)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' IS NULL');
|
||||
}
|
||||
|
||||
/**
|
||||
* IS NOT NULL condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
*/
|
||||
public function notNull($column)
|
||||
{
|
||||
$this->addCondition($this->db->escapeIdentifier($column).' IS NOT NULL');
|
||||
}
|
||||
}
|
||||
36
libs/picodb/lib/PicoDb/Builder/InsertBuilder.php
Normal file
36
libs/picodb/lib/PicoDb/Builder/InsertBuilder.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Builder;
|
||||
|
||||
/**
|
||||
* Class InsertBuilder
|
||||
*
|
||||
* @package PicoDb\Builder
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class InsertBuilder extends BaseBuilder
|
||||
{
|
||||
/**
|
||||
* Build SQL
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$columns = array();
|
||||
$placeholders = array();
|
||||
|
||||
foreach ($this->columns as $column) {
|
||||
$columns[] = $this->db->escapeIdentifier($column);
|
||||
$placeholders[] = ':'.$column;
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'INSERT INTO %s (%s) VALUES (%s)',
|
||||
$this->db->escapeIdentifier($this->table),
|
||||
implode(', ', $columns),
|
||||
implode(', ', $placeholders)
|
||||
);
|
||||
}
|
||||
}
|
||||
43
libs/picodb/lib/PicoDb/Builder/OrConditionBuilder.php
Normal file
43
libs/picodb/lib/PicoDb/Builder/OrConditionBuilder.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Builder;
|
||||
|
||||
/**
|
||||
* Class OrConditionBuilder
|
||||
*
|
||||
* @package PicoDb\Builder
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class OrConditionBuilder
|
||||
{
|
||||
/**
|
||||
* List of SQL conditions
|
||||
*
|
||||
* @access protected
|
||||
* @var string[]
|
||||
*/
|
||||
protected $conditions = array();
|
||||
|
||||
/**
|
||||
* Add new condition
|
||||
*
|
||||
* @access public
|
||||
* @param string $condition
|
||||
* @return $this
|
||||
*/
|
||||
public function withCondition($condition) {
|
||||
$this->conditions[] = $condition;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return '('.implode(' OR ', $this->conditions).')';
|
||||
}
|
||||
}
|
||||
56
libs/picodb/lib/PicoDb/Builder/UpdateBuilder.php
Normal file
56
libs/picodb/lib/PicoDb/Builder/UpdateBuilder.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Builder;
|
||||
|
||||
/**
|
||||
* Class UpdateBuilder
|
||||
*
|
||||
* @package PicoDb\Builder
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UpdateBuilder extends BaseBuilder
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
protected $sumColumns = array();
|
||||
|
||||
/**
|
||||
* Set columns name
|
||||
*
|
||||
* @access public
|
||||
* @param string[] $columns
|
||||
* @return $this
|
||||
*/
|
||||
public function withSumColumns(array $columns)
|
||||
{
|
||||
$this->sumColumns = $columns;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SQL
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$columns = array();
|
||||
|
||||
foreach ($this->columns as $column) {
|
||||
$columns[] = $this->db->escapeIdentifier($column).'=?';
|
||||
}
|
||||
|
||||
foreach ($this->sumColumns as $column) {
|
||||
$columns[] = $this->db->escapeIdentifier($column).'='.$this->db->escapeIdentifier($column).' + ?';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
'UPDATE %s SET %s %s',
|
||||
$this->db->escapeIdentifier($this->table),
|
||||
implode(', ', $columns),
|
||||
$this->conditionBuilder->build()
|
||||
);
|
||||
}
|
||||
}
|
||||
370
libs/picodb/lib/PicoDb/Database.php
Normal file
370
libs/picodb/lib/PicoDb/Database.php
Normal file
@@ -0,0 +1,370 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use Closure;
|
||||
use PDOException;
|
||||
use LogicException;
|
||||
use PicoDb\Driver\Mssql;
|
||||
use PicoDb\Driver\Sqlite;
|
||||
use PicoDb\Driver\Mysql;
|
||||
use PicoDb\Driver\Postgres;
|
||||
|
||||
/**
|
||||
* Database
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Database
|
||||
{
|
||||
/**
|
||||
* Database instances
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private static $instances = array();
|
||||
|
||||
/**
|
||||
* Statement object
|
||||
*
|
||||
* @access protected
|
||||
* @var StatementHandler
|
||||
*/
|
||||
protected $statementHandler;
|
||||
|
||||
/**
|
||||
* Queries logs
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $logs = array();
|
||||
|
||||
/**
|
||||
* Driver instance
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* Initialize the driver
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array $settings = array())
|
||||
{
|
||||
$this->driver = DriverFactory::getDriver($settings);
|
||||
$this->statementHandler = new StatementHandler($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->closeConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new database instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $name Instance name
|
||||
* @param Closure $callback Callback
|
||||
*/
|
||||
public static function setInstance($name, Closure $callback)
|
||||
{
|
||||
self::$instances[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a database instance
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $name Instance name
|
||||
* @return Database
|
||||
*/
|
||||
public static function getInstance($name)
|
||||
{
|
||||
if (! isset(self::$instances[$name])) {
|
||||
throw new LogicException('No database instance created with that name');
|
||||
}
|
||||
|
||||
if (is_callable(self::$instances[$name])) {
|
||||
self::$instances[$name] = call_user_func(self::$instances[$name]);
|
||||
}
|
||||
|
||||
return self::$instances[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a log message
|
||||
*
|
||||
* @access public
|
||||
* @param mixed $message
|
||||
* @return Database
|
||||
*/
|
||||
public function setLogMessage($message)
|
||||
{
|
||||
$this->logs[] = is_array($message) ? var_export($message, true) : $message;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add many log messages
|
||||
*
|
||||
* @access public
|
||||
* @param array $messages
|
||||
* @return Database
|
||||
*/
|
||||
public function setLogMessages(array $messages)
|
||||
{
|
||||
foreach ($messages as $message) {
|
||||
$this->setLogMessage($message);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all queries logs
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getLogMessages()
|
||||
{
|
||||
return $this->logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @return \PDO
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->driver->getConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Driver instance
|
||||
*
|
||||
* @access public
|
||||
* @return Mssql|Sqlite|Postgres|Mysql
|
||||
*/
|
||||
public function getDriver()
|
||||
{
|
||||
return $this->driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last inserted id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastId()
|
||||
{
|
||||
return (int) $this->driver->getLastId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get statement object
|
||||
*
|
||||
* @access public
|
||||
* @return StatementHandler
|
||||
*/
|
||||
public function getStatementHandler()
|
||||
{
|
||||
return $this->statementHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the PDO connection
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function closeConnection()
|
||||
{
|
||||
$this->driver->closeConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape an identifier (column, table name...)
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Value
|
||||
* @param string $table Table name
|
||||
* @return string
|
||||
*/
|
||||
public function escapeIdentifier($value, $table = '')
|
||||
{
|
||||
// Do not escape custom query
|
||||
if (strpos($value, '.') !== false || strpos($value, ' ') !== false) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (! empty($table)) {
|
||||
return $this->driver->escape($table).'.'.$this->driver->escape($value);
|
||||
}
|
||||
|
||||
return $this->driver->escape($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape an identifier list
|
||||
*
|
||||
* @access public
|
||||
* @param array $identifiers List of identifiers
|
||||
* @param string $table Table name
|
||||
* @return string[]
|
||||
*/
|
||||
public function escapeIdentifierList(array $identifiers, $table = '')
|
||||
{
|
||||
foreach ($identifiers as $key => $value) {
|
||||
$identifiers[$key] = $this->escapeIdentifier($value, $table);
|
||||
}
|
||||
|
||||
return $identifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a prepared statement
|
||||
*
|
||||
* Note: returns false on duplicate keys instead of SQLException
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql SQL query
|
||||
* @param array $values Values
|
||||
* @return \PDOStatement|false
|
||||
*/
|
||||
public function execute($sql, array $values = array())
|
||||
{
|
||||
return $this->statementHandler
|
||||
->withSql($sql)
|
||||
->withPositionalParams($values)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a transaction
|
||||
*
|
||||
* @access public
|
||||
* @param Closure $callback Callback
|
||||
* @return mixed
|
||||
*/
|
||||
public function transaction(Closure $callback)
|
||||
{
|
||||
try {
|
||||
|
||||
$this->startTransaction();
|
||||
$result = $callback($this);
|
||||
$this->closeTransaction();
|
||||
|
||||
return $result === null ? true : $result;
|
||||
} catch (PDOException $e) {
|
||||
return $this->statementHandler->handleSqlError($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin a transaction
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function startTransaction()
|
||||
{
|
||||
if (! $this->getConnection()->inTransaction()) {
|
||||
$this->getConnection()->beginTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit a transaction
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function closeTransaction()
|
||||
{
|
||||
if ($this->getConnection()->inTransaction()) {
|
||||
$this->getConnection()->commit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback a transaction
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function cancelTransaction()
|
||||
{
|
||||
if ($this->getConnection()->inTransaction()) {
|
||||
$this->getConnection()->rollBack();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a table object
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @return Table
|
||||
*/
|
||||
public function table($table)
|
||||
{
|
||||
return new Table($this, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a hashtable object
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @return Hashtable
|
||||
*/
|
||||
public function hashtable($table)
|
||||
{
|
||||
return new Hashtable($this, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a LOB object
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @return LargeObject
|
||||
*/
|
||||
public function largeObject($table)
|
||||
{
|
||||
return new LargeObject($this, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a schema object
|
||||
*
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @return Schema
|
||||
*/
|
||||
public function schema($namespace = null)
|
||||
{
|
||||
$schema = new Schema($this);
|
||||
|
||||
if ($namespace !== null) {
|
||||
$schema->setNamespace($namespace);
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
234
libs/picodb/lib/PicoDb/Driver/Base.php
Normal file
234
libs/picodb/lib/PicoDb/Driver/Base.php
Normal file
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
use PDO;
|
||||
use LogicException;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Base Driver class
|
||||
*
|
||||
* @package PicoDb\Driver
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
/**
|
||||
* List of required settings options
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $requiredAttributes = array();
|
||||
|
||||
/**
|
||||
* PDO connection
|
||||
*
|
||||
* @access protected
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo = null;
|
||||
|
||||
/**
|
||||
* Create a new PDO connection
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
abstract public function createConnection(array $settings);
|
||||
|
||||
/**
|
||||
* Enable foreign keys
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
*/
|
||||
abstract public function enableForeignKeys();
|
||||
|
||||
/**
|
||||
* Disable foreign keys
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
*/
|
||||
abstract public function disableForeignKeys();
|
||||
|
||||
/**
|
||||
* Return true if the error code is a duplicate key
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param integer $code
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function isDuplicateKeyError($code);
|
||||
|
||||
/**
|
||||
* Escape identifier
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param string $identifier
|
||||
* @return string
|
||||
*/
|
||||
abstract public function escape($identifier);
|
||||
|
||||
/**
|
||||
* Get non standard operator
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param string $operator
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getOperator($operator);
|
||||
|
||||
/**
|
||||
* Get last inserted id
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
abstract public function getLastId();
|
||||
|
||||
/**
|
||||
* Get current schema version
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
abstract public function getSchemaVersion();
|
||||
|
||||
/**
|
||||
* Set current schema version
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param integer $version
|
||||
*/
|
||||
abstract public function setSchemaVersion($version);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
public function __construct(array $settings)
|
||||
{
|
||||
foreach ($this->requiredAttributes as $attribute) {
|
||||
if (! isset($settings[$attribute])) {
|
||||
throw new LogicException('This configuration parameter is missing: "'.$attribute.'"');
|
||||
}
|
||||
}
|
||||
|
||||
$this->createConnection($settings);
|
||||
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @return PDO
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->pdo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the PDO connection
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function closeConnection()
|
||||
{
|
||||
$this->pdo = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert for a key/value variable
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @param string $keyColumn
|
||||
* @param string $valueColumn
|
||||
* @param array $dictionary
|
||||
* @return bool False on failure
|
||||
*/
|
||||
public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
|
||||
{
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
foreach ($dictionary as $key => $value) {
|
||||
|
||||
$rq = $this->pdo->prepare('SELECT 1 FROM '.$this->escape($table).' WHERE '.$this->escape($keyColumn).'=?');
|
||||
$rq->execute(array($key));
|
||||
|
||||
if ($rq->fetchColumn()) {
|
||||
$rq = $this->pdo->prepare('UPDATE '.$this->escape($table).' SET '.$this->escape($valueColumn).'=? WHERE '.$this->escape($keyColumn).'=?');
|
||||
$rq->execute(array($value, $key));
|
||||
}
|
||||
else {
|
||||
$rq = $this->pdo->prepare('INSERT INTO '.$this->escape($table).' ('.$this->escape($keyColumn).', '.$this->escape($valueColumn).') VALUES (?, ?)');
|
||||
$rq->execute(array($key, $value));
|
||||
}
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run EXPLAIN command
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
public function explain($sql, array $values)
|
||||
{
|
||||
return $this->getConnection()->query('EXPLAIN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace placeholder with values in prepared statement
|
||||
*
|
||||
* @access protected
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @return string
|
||||
*/
|
||||
protected function getSqlFromPreparedStatement($sql, array $values)
|
||||
{
|
||||
foreach ($values as $value) {
|
||||
$sql = substr_replace($sql, "'$value'", strpos($sql, '?'), 1);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database version
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDatabaseVersion()
|
||||
{
|
||||
return $this->getConnection()->query('SELECT VERSION()')->fetchColumn();
|
||||
}
|
||||
}
|
||||
178
libs/picodb/lib/PicoDb/Driver/Mssql.php
Normal file
178
libs/picodb/lib/PicoDb/Driver/Mssql.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* Microsoft SQL Server Driver
|
||||
*
|
||||
* @package PicoDb\Driver
|
||||
* @author Algy Taylor <thomas.taylor@cmft.nhs.uk>
|
||||
*/
|
||||
class Mssql extends Base
|
||||
{
|
||||
/**
|
||||
* List of required settings options
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $requiredAttributes = array(
|
||||
'hostname',
|
||||
'username',
|
||||
'password',
|
||||
'database',
|
||||
);
|
||||
|
||||
/**
|
||||
* Table to store the schema version
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $schemaTable = 'schema_version';
|
||||
|
||||
/**
|
||||
* Create a new PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
public function createConnection(array $settings)
|
||||
{
|
||||
$dsn = 'sqlsrv:Server=' . $settings['hostname'] . ';Database=' . $settings['database'];
|
||||
|
||||
if (! empty($settings['port'])) {
|
||||
$dsn .= ';port=' . $settings['port'];
|
||||
}
|
||||
|
||||
$this->pdo = new PDO($dsn, $settings['username'], $settings['password']);
|
||||
|
||||
if (isset($settings['schema_table'])) {
|
||||
$this->schemaTable = $settings['schema_table'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enableForeignKeys()
|
||||
{
|
||||
$this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT ALL"; GO;');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disableForeignKeys()
|
||||
{
|
||||
$this->pdo->exec('EXEC sp_MSforeachtable @command1="ALTER TABLE ? NOCHECK CONSTRAINT ALL"; GO;');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the error code is a duplicate key
|
||||
*
|
||||
* @access public
|
||||
* @param integer $code
|
||||
* @return boolean
|
||||
*/
|
||||
public function isDuplicateKeyError($code)
|
||||
{
|
||||
return $code == 2601;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape identifier
|
||||
*
|
||||
* https://msdn.microsoft.com/en-us/library/ms175874.aspx
|
||||
*
|
||||
* @access public
|
||||
* @param string $identifier
|
||||
* @return string
|
||||
*/
|
||||
public function escape($identifier)
|
||||
{
|
||||
return '['.$identifier.']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non standard operator
|
||||
*
|
||||
* @access public
|
||||
* @param string $operator
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator($operator)
|
||||
{
|
||||
if ($operator === 'LIKE' || $operator === 'ILIKE') {
|
||||
return 'LIKE';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastId()
|
||||
{
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current schema version
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getSchemaVersion()
|
||||
{
|
||||
$this->pdo->exec("CREATE TABLE IF NOT EXISTS [".$this->schemaTable."] ([version] INT DEFAULT '0')");
|
||||
|
||||
$rq = $this->pdo->prepare('SELECT [version] FROM ['.$this->schemaTable.']');
|
||||
$rq->execute();
|
||||
$result = $rq->fetchColumn();
|
||||
|
||||
if ($result !== false) {
|
||||
return (int) $result;
|
||||
}
|
||||
else {
|
||||
$this->pdo->exec('INSERT INTO ['.$this->schemaTable.'] VALUES(0)');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current schema version
|
||||
*
|
||||
* @access public
|
||||
* @param integer $version
|
||||
*/
|
||||
public function setSchemaVersion($version)
|
||||
{
|
||||
$rq = $this->pdo->prepare('UPDATE ['.$this->schemaTable.'] SET [version]=?');
|
||||
$rq->execute(array($version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run EXPLAIN command
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
public function explain($sql, array $values)
|
||||
{
|
||||
$this->getConnection()->exec('SET SHOWPLAN_ALL ON');
|
||||
return $this->getConnection()->query($this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
268
libs/picodb/lib/PicoDb/Driver/Mysql.php
Normal file
268
libs/picodb/lib/PicoDb/Driver/Mysql.php
Normal file
@@ -0,0 +1,268 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Mysql Driver
|
||||
*
|
||||
* @package PicoDb\Driver
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Mysql extends Base
|
||||
{
|
||||
/**
|
||||
* List of required settings options
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $requiredAttributes = array(
|
||||
'hostname',
|
||||
'username',
|
||||
'password',
|
||||
'database',
|
||||
);
|
||||
|
||||
/**
|
||||
* Table to store the schema version
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $schemaTable = 'schema_version';
|
||||
|
||||
/**
|
||||
* Create a new PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
public function createConnection(array $settings)
|
||||
{
|
||||
$this->pdo = new PDO(
|
||||
$this->buildDsn($settings),
|
||||
$settings['username'],
|
||||
$settings['password'],
|
||||
$this->buildOptions($settings)
|
||||
);
|
||||
|
||||
if (isset($settings['schema_table'])) {
|
||||
$this->schemaTable = $settings['schema_table'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build connection DSN
|
||||
*
|
||||
* @access protected
|
||||
* @param array $settings
|
||||
* @return string
|
||||
*/
|
||||
protected function buildDsn(array $settings)
|
||||
{
|
||||
$charset = empty($settings['charset']) ? 'utf8' : $settings['charset'];
|
||||
$dsn = 'mysql:host='.$settings['hostname'].';dbname='.$settings['database'].';charset='.$charset;
|
||||
|
||||
if (! empty($settings['port'])) {
|
||||
$dsn .= ';port='.$settings['port'];
|
||||
}
|
||||
|
||||
return $dsn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build connection options
|
||||
*
|
||||
* @access protected
|
||||
* @param array $settings
|
||||
* @return array
|
||||
*/
|
||||
protected function buildOptions(array $settings)
|
||||
{
|
||||
$options = array(
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET sql_mode = STRICT_ALL_TABLES',
|
||||
);
|
||||
|
||||
if (! empty($settings['ssl_key'])) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_KEY] = $settings['ssl_key'];
|
||||
}
|
||||
|
||||
if (! empty($settings['ssl_cert'])) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_CERT] = $settings['ssl_cert'];
|
||||
}
|
||||
|
||||
if (! empty($settings['ssl_ca'])) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_CA] = $settings['ssl_ca'];
|
||||
}
|
||||
|
||||
if (! empty($settings['persistent'])) {
|
||||
$options[PDO::ATTR_PERSISTENT] = $settings['persistent'];
|
||||
}
|
||||
|
||||
if (! empty($settings['timeout'])) {
|
||||
$options[PDO::ATTR_TIMEOUT] = $settings['timeout'];
|
||||
}
|
||||
|
||||
if (isset($settings['verify_server_cert'])) {
|
||||
$options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $settings['verify_server_cert'];
|
||||
}
|
||||
|
||||
if (! empty($settings['case'])) {
|
||||
$options[PDO::ATTR_CASE] = $settings['case'];
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enableForeignKeys()
|
||||
{
|
||||
$this->pdo->exec('SET FOREIGN_KEY_CHECKS=1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disableForeignKeys()
|
||||
{
|
||||
$this->pdo->exec('SET FOREIGN_KEY_CHECKS=0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the error code is a duplicate key
|
||||
*
|
||||
* @access public
|
||||
* @param integer $code
|
||||
* @return boolean
|
||||
*/
|
||||
public function isDuplicateKeyError($code)
|
||||
{
|
||||
return $code == 23000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape identifier
|
||||
*
|
||||
* @access public
|
||||
* @param string $identifier
|
||||
* @return string
|
||||
*/
|
||||
public function escape($identifier)
|
||||
{
|
||||
return '`'.$identifier.'`';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non standard operator
|
||||
*
|
||||
* @access public
|
||||
* @param string $operator
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator($operator)
|
||||
{
|
||||
if ($operator === 'LIKE') {
|
||||
return 'LIKE BINARY';
|
||||
}
|
||||
else if ($operator === 'ILIKE') {
|
||||
return 'LIKE';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastId()
|
||||
{
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current schema version
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getSchemaVersion()
|
||||
{
|
||||
$this->pdo->exec("CREATE TABLE IF NOT EXISTS `".$this->schemaTable."` (`version` INT DEFAULT '0') ENGINE=InnoDB CHARSET=utf8");
|
||||
|
||||
$rq = $this->pdo->prepare('SELECT `version` FROM `'.$this->schemaTable.'`');
|
||||
$rq->execute();
|
||||
$result = $rq->fetchColumn();
|
||||
|
||||
if ($result !== false) {
|
||||
return (int) $result;
|
||||
}
|
||||
else {
|
||||
$this->pdo->exec('INSERT INTO `'.$this->schemaTable.'` VALUES(0)');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current schema version
|
||||
*
|
||||
* @access public
|
||||
* @param integer $version
|
||||
*/
|
||||
public function setSchemaVersion($version)
|
||||
{
|
||||
$rq = $this->pdo->prepare('UPDATE `'.$this->schemaTable.'` SET `version`=?');
|
||||
$rq->execute(array($version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert for a key/value variable
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @param string $keyColumn
|
||||
* @param string $valueColumn
|
||||
* @param array $dictionary
|
||||
* @return bool False on failure
|
||||
*/
|
||||
public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
|
||||
{
|
||||
try {
|
||||
|
||||
$sql = sprintf(
|
||||
'REPLACE INTO %s (%s, %s) VALUES %s',
|
||||
$this->escape($table),
|
||||
$this->escape($keyColumn),
|
||||
$this->escape($valueColumn),
|
||||
implode(', ', array_fill(0, count($dictionary), '(?, ?)'))
|
||||
);
|
||||
|
||||
$values = array();
|
||||
|
||||
foreach ($dictionary as $key => $value) {
|
||||
$values[] = $key;
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
$rq = $this->pdo->prepare($sql);
|
||||
$rq->execute($values);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
212
libs/picodb/lib/PicoDb/Driver/Postgres.php
Normal file
212
libs/picodb/lib/PicoDb/Driver/Postgres.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Postgres Driver
|
||||
*
|
||||
* @package PicoDb\Driver
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Postgres extends Base
|
||||
{
|
||||
/**
|
||||
* List of required settings options
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $requiredAttributes = array(
|
||||
'database',
|
||||
);
|
||||
|
||||
/**
|
||||
* Table to store the schema version
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $schemaTable = 'schema_version';
|
||||
|
||||
/**
|
||||
* Create a new PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
public function createConnection(array $settings)
|
||||
{
|
||||
$dsn = 'pgsql:dbname='.$settings['database'];
|
||||
$username = null;
|
||||
$password = null;
|
||||
$options = array();
|
||||
|
||||
if (! empty($settings['username'])) {
|
||||
$username = $settings['username'];
|
||||
}
|
||||
|
||||
if (! empty($settings['password'])) {
|
||||
$password = $settings['password'];
|
||||
}
|
||||
|
||||
if (! empty($settings['hostname'])) {
|
||||
$dsn .= ';host='.$settings['hostname'];
|
||||
}
|
||||
|
||||
if (! empty($settings['port'])) {
|
||||
$dsn .= ';port='.$settings['port'];
|
||||
}
|
||||
|
||||
if (! empty($settings['timeout'])) {
|
||||
$options[PDO::ATTR_TIMEOUT] = $settings['timeout'];
|
||||
}
|
||||
|
||||
$this->pdo = new PDO($dsn, $username, $password, $options);
|
||||
|
||||
if (isset($settings['schema_table'])) {
|
||||
$this->schemaTable = $settings['schema_table'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enableForeignKeys()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disableForeignKeys()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the error code is a duplicate key
|
||||
*
|
||||
* @access public
|
||||
* @param integer $code
|
||||
* @return boolean
|
||||
*/
|
||||
public function isDuplicateKeyError($code)
|
||||
{
|
||||
return $code == 23505 || $code == 23503;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape identifier
|
||||
*
|
||||
* @access public
|
||||
* @param string $identifier
|
||||
* @return string
|
||||
*/
|
||||
public function escape($identifier)
|
||||
{
|
||||
return '"'.$identifier.'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non standard operator
|
||||
*
|
||||
* @access public
|
||||
* @param string $operator
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator($operator)
|
||||
{
|
||||
if ($operator === 'LIKE') {
|
||||
return 'LIKE';
|
||||
}
|
||||
else if ($operator === 'ILIKE') {
|
||||
return 'ILIKE';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastId()
|
||||
{
|
||||
try {
|
||||
$rq = $this->pdo->prepare('SELECT LASTVAL()');
|
||||
$rq->execute();
|
||||
|
||||
return $rq->fetchColumn();
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current schema version
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getSchemaVersion()
|
||||
{
|
||||
$this->pdo->exec("CREATE TABLE IF NOT EXISTS ".$this->schemaTable." (version INTEGER DEFAULT 0)");
|
||||
|
||||
$rq = $this->pdo->prepare('SELECT "version" FROM "'.$this->schemaTable.'"');
|
||||
$rq->execute();
|
||||
$result = $rq->fetchColumn();
|
||||
|
||||
if ($result !== false) {
|
||||
return (int) $result;
|
||||
}
|
||||
else {
|
||||
$this->pdo->exec('INSERT INTO '.$this->schemaTable.' VALUES(0)');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current schema version
|
||||
*
|
||||
* @access public
|
||||
* @param integer $version
|
||||
*/
|
||||
public function setSchemaVersion($version)
|
||||
{
|
||||
$rq = $this->pdo->prepare('UPDATE '.$this->schemaTable.' SET version=?');
|
||||
$rq->execute(array($version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Run EXPLAIN command
|
||||
*
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
public function explain($sql, array $values)
|
||||
{
|
||||
return $this->getConnection()->query('EXPLAIN (FORMAT YAML) '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database version
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDatabaseVersion()
|
||||
{
|
||||
return $this->getConnection()->query('SHOW server_version')->fetchColumn();
|
||||
}
|
||||
}
|
||||
199
libs/picodb/lib/PicoDb/Driver/Sqlite.php
Normal file
199
libs/picodb/lib/PicoDb/Driver/Sqlite.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb\Driver;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Sqlite Driver
|
||||
*
|
||||
* @package PicoDb\Driver
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Sqlite extends Base
|
||||
{
|
||||
/**
|
||||
* List of required settings options
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $requiredAttributes = array('filename');
|
||||
|
||||
/**
|
||||
* Create a new PDO connection
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
*/
|
||||
public function createConnection(array $settings)
|
||||
{
|
||||
$options = array();
|
||||
|
||||
if (! empty($settings['timeout'])) {
|
||||
$options[PDO::ATTR_TIMEOUT] = $settings['timeout'];
|
||||
}
|
||||
|
||||
$this->pdo = new PDO('sqlite:'.$settings['filename'], null, null, $options);
|
||||
$this->enableForeignKeys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enableForeignKeys()
|
||||
{
|
||||
$this->pdo->exec('PRAGMA foreign_keys = ON');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable foreign keys
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disableForeignKeys()
|
||||
{
|
||||
$this->pdo->exec('PRAGMA foreign_keys = OFF');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the error code is a duplicate key
|
||||
*
|
||||
* @access public
|
||||
* @param integer $code
|
||||
* @return boolean
|
||||
*/
|
||||
public function isDuplicateKeyError($code)
|
||||
{
|
||||
return $code == 23000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape identifier
|
||||
*
|
||||
* @access public
|
||||
* @param string $identifier
|
||||
* @return string
|
||||
*/
|
||||
public function escape($identifier)
|
||||
{
|
||||
return '"'.$identifier.'"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non standard operator
|
||||
*
|
||||
* @access public
|
||||
* @param string $operator
|
||||
* @return string
|
||||
*/
|
||||
public function getOperator($operator)
|
||||
{
|
||||
if ($operator === 'LIKE' || $operator === 'ILIKE') {
|
||||
return 'LIKE';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last inserted id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastId()
|
||||
{
|
||||
return $this->pdo->lastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current schema version
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getSchemaVersion()
|
||||
{
|
||||
$rq = $this->pdo->prepare('PRAGMA user_version');
|
||||
$rq->execute();
|
||||
|
||||
return (int) $rq->fetchColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current schema version
|
||||
*
|
||||
* @access public
|
||||
* @param integer $version
|
||||
*/
|
||||
public function setSchemaVersion($version)
|
||||
{
|
||||
$this->pdo->exec('PRAGMA user_version='.$version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert for a key/value variable
|
||||
*
|
||||
* @access public
|
||||
* @param string $table
|
||||
* @param string $keyColumn
|
||||
* @param string $valueColumn
|
||||
* @param array $dictionary
|
||||
* @return bool False on failure
|
||||
*/
|
||||
public function upsert($table, $keyColumn, $valueColumn, array $dictionary)
|
||||
{
|
||||
try {
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
foreach ($dictionary as $key => $value) {
|
||||
|
||||
$sql = sprintf(
|
||||
'INSERT OR REPLACE INTO %s (%s, %s) VALUES (?, ?)',
|
||||
$this->escape($table),
|
||||
$this->escape($keyColumn),
|
||||
$this->escape($valueColumn)
|
||||
);
|
||||
|
||||
$rq = $this->pdo->prepare($sql);
|
||||
$rq->execute(array($key, $value));
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (PDOException $e) {
|
||||
$this->pdo->rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run EXPLAIN command
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql
|
||||
* @param array $values
|
||||
* @return array
|
||||
*/
|
||||
public function explain($sql, array $values)
|
||||
{
|
||||
return $this->getConnection()->query('EXPLAIN QUERY PLAN '.$this->getSqlFromPreparedStatement($sql, $values))->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database version
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDatabaseVersion()
|
||||
{
|
||||
return $this->getConnection()->query('SELECT sqlite_version()')->fetchColumn();
|
||||
}
|
||||
}
|
||||
45
libs/picodb/lib/PicoDb/DriverFactory.php
Normal file
45
libs/picodb/lib/PicoDb/DriverFactory.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use LogicException;
|
||||
use PicoDb\Driver\Mssql;
|
||||
use PicoDb\Driver\Mysql;
|
||||
use PicoDb\Driver\Postgres;
|
||||
use PicoDb\Driver\Sqlite;
|
||||
|
||||
/**
|
||||
* Class DriverFactory
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class DriverFactory
|
||||
{
|
||||
/**
|
||||
* Get database driver from settings or environment URL
|
||||
*
|
||||
* @access public
|
||||
* @param array $settings
|
||||
* @return Mssql|Mysql|Postgres|Sqlite
|
||||
*/
|
||||
public static function getDriver(array $settings)
|
||||
{
|
||||
if (! isset($settings['driver'])) {
|
||||
throw new LogicException('You must define a database driver');
|
||||
}
|
||||
|
||||
switch ($settings['driver']) {
|
||||
case 'sqlite':
|
||||
return new Sqlite($settings);
|
||||
case 'mssql':
|
||||
return new Mssql($settings);
|
||||
case 'mysql':
|
||||
return new Mysql($settings);
|
||||
case 'postgres':
|
||||
return new Postgres($settings);
|
||||
default:
|
||||
throw new LogicException('This database driver is not supported');
|
||||
}
|
||||
}
|
||||
}
|
||||
112
libs/picodb/lib/PicoDb/Hashtable.php
Normal file
112
libs/picodb/lib/PicoDb/Hashtable.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
* HashTable (key/value)
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
* @author Mathias Kresin
|
||||
*/
|
||||
class Hashtable extends Table
|
||||
{
|
||||
/**
|
||||
* Column for the key
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $keyColumn = 'key';
|
||||
|
||||
/**
|
||||
* Column for the value
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $valueColumn = 'value';
|
||||
|
||||
/**
|
||||
* Set the key column
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return $this
|
||||
*/
|
||||
public function columnKey($column)
|
||||
{
|
||||
$this->keyColumn = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value column
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return $this
|
||||
*/
|
||||
public function columnValue($column)
|
||||
{
|
||||
$this->valueColumn = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update
|
||||
*
|
||||
* @access public
|
||||
* @param array $hashmap
|
||||
* @return boolean
|
||||
*/
|
||||
public function put(array $hashmap)
|
||||
{
|
||||
return $this->db->getDriver()->upsert($this->getName(), $this->keyColumn, $this->valueColumn, $hashmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hashmap result [ [column1 => column2], [], ...]
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
$hashmap = array();
|
||||
|
||||
// setup where condition
|
||||
if (func_num_args() > 0) {
|
||||
$this->in($this->keyColumn, func_get_args());
|
||||
}
|
||||
|
||||
// setup to select columns in case that there are more than two
|
||||
$this->columns($this->keyColumn, $this->valueColumn);
|
||||
|
||||
$rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
|
||||
$rows = $rq->fetchAll(PDO::FETCH_NUM);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$hashmap[$row[0]] = $row[1];
|
||||
}
|
||||
|
||||
return $hashmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut method to get a hashmap result
|
||||
*
|
||||
* @access public
|
||||
* @param string $key Key column
|
||||
* @param string $value Value column
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($key, $value)
|
||||
{
|
||||
$this->keyColumn = $key;
|
||||
$this->valueColumn = $value;
|
||||
return $this->get();
|
||||
}
|
||||
}
|
||||
167
libs/picodb/lib/PicoDb/LargeObject.php
Normal file
167
libs/picodb/lib/PicoDb/LargeObject.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use PDO;
|
||||
use PicoDb\Builder\InsertBuilder;
|
||||
use PicoDb\Builder\UpdateBuilder;
|
||||
|
||||
/**
|
||||
* Handle Large Objects (LOBs)
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class LargeObject extends Table
|
||||
{
|
||||
/**
|
||||
* Fetch large object as file descriptor
|
||||
*
|
||||
* This method is not compatible with Sqlite and Mysql (return a string instead of resource)
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return resource
|
||||
*/
|
||||
public function findOneColumnAsStream($column)
|
||||
{
|
||||
$this->limit(1);
|
||||
$this->columns($column);
|
||||
|
||||
$rq = $this->db->getStatementHandler()
|
||||
->withSql($this->buildSelectQuery())
|
||||
->withPositionalParams($this->conditionBuilder->getValues())
|
||||
->execute();
|
||||
|
||||
$rq->bindColumn($column, $fd, PDO::PARAM_LOB);
|
||||
$rq->fetch(PDO::FETCH_BOUND);
|
||||
|
||||
return $fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch large object as string
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return string
|
||||
*/
|
||||
public function findOneColumnAsString($column)
|
||||
{
|
||||
$fd = $this->findOneColumnAsStream($column);
|
||||
|
||||
if (is_string($fd)) {
|
||||
return $fd;
|
||||
}
|
||||
|
||||
return stream_get_contents($fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert large object from stream
|
||||
*
|
||||
* @access public
|
||||
* @param string $blobColumn
|
||||
* @param resource|string $blobDescriptor
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function insertFromStream($blobColumn, &$blobDescriptor, array $data = array())
|
||||
{
|
||||
$columns = array_merge(array($blobColumn), array_keys($data));
|
||||
$this->db->startTransaction();
|
||||
|
||||
$result = $this->db->getStatementHandler()
|
||||
->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder)
|
||||
->withTable($this->name)
|
||||
->withColumns($columns)
|
||||
->build()
|
||||
)
|
||||
->withNamedParams($data)
|
||||
->withLobParam($blobColumn, $blobDescriptor)
|
||||
->execute();
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert large object from file
|
||||
*
|
||||
* @access public
|
||||
* @param string $blobColumn
|
||||
* @param string $filename
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function insertFromFile($blobColumn, $filename, array $data = array())
|
||||
{
|
||||
$fp = fopen($filename, 'rb');
|
||||
$result = $this->insertFromStream($blobColumn, $fp, $data);
|
||||
fclose($fp);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert large object from string
|
||||
*
|
||||
* @access public
|
||||
* @param string $blobColumn
|
||||
* @param string $blobData
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function insertFromString($blobColumn, &$blobData, array $data = array())
|
||||
{
|
||||
return $this->insertFromStream($blobColumn, $blobData, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update large object from stream
|
||||
*
|
||||
* @access public
|
||||
* @param string $blobColumn
|
||||
* @param resource $blobDescriptor
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function updateFromStream($blobColumn, &$blobDescriptor, array $data = array())
|
||||
{
|
||||
$values = array_merge(array_values($data), $this->conditionBuilder->getValues());
|
||||
$columns = array_merge(array($blobColumn), array_keys($data));
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
$result = $this->db->getStatementHandler()
|
||||
->withSql(UpdateBuilder::getInstance($this->db, $this->conditionBuilder)
|
||||
->withTable($this->name)
|
||||
->withColumns($columns)
|
||||
->build()
|
||||
)
|
||||
->withPositionalParams($values)
|
||||
->withLobParam($blobColumn, $blobDescriptor)
|
||||
->execute();
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $result !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update large object from file
|
||||
*
|
||||
* @access public
|
||||
* @param string $blobColumn
|
||||
* @param string $filename
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function updateFromFile($blobColumn, $filename, array $data = array())
|
||||
{
|
||||
$fp = fopen($filename, 'r');
|
||||
$result = $this->updateFromStream($blobColumn, $fp, $data);
|
||||
fclose($fp);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
15
libs/picodb/lib/PicoDb/SQLException.php
Normal file
15
libs/picodb/lib/PicoDb/SQLException.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* SQLException
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SQLException extends Exception
|
||||
{
|
||||
}
|
||||
119
libs/picodb/lib/PicoDb/Schema.php
Normal file
119
libs/picodb/lib/PicoDb/Schema.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use PDOException;
|
||||
|
||||
/**
|
||||
* Schema migration class
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Schema
|
||||
{
|
||||
/**
|
||||
* Database instance
|
||||
*
|
||||
* @access protected
|
||||
* @var Database
|
||||
*/
|
||||
protected $db = null;
|
||||
|
||||
/**
|
||||
* Schema namespace
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = '\Schema';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set another namespace
|
||||
*
|
||||
* @access public
|
||||
* @param string $namespace
|
||||
* @return Schema
|
||||
*/
|
||||
public function setNamespace($namespace)
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get schema namespace
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getNamespace()
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the schema version and run the migrations
|
||||
*
|
||||
* @access public
|
||||
* @param integer $last_version
|
||||
* @return boolean
|
||||
*/
|
||||
public function check($last_version = 1)
|
||||
{
|
||||
$current_version = $this->db->getDriver()->getSchemaVersion();
|
||||
|
||||
if ($current_version < $last_version) {
|
||||
return $this->migrateTo($current_version, $last_version);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate the schema to one version to another
|
||||
*
|
||||
* @access public
|
||||
* @param integer $current_version
|
||||
* @param integer $next_version
|
||||
* @return boolean
|
||||
*/
|
||||
public function migrateTo($current_version, $next_version)
|
||||
{
|
||||
try {
|
||||
for ($i = $current_version + 1; $i <= $next_version; $i++) {
|
||||
$this->db->startTransaction();
|
||||
$this->db->getDriver()->disableForeignKeys();
|
||||
|
||||
$function_name = $this->getNamespace().'\version_'.$i;
|
||||
|
||||
if (function_exists($function_name)) {
|
||||
$this->db->setLogMessage('Running migration '.$function_name);
|
||||
call_user_func($function_name, $this->db->getConnection());
|
||||
}
|
||||
|
||||
$this->db->getDriver()->setSchemaVersion($i);
|
||||
$this->db->getDriver()->enableForeignKeys();
|
||||
$this->db->closeTransaction();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$this->db->setLogMessage($e->getMessage());
|
||||
$this->db->cancelTransaction();
|
||||
$this->db->getDriver()->enableForeignKeys();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
353
libs/picodb/lib/PicoDb/StatementHandler.php
Normal file
353
libs/picodb/lib/PicoDb/StatementHandler.php
Normal file
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use PDOStatement;
|
||||
|
||||
/**
|
||||
* Statement Handler
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class StatementHandler
|
||||
{
|
||||
/**
|
||||
* Database instance
|
||||
*
|
||||
* @access protected
|
||||
* @var Database
|
||||
*/
|
||||
protected $db = null;
|
||||
|
||||
/**
|
||||
* Flag to calculate query time
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $stopwatch = false;
|
||||
|
||||
/**
|
||||
* Start time
|
||||
*
|
||||
* @access protected
|
||||
* @var float
|
||||
*/
|
||||
protected $startTime = 0;
|
||||
|
||||
/**
|
||||
* Execution time of all queries
|
||||
*
|
||||
* @access protected
|
||||
* @var float
|
||||
*/
|
||||
protected $executionTime = 0;
|
||||
|
||||
/**
|
||||
* Flag to log generated SQL queries
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $logQueries = false;
|
||||
|
||||
/**
|
||||
* Run explain command on each query
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $explain = false;
|
||||
|
||||
/**
|
||||
* Number of SQL queries executed
|
||||
*
|
||||
* @access protected
|
||||
* @var integer
|
||||
*/
|
||||
protected $nbQueries = 0;
|
||||
|
||||
/**
|
||||
* SQL query
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $sql = '';
|
||||
|
||||
/**
|
||||
* Positional SQL parameters
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $positionalParams = array();
|
||||
|
||||
/**
|
||||
* Named SQL parameters
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $namedParams = array();
|
||||
|
||||
/**
|
||||
* Flag to use named params
|
||||
*
|
||||
* @access protected
|
||||
* @var boolean
|
||||
*/
|
||||
protected $useNamedParams = false;
|
||||
|
||||
/**
|
||||
* LOB params
|
||||
*
|
||||
* @access protected
|
||||
* @var array
|
||||
*/
|
||||
protected $lobParams = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Database $db
|
||||
*/
|
||||
public function __construct(Database $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable query logging
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withLogging()
|
||||
{
|
||||
$this->logQueries = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record query execution time
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withStopWatch()
|
||||
{
|
||||
$this->stopwatch = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute explain command on query
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function withExplain()
|
||||
{
|
||||
$this->explain = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set SQL query
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql
|
||||
* @return $this
|
||||
*/
|
||||
public function withSql($sql)
|
||||
{
|
||||
$this->sql = $sql;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set positional parameters
|
||||
*
|
||||
* @access public
|
||||
* @param array $params
|
||||
* @return $this
|
||||
*/
|
||||
public function withPositionalParams(array $params)
|
||||
{
|
||||
$this->positionalParams = $params;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set named parameters
|
||||
*
|
||||
* @access public
|
||||
* @param array $params
|
||||
* @return $this
|
||||
*/
|
||||
public function withNamedParams(array $params)
|
||||
{
|
||||
$this->namedParams = $params;
|
||||
$this->useNamedParams = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind large object parameter
|
||||
*
|
||||
* @access public
|
||||
* @param $name
|
||||
* @param $fp
|
||||
* @return $this
|
||||
*/
|
||||
public function withLobParam($name, &$fp)
|
||||
{
|
||||
$this->lobParams[$name] =& $fp;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of queries executed
|
||||
*
|
||||
* @access public
|
||||
* @return int
|
||||
*/
|
||||
public function getNbQueries()
|
||||
{
|
||||
return $this->nbQueries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a prepared statement
|
||||
*
|
||||
* Note: returns false on duplicate keys instead of SQLException
|
||||
*
|
||||
* @access public
|
||||
* @return PDOStatement|false
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
try {
|
||||
$this->beforeExecute();
|
||||
|
||||
$pdoStatement = $this->db->getConnection()->prepare($this->sql);
|
||||
$this->bindParams($pdoStatement);
|
||||
$pdoStatement->execute();
|
||||
|
||||
$this->afterExecute();
|
||||
return $pdoStatement;
|
||||
} catch (PDOException $e) {
|
||||
return $this->handleSqlError($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind parameters to PDOStatement
|
||||
*
|
||||
* @access protected
|
||||
* @param PDOStatement $pdoStatement
|
||||
*/
|
||||
protected function bindParams(PDOStatement $pdoStatement)
|
||||
{
|
||||
$i = 1;
|
||||
|
||||
foreach ($this->lobParams as $name => $variable) {
|
||||
if (! $this->useNamedParams) {
|
||||
$parameter = $i;
|
||||
$i++;
|
||||
} else {
|
||||
$parameter = $name;
|
||||
}
|
||||
|
||||
$pdoStatement->bindParam($parameter, $variable, PDO::PARAM_LOB);
|
||||
}
|
||||
|
||||
foreach ($this->positionalParams as $value) {
|
||||
$pdoStatement->bindValue($i, $value, PDO::PARAM_STR);
|
||||
$i++;
|
||||
}
|
||||
|
||||
foreach ($this->namedParams as $name => $value) {
|
||||
$pdoStatement->bindValue($name, $value, PDO::PARAM_STR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method executed before query execution
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function beforeExecute()
|
||||
{
|
||||
if ($this->logQueries) {
|
||||
$this->db->setLogMessage($this->sql);
|
||||
}
|
||||
|
||||
if ($this->stopwatch) {
|
||||
$this->startTime = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method executed after query execution
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function afterExecute()
|
||||
{
|
||||
if ($this->stopwatch) {
|
||||
$duration = microtime(true) - $this->startTime;
|
||||
$this->executionTime += $duration;
|
||||
$this->db->setLogMessage('query_duration='.$duration);
|
||||
$this->db->setLogMessage('total_execution_time='.$this->executionTime);
|
||||
}
|
||||
|
||||
if ($this->explain) {
|
||||
$this->db->setLogMessages($this->db->getDriver()->explain($this->sql, $this->positionalParams));
|
||||
}
|
||||
|
||||
$this->nbQueries++;
|
||||
$this->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset internal properties after execution
|
||||
* The same object instance is used
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function cleanup()
|
||||
{
|
||||
$this->sql = '';
|
||||
$this->useNamedParams = false;
|
||||
$this->positionalParams = array();
|
||||
$this->namedParams = array();
|
||||
$this->lobParams = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle PDOException
|
||||
*
|
||||
* @access public
|
||||
* @param PDOException $e
|
||||
* @return bool
|
||||
* @throws SQLException
|
||||
*/
|
||||
public function handleSqlError(PDOException $e)
|
||||
{
|
||||
$this->cleanup();
|
||||
$this->db->cancelTransaction();
|
||||
$this->db->setLogMessage($e->getMessage());
|
||||
|
||||
if ($this->db->getDriver()->isDuplicateKeyError($e->getCode())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
throw new SQLException('SQL Error: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
729
libs/picodb/lib/PicoDb/Table.php
Normal file
729
libs/picodb/lib/PicoDb/Table.php
Normal file
@@ -0,0 +1,729 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
use PDO;
|
||||
use Closure;
|
||||
use PicoDb\Builder\ConditionBuilder;
|
||||
use PicoDb\Builder\InsertBuilder;
|
||||
use PicoDb\Builder\UpdateBuilder;
|
||||
|
||||
/**
|
||||
* Table
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @method $this addCondition($sql)
|
||||
* @method $this beginOr()
|
||||
* @method $this closeOr()
|
||||
* @method $this eq($column, $value)
|
||||
* @method $this neq($column, $value)
|
||||
* @method $this in($column, array $values)
|
||||
* @method $this inSubquery($column, Table $subquery)
|
||||
* @method $this notIn($column, array $values)
|
||||
* @method $this notInSubquery($column, Table $subquery)
|
||||
* @method $this like($column, $value)
|
||||
* @method $this ilike($column, $value)
|
||||
* @method $this gt($column, $value)
|
||||
* @method $this gtSubquery($column, Table $subquery)
|
||||
* @method $this lt($column, $value)
|
||||
* @method $this ltSubquery($column, Table $subquery)
|
||||
* @method $this gte($column, $value)
|
||||
* @method $this gteSubquery($column, Table $subquery)
|
||||
* @method $this lte($column, $value)
|
||||
* @method $this lteSubquery($column, Table $subquery)
|
||||
* @method $this isNull($column)
|
||||
* @method $this notNull($column)
|
||||
*/
|
||||
class Table
|
||||
{
|
||||
/**
|
||||
* Sorting direction
|
||||
*
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
const SORT_ASC = 'ASC';
|
||||
const SORT_DESC = 'DESC';
|
||||
|
||||
/**
|
||||
* Condition instance
|
||||
*
|
||||
* @access protected
|
||||
* @var ConditionBuilder
|
||||
*/
|
||||
protected $conditionBuilder;
|
||||
|
||||
/**
|
||||
* Database instance
|
||||
*
|
||||
* @access protected
|
||||
* @var Database
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Table name
|
||||
*
|
||||
* @access protected
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* Columns list for SELECT query
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $columns = array();
|
||||
|
||||
/**
|
||||
* Columns to sum during update
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $sumColumns = array();
|
||||
|
||||
/**
|
||||
* SQL limit
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $sqlLimit = '';
|
||||
|
||||
/**
|
||||
* SQL offset
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $sqlOffset = '';
|
||||
|
||||
/**
|
||||
* SQL order
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $sqlOrder = '';
|
||||
|
||||
/**
|
||||
* SQL custom SELECT value
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $sqlSelect = '';
|
||||
|
||||
/**
|
||||
* SQL joins
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $joins = array();
|
||||
|
||||
/**
|
||||
* Use DISTINCT or not?
|
||||
*
|
||||
* @access private
|
||||
* @var boolean
|
||||
*/
|
||||
private $distinct = false;
|
||||
|
||||
/**
|
||||
* Group by those columns
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $groupBy = array();
|
||||
|
||||
/**
|
||||
* Callback for result filtering
|
||||
*
|
||||
* @access private
|
||||
* @var Closure
|
||||
*/
|
||||
private $callback = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Database $db
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct(Database $db, $name)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->name = $name;
|
||||
$this->conditionBuilder = new ConditionBuilder($db);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the table name
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return ConditionBuilder object
|
||||
*
|
||||
* @access public
|
||||
* @return ConditionBuilder
|
||||
*/
|
||||
public function getConditionBuilder()
|
||||
{
|
||||
return $this->conditionBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update
|
||||
*
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @return boolean
|
||||
*/
|
||||
public function save(array $data)
|
||||
{
|
||||
return $this->conditionBuilder->hasCondition() ? $this->update($data) : $this->insert($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update
|
||||
*
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $data = array())
|
||||
{
|
||||
$values = array_merge(array_values($data), array_values($this->sumColumns), $this->conditionBuilder->getValues());
|
||||
$sql = UpdateBuilder::getInstance($this->db, $this->conditionBuilder)
|
||||
->withTable($this->name)
|
||||
->withColumns(array_keys($data))
|
||||
->withSumColumns(array_keys($this->sumColumns))
|
||||
->build();
|
||||
|
||||
return $this->db->execute($sql, $values) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert
|
||||
*
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @return boolean
|
||||
*/
|
||||
public function insert(array $data)
|
||||
{
|
||||
return $this->db->getStatementHandler()
|
||||
->withSql(InsertBuilder::getInstance($this->db, $this->conditionBuilder)
|
||||
->withTable($this->name)
|
||||
->withColumns(array_keys($data))
|
||||
->build()
|
||||
)
|
||||
->withNamedParams($data)
|
||||
->execute() !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new row and return the ID of the primary key
|
||||
*
|
||||
* @access public
|
||||
* @param array $data
|
||||
* @return bool|int
|
||||
*/
|
||||
public function persist(array $data)
|
||||
{
|
||||
if ($this->insert($data)) {
|
||||
return $this->db->getLastId();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$sql = sprintf(
|
||||
'DELETE FROM %s %s',
|
||||
$this->db->escapeIdentifier($this->name),
|
||||
$this->conditionBuilder->build()
|
||||
);
|
||||
|
||||
$result = $this->db->execute($sql, $this->conditionBuilder->getValues());
|
||||
return $result->rowCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all rows
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function findAll()
|
||||
{
|
||||
$rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
|
||||
$results = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (is_callable($this->callback) && ! empty($results)) {
|
||||
return call_user_func($this->callback, $results);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all with a single column
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return mixed
|
||||
*/
|
||||
public function findAllByColumn($column)
|
||||
{
|
||||
$this->columns = array($column);
|
||||
$rq = $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues());
|
||||
|
||||
return $rq->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one row
|
||||
*
|
||||
* @access public
|
||||
* @return array|null
|
||||
*/
|
||||
public function findOne()
|
||||
{
|
||||
$this->limit(1);
|
||||
$result = $this->findAll();
|
||||
|
||||
return isset($result[0]) ? $result[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one column, first row
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return string
|
||||
*/
|
||||
public function findOneColumn($column)
|
||||
{
|
||||
$this->limit(1);
|
||||
$this->columns = array($column);
|
||||
|
||||
return $this->db->execute($this->buildSelectQuery(), $this->conditionBuilder->getValues())->fetchColumn();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a subquery with an alias
|
||||
*
|
||||
* @access public
|
||||
* @param string $sql
|
||||
* @param string $alias
|
||||
* @return $this
|
||||
*/
|
||||
public function subquery($sql, $alias)
|
||||
{
|
||||
$this->columns[] = '('.$sql.') AS '.$this->db->escapeIdentifier($alias);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
$sql = sprintf(
|
||||
'SELECT 1 FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build(),
|
||||
$this->db->escapeIdentifier($this->name)
|
||||
);
|
||||
|
||||
$rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
|
||||
$result = $rq->fetchColumn();
|
||||
|
||||
return $result ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
$sql = sprintf(
|
||||
'SELECT COUNT(*) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
|
||||
$this->db->escapeIdentifier($this->name)
|
||||
);
|
||||
|
||||
$rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
|
||||
$result = $rq->fetchColumn();
|
||||
|
||||
return $result ? (int) $result : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return float
|
||||
*/
|
||||
public function sum($column)
|
||||
{
|
||||
$sql = sprintf(
|
||||
'SELECT SUM(%s) FROM %s '.implode(' ', $this->joins).$this->conditionBuilder->build().$this->sqlOrder.$this->sqlLimit.$this->sqlOffset,
|
||||
$this->db->escapeIdentifier($column),
|
||||
$this->db->escapeIdentifier($this->name)
|
||||
);
|
||||
|
||||
$rq = $this->db->execute($sql, $this->conditionBuilder->getValues());
|
||||
$result = $rq->fetchColumn();
|
||||
|
||||
return $result ? (float) $result : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment column value
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
* @return boolean
|
||||
*/
|
||||
public function increment($column, $value)
|
||||
{
|
||||
$sql = sprintf(
|
||||
'UPDATE %s SET %s=%s+%d '.$this->conditionBuilder->build(),
|
||||
$this->db->escapeIdentifier($this->name),
|
||||
$this->db->escapeIdentifier($column),
|
||||
$this->db->escapeIdentifier($column),
|
||||
$value
|
||||
);
|
||||
|
||||
return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement column value
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param string $value
|
||||
* @return boolean
|
||||
*/
|
||||
public function decrement($column, $value)
|
||||
{
|
||||
$sql = sprintf(
|
||||
'UPDATE %s SET %s=%s-%d '.$this->conditionBuilder->build(),
|
||||
$this->db->escapeIdentifier($this->name),
|
||||
$this->db->escapeIdentifier($column),
|
||||
$this->db->escapeIdentifier($column),
|
||||
$value
|
||||
);
|
||||
|
||||
return $this->db->execute($sql, $this->conditionBuilder->getValues()) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left join
|
||||
*
|
||||
* @access public
|
||||
* @param string $table Join table
|
||||
* @param string $foreign_column Foreign key on the join table
|
||||
* @param string $local_column Local column
|
||||
* @param string $local_table Local table
|
||||
* @param string $alias Join table alias
|
||||
* @return $this
|
||||
*/
|
||||
public function join($table, $foreign_column, $local_column, $local_table = '', $alias = '')
|
||||
{
|
||||
$this->joins[] = sprintf(
|
||||
'LEFT JOIN %s ON %s=%s',
|
||||
$this->db->escapeIdentifier($table),
|
||||
$this->db->escapeIdentifier($alias ?: $table).'.'.$this->db->escapeIdentifier($foreign_column),
|
||||
$this->db->escapeIdentifier($local_table ?: $this->name).'.'.$this->db->escapeIdentifier($local_column)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Left join
|
||||
*
|
||||
* @access public
|
||||
* @param string $table1
|
||||
* @param string $alias1
|
||||
* @param string $column1
|
||||
* @param string $table2
|
||||
* @param string $column2
|
||||
* @return $this
|
||||
*/
|
||||
public function left($table1, $alias1, $column1, $table2, $column2)
|
||||
{
|
||||
$this->joins[] = sprintf(
|
||||
'LEFT JOIN %s AS %s ON %s=%s',
|
||||
$this->db->escapeIdentifier($table1),
|
||||
$this->db->escapeIdentifier($alias1),
|
||||
$this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
|
||||
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner join
|
||||
*
|
||||
* @access public
|
||||
* @param string $table1
|
||||
* @param string $alias1
|
||||
* @param string $column1
|
||||
* @param string $table2
|
||||
* @param string $column2
|
||||
* @return $this
|
||||
*/
|
||||
public function inner($table1, $alias1, $column1, $table2, $column2)
|
||||
{
|
||||
$this->joins[] = sprintf(
|
||||
'JOIN %s AS %s ON %s=%s',
|
||||
$this->db->escapeIdentifier($table1),
|
||||
$this->db->escapeIdentifier($alias1),
|
||||
$this->db->escapeIdentifier($alias1).'.'.$this->db->escapeIdentifier($column1),
|
||||
$this->db->escapeIdentifier($table2).'.'.$this->db->escapeIdentifier($column2)
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order by
|
||||
*
|
||||
* @access public
|
||||
* @param string $column Column name
|
||||
* @param string $order Direction ASC or DESC
|
||||
* @return $this
|
||||
*/
|
||||
public function orderBy($column, $order = self::SORT_ASC)
|
||||
{
|
||||
$order = strtoupper($order);
|
||||
$order = $order === self::SORT_ASC || $order === self::SORT_DESC ? $order : self::SORT_ASC;
|
||||
|
||||
if ($this->sqlOrder === '') {
|
||||
$this->sqlOrder = ' ORDER BY '.$this->db->escapeIdentifier($column).' '.$order;
|
||||
}
|
||||
else {
|
||||
$this->sqlOrder .= ', '.$this->db->escapeIdentifier($column).' '.$order;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ascending sort
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return $this
|
||||
*/
|
||||
public function asc($column)
|
||||
{
|
||||
$this->orderBy($column, self::SORT_ASC);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Descending sort
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @return $this
|
||||
*/
|
||||
public function desc($column)
|
||||
{
|
||||
$this->orderBy($column, self::SORT_DESC);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit
|
||||
*
|
||||
* @access public
|
||||
* @param integer $value
|
||||
* @return $this
|
||||
*/
|
||||
public function limit($value)
|
||||
{
|
||||
if (! is_null($value)) {
|
||||
$this->sqlLimit = ' LIMIT '.(int) $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset
|
||||
*
|
||||
* @access public
|
||||
* @param integer $value
|
||||
* @return $this
|
||||
*/
|
||||
public function offset($value)
|
||||
{
|
||||
if (! is_null($value)) {
|
||||
$this->sqlOffset = ' OFFSET '.(int) $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group by
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function groupBy()
|
||||
{
|
||||
$this->groupBy = func_get_args();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom select
|
||||
*
|
||||
* @access public
|
||||
* @param string $select
|
||||
* @return $this
|
||||
*/
|
||||
public function select($select)
|
||||
{
|
||||
$this->sqlSelect = $select;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the columns for the select
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function columns()
|
||||
{
|
||||
$this->columns = func_get_args();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sum column
|
||||
*
|
||||
* @access public
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function sumColumn($column, $value)
|
||||
{
|
||||
$this->sumColumns[$column] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Distinct
|
||||
*
|
||||
* @access public
|
||||
* @return $this
|
||||
*/
|
||||
public function distinct()
|
||||
{
|
||||
$this->columns = func_get_args();
|
||||
$this->distinct = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add callback to alter the resultset
|
||||
*
|
||||
* @access public
|
||||
* @param Closure|array $callback
|
||||
* @return $this
|
||||
*/
|
||||
public function callback($callback)
|
||||
{
|
||||
$this->callback = $callback;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a select query
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function buildSelectQuery()
|
||||
{
|
||||
if (empty($this->sqlSelect)) {
|
||||
$this->columns = $this->db->escapeIdentifierList($this->columns);
|
||||
$this->sqlSelect = ($this->distinct ? 'DISTINCT ' : '').(empty($this->columns) ? '*' : implode(', ', $this->columns));
|
||||
}
|
||||
|
||||
$this->groupBy = $this->db->escapeIdentifierList($this->groupBy);
|
||||
|
||||
return trim(sprintf(
|
||||
'SELECT %s FROM %s %s %s %s %s %s %s',
|
||||
$this->sqlSelect,
|
||||
$this->db->escapeIdentifier($this->name),
|
||||
implode(' ', $this->joins),
|
||||
$this->conditionBuilder->build(),
|
||||
empty($this->groupBy) ? '' : 'GROUP BY '.implode(', ', $this->groupBy),
|
||||
$this->sqlOrder,
|
||||
$this->sqlLimit,
|
||||
$this->sqlOffset
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method for sql conditions
|
||||
*
|
||||
* @access public
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($name, array $arguments)
|
||||
{
|
||||
call_user_func_array(array($this->conditionBuilder, $name), $arguments);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone function ensures that cloned objects are really clones
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->conditionBuilder = clone $this->conditionBuilder;
|
||||
}
|
||||
}
|
||||
93
libs/picodb/lib/PicoDb/UrlParser.php
Normal file
93
libs/picodb/lib/PicoDb/UrlParser.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace PicoDb;
|
||||
|
||||
/**
|
||||
* Class UrlParser
|
||||
*
|
||||
* @package PicoDb
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class UrlParser
|
||||
{
|
||||
/**
|
||||
* URL
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param string $environmentVariable
|
||||
*/
|
||||
public function __construct($environmentVariable = 'DATABASE_URL')
|
||||
{
|
||||
$this->url = getenv($environmentVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object instance
|
||||
*
|
||||
* @access public
|
||||
* @param string $environmentVariable
|
||||
* @return static
|
||||
*/
|
||||
public static function getInstance($environmentVariable = 'DATABASE_URL')
|
||||
{
|
||||
return new static($environmentVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the variable is defined
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnvironmentVariableDefined()
|
||||
{
|
||||
return ! empty($this->url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings from URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @return array
|
||||
*/
|
||||
public function getSettings($url = '')
|
||||
{
|
||||
$url = $url ?: $this->url;
|
||||
$components = parse_url($url);
|
||||
|
||||
if ($components === false) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array(
|
||||
'driver' => $this->getUrlComponent($components, 'scheme'),
|
||||
'username' => $this->getUrlComponent($components, 'user'),
|
||||
'password' => $this->getUrlComponent($components, 'pass'),
|
||||
'hostname' => $this->getUrlComponent($components, 'host'),
|
||||
'port' => $this->getUrlComponent($components, 'port'),
|
||||
'database' => ltrim($this->getUrlComponent($components, 'path'), '/'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL component
|
||||
*
|
||||
* @access private
|
||||
* @param array $components
|
||||
* @param string $component
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function getUrlComponent(array $components, $component)
|
||||
{
|
||||
return ! empty($components[$component]) ? $components[$component] : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user