Initial commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<?php namespace FluentMail\App\Services\DB;
|
||||
|
||||
use FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler;
|
||||
|
||||
/**
|
||||
* This class gives the ability to access non-static methods statically
|
||||
*
|
||||
* Class AliasFacade
|
||||
*
|
||||
* @package FluentAuthDb
|
||||
*/
|
||||
class AliasFacade
|
||||
{
|
||||
|
||||
/**
|
||||
* @var QueryBuilderHandler
|
||||
*/
|
||||
protected static $queryBuilderInstance;
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $args
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
if (!static::$queryBuilderInstance) {
|
||||
static::$queryBuilderInstance = new QueryBuilderHandler();
|
||||
}
|
||||
|
||||
// Call the non-static method from the class instance
|
||||
return call_user_func_array(array(static::$queryBuilderInstance, $method), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param QueryBuilderHandler $queryBuilderInstance
|
||||
*/
|
||||
public static function setQueryBuilderInstance($queryBuilderInstance)
|
||||
{
|
||||
static::$queryBuilderInstance = $queryBuilderInstance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
namespace FluentMail\App\Services\DB;
|
||||
|
||||
use FluentMail\App\Services\DB\Viocon\Container;
|
||||
|
||||
class Connection
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $adapter;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $adapterConfig;
|
||||
|
||||
/**
|
||||
* @var \wpdb $wpdb
|
||||
*/
|
||||
protected $dbInstance;
|
||||
|
||||
/**
|
||||
* @var \wpdb $wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
protected static $storedConnection;
|
||||
|
||||
/**
|
||||
* @var EventHandler
|
||||
*/
|
||||
protected $eventHandler;
|
||||
|
||||
/**
|
||||
* @param $wpdb
|
||||
* @param array $adapterConfig
|
||||
* @param null|string $alias
|
||||
* @param null|Container $container
|
||||
*/
|
||||
public function __construct($wpdb, array $config = array(), $alias = null, ?Container $container = null)
|
||||
{
|
||||
$container = $container ? : new Container();
|
||||
|
||||
$this->container = $container;
|
||||
|
||||
$this->wpdb = $wpdb;
|
||||
|
||||
$this->setAdapter()->setAdapterConfig($config)->connect();
|
||||
|
||||
// Create event dependency
|
||||
$this->eventHandler = $this->container->build('\\FluentMail\\App\\Services\\DB\\EventHandler');
|
||||
|
||||
if ($alias) {
|
||||
$this->createAlias($alias);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an easily accessible query builder alias
|
||||
*
|
||||
* @param $alias
|
||||
*/
|
||||
public function createAlias($alias)
|
||||
{
|
||||
class_alias('FluentMail\\App\\Services\\DB\\AliasFacade', $alias);
|
||||
|
||||
$builder = $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryBuilderHandler', array($this));
|
||||
|
||||
AliasFacade::setQueryBuilderInstance($builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of Query Builder
|
||||
*/
|
||||
public function getQueryBuilder()
|
||||
{
|
||||
return $this->container->build('\\FluentMail\\App\\Services\\DB\\QueryBuilder\\QueryBuilderHandler', array($this));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the connection adapter
|
||||
*/
|
||||
protected function connect()
|
||||
{
|
||||
$this->setDbInstance($this->wpdb);
|
||||
|
||||
// Preserve the first database connection with a static property
|
||||
if (! static::$storedConnection) {
|
||||
static::$storedConnection = $this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $db
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDbInstance($db)
|
||||
{
|
||||
$this->dbInstance = $db;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \wpdb
|
||||
*/
|
||||
public function getDbInstance()
|
||||
{
|
||||
return $this->dbInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $adapter
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAdapter($adapter = 'mysql')
|
||||
{
|
||||
$this->adapter = $adapter;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAdapter()
|
||||
{
|
||||
return $this->adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $adapterConfig
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAdapterConfig(array $adapterConfig)
|
||||
{
|
||||
$this->adapterConfig = $adapterConfig;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAdapterConfig()
|
||||
{
|
||||
return $this->adapterConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FluentMail\App\Services\DB\Viocon\Container
|
||||
*/
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EventHandler
|
||||
*/
|
||||
public function getEventHandler()
|
||||
{
|
||||
return $this->eventHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Connection
|
||||
*/
|
||||
public static function getStoredConnection()
|
||||
{
|
||||
return static::$storedConnection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
<?php namespace FluentMail\App\Services\DB;
|
||||
|
||||
use FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler;
|
||||
use FluentMail\App\Services\DB\QueryBuilder\Raw;
|
||||
|
||||
class EventHandler
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $events = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $firedEvents = array();
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $event
|
||||
* @param $table
|
||||
*
|
||||
* @return callable|null
|
||||
*/
|
||||
public function getEvent($event, $table = ':any')
|
||||
{
|
||||
if ($table instanceof Raw) {
|
||||
return null;
|
||||
}
|
||||
return isset($this->events[$table][$event]) ? $this->events[$table][$event] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $event
|
||||
* @param string $table
|
||||
* @param callable $action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function registerEvent($event, $table, \Closure $action)
|
||||
{
|
||||
$table = $table ?: ':any';
|
||||
|
||||
$this->events[$table][$event] = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $event
|
||||
* @param string $table
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeEvent($event, $table = ':any')
|
||||
{
|
||||
unset($this->events[$table][$event]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \FluentMail\App\Services\DB\src\QueryBuilder\QueryBuilderHandler $queryBuilder
|
||||
* @param $event
|
||||
* @return mixed
|
||||
*/
|
||||
public function fireEvents($queryBuilder, $event)
|
||||
{
|
||||
$originalArgs = func_get_args();
|
||||
$statements = $queryBuilder->getStatements();
|
||||
$tables = isset($statements['tables']) ? $statements['tables'] : array();
|
||||
|
||||
// Events added with :any will be fired in case of any table,
|
||||
// we are adding :any as a fake table at the beginning.
|
||||
array_unshift($tables, ':any');
|
||||
|
||||
// Fire all events
|
||||
foreach ($tables as $table) {
|
||||
// Fire before events for :any table
|
||||
if ($action = $this->getEvent($event, $table)) {
|
||||
// Make an event id, with event type and table
|
||||
$eventId = $event . $table;
|
||||
|
||||
// Fire event
|
||||
$handlerParams = $originalArgs;
|
||||
unset($handlerParams[1]); // we do not need $event
|
||||
// Add to fired list
|
||||
$this->firedEvents[] = $eventId;
|
||||
$result = call_user_func_array($action, $handlerParams);
|
||||
if (!is_null($result)) {
|
||||
return $result;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php namespace FluentMail\App\Services\DB;
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Muhammad Usman
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,545 @@
|
||||
<?php namespace FluentMail\App\Services\DB\QueryBuilder\Adapters;
|
||||
|
||||
use FluentMail\App\Services\DB\Connection;
|
||||
use FluentMail\App\Services\DB\Exception;
|
||||
use FluentMail\App\Services\DB\QueryBuilder\Raw;
|
||||
|
||||
abstract class BaseAdapter
|
||||
{
|
||||
/**
|
||||
* @var \FluentMail\App\Services\DB\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* @var \FluentMail\App\Services\DB\Viocon\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->container = $this->connection->getContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build select query string and bindings
|
||||
*
|
||||
* @param $statements
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\Exception
|
||||
*/
|
||||
public function select($statements)
|
||||
{
|
||||
if (! array_key_exists('tables', $statements)) {
|
||||
throw new Exception('No table specified.', 3);
|
||||
} elseif (! array_key_exists('selects', $statements)) {
|
||||
$statements['selects'][] = '*';
|
||||
}
|
||||
|
||||
// From
|
||||
$tables = $this->arrayStr($statements['tables'], ', ');
|
||||
// Select
|
||||
$selects = $this->arrayStr($statements['selects'], ', ');
|
||||
|
||||
|
||||
// Wheres
|
||||
list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
|
||||
// Group bys
|
||||
$groupBys = '';
|
||||
if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) {
|
||||
$groupBys = 'GROUP BY ' . $groupBys;
|
||||
}
|
||||
|
||||
// Order bys
|
||||
$orderBys = '';
|
||||
if (isset($statements['orderBys']) && is_array($statements['orderBys'])) {
|
||||
foreach ($statements['orderBys'] as $orderBy) {
|
||||
$orderBys .= $this->wrapSanitizer($orderBy['field']) . ' ' . $orderBy['type'] . ', ';
|
||||
}
|
||||
|
||||
if ($orderBys = trim($orderBys, ', ')) {
|
||||
$orderBys = 'ORDER BY ' . $orderBys;
|
||||
}
|
||||
}
|
||||
|
||||
// Limit and offset
|
||||
$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
|
||||
$offset = isset($statements['offset']) ? 'OFFSET ' . $statements['offset'] : '';
|
||||
|
||||
// Having
|
||||
list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING');
|
||||
|
||||
// Joins
|
||||
$joinString = $this->buildJoin($statements);
|
||||
|
||||
$sqlArray = array(
|
||||
'SELECT' . (isset($statements['distinct']) ? ' DISTINCT' : ''),
|
||||
$selects,
|
||||
'FROM',
|
||||
$tables,
|
||||
$joinString,
|
||||
$whereCriteria,
|
||||
$groupBys,
|
||||
$havingCriteria,
|
||||
$orderBys,
|
||||
$limit,
|
||||
$offset
|
||||
);
|
||||
|
||||
$sql = $this->concatenateQuery($sqlArray);
|
||||
|
||||
$bindings = array_merge(
|
||||
$whereBindings,
|
||||
$havingBindings
|
||||
);
|
||||
|
||||
return compact('sql', 'bindings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build just criteria part of the query
|
||||
*
|
||||
* @param $statements
|
||||
* @param bool $bindValues
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function criteriaOnly($statements, $bindValues = true)
|
||||
{
|
||||
$sql = $bindings = array();
|
||||
|
||||
if (! isset($statements['criteria'])) {
|
||||
return compact('sql', 'bindings');
|
||||
}
|
||||
|
||||
list($sql, $bindings) = $this->buildCriteria($statements['criteria'], $bindValues);
|
||||
|
||||
return compact('sql', 'bindings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a generic insert/ignore/replace query
|
||||
*
|
||||
* @param $statements
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\Exception
|
||||
*/
|
||||
private function doInsert($statements, array $data, $type)
|
||||
{
|
||||
if (! isset($statements['tables'])) {
|
||||
throw new Exception('No table specified', 3);
|
||||
}
|
||||
|
||||
$table = end($statements['tables']);
|
||||
|
||||
$bindings = $keys = $values = array();
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$keys[] = $key;
|
||||
if ($value instanceof Raw) {
|
||||
$values[] = (string) $value;
|
||||
} else {
|
||||
$values[] = '?';
|
||||
$bindings[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$sqlArray = array(
|
||||
$type . ' INTO',
|
||||
$this->wrapSanitizer($table),
|
||||
'(' . $this->arrayStr($keys, ',') . ')',
|
||||
'VALUES',
|
||||
'(' . $this->arrayStr($values, ',', false) . ')',
|
||||
);
|
||||
|
||||
if (isset($statements['onduplicate'])) {
|
||||
if (count($statements['onduplicate']) < 1) {
|
||||
throw new Exception('No data given.', 4);
|
||||
}
|
||||
list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']);
|
||||
$sqlArray[] = 'ON DUPLICATE KEY UPDATE ' . $updateStatement;
|
||||
$bindings = array_merge($bindings, $updateBindings);
|
||||
}
|
||||
|
||||
$sql = $this->concatenateQuery($sqlArray);
|
||||
|
||||
return compact('sql', 'bindings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Insert query
|
||||
*
|
||||
* @param $statements
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\Exception
|
||||
*/
|
||||
public function insert($statements, array $data)
|
||||
{
|
||||
return $this->doInsert($statements, $data, 'INSERT');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Insert Ignore query
|
||||
*
|
||||
* @param $statements
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\Exception
|
||||
*/
|
||||
public function insertIgnore($statements, array $data)
|
||||
{
|
||||
return $this->doInsert($statements, $data, 'INSERT IGNORE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Insert Ignore query
|
||||
*
|
||||
* @param $statements
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\Exception
|
||||
*/
|
||||
public function replace($statements, array $data)
|
||||
{
|
||||
return $this->doInsert($statements, $data, 'REPLACE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getUpdateStatement($data)
|
||||
{
|
||||
$bindings = array();
|
||||
$statement = '';
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if ($value instanceof Raw) {
|
||||
$statement .= $this->wrapSanitizer($key) . '=' . $value . ',';
|
||||
} else {
|
||||
$statement .= $this->wrapSanitizer($key) . '=?,';
|
||||
$bindings[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$statement = trim($statement, ',');
|
||||
|
||||
return array($statement, $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build update query
|
||||
*
|
||||
* @param $statements
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\Exception
|
||||
*/
|
||||
public function update($statements, array $data)
|
||||
{
|
||||
if (! isset($statements['tables'])) {
|
||||
throw new Exception('No table specified', 3);
|
||||
} elseif (count($data) < 1) {
|
||||
throw new Exception('No data given.', 4);
|
||||
}
|
||||
|
||||
$table = end($statements['tables']);
|
||||
|
||||
// Update statement
|
||||
list($updateStatement, $bindings) = $this->getUpdateStatement($data);
|
||||
|
||||
// Wheres
|
||||
list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
|
||||
|
||||
// Limit
|
||||
$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
|
||||
|
||||
$sqlArray = array(
|
||||
'UPDATE',
|
||||
$this->wrapSanitizer($table),
|
||||
'SET ' . $updateStatement,
|
||||
$whereCriteria,
|
||||
$limit
|
||||
);
|
||||
|
||||
$sql = $this->concatenateQuery($sqlArray);
|
||||
|
||||
$bindings = array_merge($bindings, $whereBindings);
|
||||
|
||||
return compact('sql', 'bindings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build delete query
|
||||
*
|
||||
* @param $statements
|
||||
*
|
||||
* @return array
|
||||
* @throws \FluentMail\App\Services\DB\src\Exception
|
||||
*/
|
||||
public function delete($statements)
|
||||
{
|
||||
if (! isset($statements['tables'])) {
|
||||
throw new Exception('No table specified', 3);
|
||||
}
|
||||
|
||||
$table = end($statements['tables']);
|
||||
|
||||
// Wheres
|
||||
list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE');
|
||||
|
||||
// Limit
|
||||
$limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : '';
|
||||
|
||||
$sqlArray = array('DELETE FROM', $this->wrapSanitizer($table), $whereCriteria);
|
||||
$sql = $this->concatenateQuery($sqlArray);
|
||||
$bindings = $whereBindings;
|
||||
|
||||
return compact('sql', 'bindings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Array concatenating method, like implode.
|
||||
* But it does wrap sanitizer and trims last glue
|
||||
*
|
||||
* @param array $pieces
|
||||
* @param $glue
|
||||
* @param bool $wrapSanitizer
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function arrayStr(array $pieces, $glue, $wrapSanitizer = true)
|
||||
{
|
||||
$str = '';
|
||||
|
||||
foreach ($pieces as $key => $piece) {
|
||||
if ($wrapSanitizer) {
|
||||
$piece = $this->wrapSanitizer($piece);
|
||||
}
|
||||
|
||||
if (! is_int($key)) {
|
||||
$piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece;
|
||||
}
|
||||
|
||||
$str .= $piece . $glue;
|
||||
}
|
||||
|
||||
return trim($str, $glue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Join different part of queries with a space.
|
||||
*
|
||||
* @param array $pieces
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function concatenateQuery(array $pieces)
|
||||
{
|
||||
$str = '';
|
||||
|
||||
foreach ($pieces as $piece) {
|
||||
$str = trim($str) . ' ' . trim($piece);
|
||||
}
|
||||
|
||||
return trim($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build generic criteria string and bindings from statements, like "a = b and c = ?"
|
||||
*
|
||||
* @param $statements
|
||||
* @param bool $bindValues
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function buildCriteria($statements, $bindValues = true)
|
||||
{
|
||||
$criteria = '';
|
||||
$bindings = array();
|
||||
|
||||
foreach ($statements as $statement) {
|
||||
$key = $this->wrapSanitizer($statement['key']);
|
||||
$value = $statement['value'];
|
||||
|
||||
if (is_null($value) && $key instanceof \Closure) {
|
||||
// We have a closure, a nested criteria
|
||||
|
||||
// Build a new NestedCriteria class, keep it by reference so any changes made
|
||||
// in the closure should reflect here
|
||||
$nestedCriteria = $this->container->build(
|
||||
'\\FluentMail\\App\\Services\\DB\\QueryBuilder\\NestedCriteria',
|
||||
array($this->connection)
|
||||
);
|
||||
|
||||
$nestedCriteria = & $nestedCriteria;
|
||||
// Call the closure with our new nestedCriteria object
|
||||
$key($nestedCriteria);
|
||||
// Get the criteria only query from the nestedCriteria object
|
||||
$queryObject = $nestedCriteria->getQuery('criteriaOnly', true);
|
||||
// Merge the bindings we get from nestedCriteria object
|
||||
$bindings = array_merge($bindings, $queryObject->getBindings());
|
||||
// Append the sql we get from the nestedCriteria object
|
||||
$criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') ';
|
||||
} elseif (is_array($value)) {
|
||||
// where_in or between like query
|
||||
$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'];
|
||||
|
||||
switch ($statement['operator']) {
|
||||
case 'BETWEEN':
|
||||
$bindings = array_merge($bindings, $statement['value']);
|
||||
$criteria .= ' ? AND ? ';
|
||||
break;
|
||||
default:
|
||||
$valuePlaceholder = '';
|
||||
foreach ($statement['value'] as $subValue) {
|
||||
$valuePlaceholder .= '?, ';
|
||||
$bindings[] = $subValue;
|
||||
}
|
||||
|
||||
$valuePlaceholder = trim($valuePlaceholder, ', ');
|
||||
$criteria .= ' (' . $valuePlaceholder . ') ';
|
||||
break;
|
||||
}
|
||||
} elseif ($value instanceof Raw) {
|
||||
$criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value ";
|
||||
} else {
|
||||
// Usual where like criteria
|
||||
|
||||
if (! $bindValues) {
|
||||
// Specially for joins
|
||||
|
||||
// We are not binding values, lets sanitize then
|
||||
$value = $this->wrapSanitizer($value);
|
||||
$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' ';
|
||||
} elseif ($statement['key'] instanceof Raw) {
|
||||
$criteria .= $statement['joiner'] . ' ' . $key . ' ';
|
||||
$bindings = array_merge($bindings, $statement['key']->getBindings());
|
||||
} else {
|
||||
// For wheres
|
||||
|
||||
$valuePlaceholder = '?';
|
||||
$bindings[] = $value;
|
||||
$criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' '
|
||||
. $valuePlaceholder . ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all white spaces, and, or from beginning and white spaces from ending
|
||||
$criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria);
|
||||
|
||||
return array($criteria, $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap values with adapter's sanitizer like, '`'
|
||||
*
|
||||
* @param $value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function wrapSanitizer($value)
|
||||
{
|
||||
// Its a raw query, just cast as string, object has __toString()
|
||||
if ($value instanceof Raw) {
|
||||
return (string) $value;
|
||||
} elseif ($value instanceof \Closure) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Separate our table and fields which are joined with a ".",
|
||||
// like my_table.id
|
||||
$valueArr = explode('.', $value, 2);
|
||||
|
||||
foreach ($valueArr as $key => $subValue) {
|
||||
// Don't wrap if we have *, which is not a usual field
|
||||
$valueArr[$key] = trim($subValue) == '*' ? $subValue : $this->sanitizer . $subValue . $this->sanitizer;
|
||||
}
|
||||
|
||||
// Join these back with "." and return
|
||||
return implode('.', $valueArr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build criteria string and binding with various types added, like WHERE and Having
|
||||
*
|
||||
* @param $statements
|
||||
* @param $key
|
||||
* @param $type
|
||||
* @param bool $bindValues
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function buildCriteriaWithType($statements, $key, $type, $bindValues = true)
|
||||
{
|
||||
$criteria = '';
|
||||
$bindings = array();
|
||||
|
||||
if (isset($statements[$key])) {
|
||||
// Get the generic/adapter agnostic criteria string from parent
|
||||
list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues);
|
||||
|
||||
if ($criteria) {
|
||||
$criteria = $type . ' ' . $criteria;
|
||||
}
|
||||
}
|
||||
|
||||
return array($criteria, $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build join string
|
||||
*
|
||||
* @param $statements
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
protected function buildJoin($statements)
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
if (! array_key_exists('joins', $statements) || ! is_array($statements['joins'])) {
|
||||
return $sql;
|
||||
}
|
||||
|
||||
foreach ($statements['joins'] as $joinArr) {
|
||||
if (is_array($joinArr['table'])) {
|
||||
$mainTable = $joinArr['table'][0];
|
||||
$aliasTable = $joinArr['table'][1];
|
||||
$table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable);
|
||||
} else {
|
||||
$table = $joinArr['table'] instanceof Raw ?
|
||||
(string) $joinArr['table'] :
|
||||
$this->wrapSanitizer($joinArr['table']);
|
||||
}
|
||||
|
||||
$joinBuilder = $joinArr['joinBuilder'];
|
||||
|
||||
$sqlArr = array(
|
||||
$sql,
|
||||
strtoupper($joinArr['type']),
|
||||
'JOIN',
|
||||
$table,
|
||||
'ON',
|
||||
$joinBuilder->getQuery('criteriaOnly', false)->getSql()
|
||||
);
|
||||
|
||||
$sql = $this->concatenateQuery($sqlArr);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace FluentMail\App\Services\DB\QueryBuilder\Adapters;
|
||||
|
||||
class Mysql extends BaseAdapter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sanitizer = '`';
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php namespace FluentMail\App\Services\DB\QueryBuilder;
|
||||
|
||||
class JoinBuilder extends QueryBuilderHandler
|
||||
{
|
||||
/**
|
||||
* @param $key
|
||||
* @param $operator
|
||||
* @param $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function on($key, $operator, $value)
|
||||
{
|
||||
return $this->joinHandler($key, $operator, $value, 'AND');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $operator
|
||||
* @param $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function orOn($key, $operator, $value)
|
||||
{
|
||||
return $this->joinHandler($key, $operator, $value, 'OR');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $operator
|
||||
* @param null $value
|
||||
* @param string $joiner
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function joinHandler($key, $operator = null, $value = null, $joiner = 'AND')
|
||||
{
|
||||
$key = $this->addTablePrefix($key);
|
||||
$value = $this->addTablePrefix($value);
|
||||
$this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php namespace FluentMail\App\Services\DB\QueryBuilder;
|
||||
|
||||
class NestedCriteria extends QueryBuilderHandler
|
||||
{
|
||||
/**
|
||||
* @param $key
|
||||
* @param null $operator
|
||||
* @param null $value
|
||||
* @param string $joiner
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND')
|
||||
{
|
||||
$key = $this->addTablePrefix($key);
|
||||
$this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner');
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace FluentMail\App\Services\DB\QueryBuilder;
|
||||
|
||||
class QueryObject
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sql;
|
||||
|
||||
/**
|
||||
* @var \wpdb
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $bindings = array();
|
||||
|
||||
public function __construct($sql, array $bindings)
|
||||
{
|
||||
$this->sql = (string) $sql;
|
||||
|
||||
$this->bindings = $bindings;
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$this->db = $wpdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSql()
|
||||
{
|
||||
return $this->sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getBindings()
|
||||
{
|
||||
return $this->bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw/bound sql
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRawSql()
|
||||
{
|
||||
return $this->interpolateQuery($this->sql, $this->bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces any parameter placeholders in a query with the value of that
|
||||
* parameter. Useful for debugging. Assumes anonymous parameters from
|
||||
* $params are are in the same order as specified in $query
|
||||
*
|
||||
* Reference: http://stackoverflow.com/a/1376838/656489
|
||||
*
|
||||
* @param string $query The sql query with parameter placeholders
|
||||
* @param array $params The array of substitution parameters
|
||||
*
|
||||
* @return string The interpolated query
|
||||
*/
|
||||
protected function interpolateQuery($query, $params)
|
||||
{
|
||||
$keys = $placeHolders = [];
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
if (is_string($key)) {
|
||||
$keys[] = '/:' . $key . '/';
|
||||
} else {
|
||||
$keys[] = '/[?]/';
|
||||
}
|
||||
|
||||
$placeHolders[] = $this->getPlaceHolder($value);
|
||||
}
|
||||
|
||||
$query = preg_replace($keys, $placeHolders, $query, 1, $count);
|
||||
|
||||
return $params ? $this->db->prepare($query, $params) : $query;
|
||||
}
|
||||
|
||||
private function getPlaceHolder($value)
|
||||
{
|
||||
$placeHolder = '%s';
|
||||
|
||||
if (is_int($value)) {
|
||||
$placeHolder = '%d';
|
||||
} elseif (is_float($value)) {
|
||||
$placeHolder = '%f';
|
||||
}
|
||||
|
||||
return $placeHolder;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php namespace FluentMail\App\Services\DB\QueryBuilder;
|
||||
|
||||
class Raw
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $bindings;
|
||||
|
||||
public function __construct($value, $bindings = array())
|
||||
{
|
||||
$this->value = (string)$value;
|
||||
$this->bindings = (array)$bindings;
|
||||
}
|
||||
|
||||
public function getBindings()
|
||||
{
|
||||
return $this->bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string) $this->value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace FluentMail\App\Services\DB\QueryBuilder;
|
||||
|
||||
class Transaction extends QueryBuilderHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Commit the database changes
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$this->db->query('COMMIT');
|
||||
|
||||
throw new TransactionHaltException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the database changes
|
||||
*/
|
||||
public function rollback()
|
||||
{
|
||||
$this->db->query('ROLLBACK');
|
||||
|
||||
throw new TransactionHaltException();
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace FluentMail\App\Services\DB\QueryBuilder;
|
||||
|
||||
class TransactionHaltException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
namespace FluentMail\App\Services\DB\Viocon;
|
||||
|
||||
/**
|
||||
* This class gives the ability to access non-static methods statically
|
||||
*
|
||||
* Class AliasFacade
|
||||
*
|
||||
* @package Viocon
|
||||
*/
|
||||
class AliasFacade {
|
||||
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected static $vioconInstance;
|
||||
|
||||
/**
|
||||
* @param $method
|
||||
* @param $args
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
if(!static::$vioconInstance) {
|
||||
static::$vioconInstance = new Container();
|
||||
}
|
||||
|
||||
return call_user_func_array(array(static::$vioconInstance, $method), $args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Container $instance
|
||||
*/
|
||||
public static function setVioconInstance(Container $instance)
|
||||
{
|
||||
static::$vioconInstance = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \FluentMail\App\Services\DB\Viocon\Container $instance
|
||||
*/
|
||||
public static function getVioconInstance()
|
||||
{
|
||||
return static::$vioconInstance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php namespace FluentMail\App\Services\DB\Viocon;
|
||||
|
||||
class Container
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $registry = array();
|
||||
|
||||
/**
|
||||
* Singleton instances
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $singletons = array();
|
||||
|
||||
public function __construct($alias = null)
|
||||
{
|
||||
if ($alias) {
|
||||
AliasFacade::setVioconInstance($this);
|
||||
class_alias('\\FluentMail\\App\\Services\\DB\\Viocon\\AliasFacade', $alias);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an object with a key
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $object
|
||||
* @param bool $singleton
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $object, $singleton = false)
|
||||
{
|
||||
$this->registry[$key] = compact('object', 'singleton');
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have a registry for the given key
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return array_key_exists($key, $this->registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register as singleton.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function singleton($key, $object)
|
||||
{
|
||||
$this->set($key, $object, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register or replace an instance as a singleton.
|
||||
* Useful for replacing with Mocked instance
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setInstance($key, $instance)
|
||||
{
|
||||
$this->singletons[$key] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build from the given key.
|
||||
* If there is a class registered with Container::set() then it's instance
|
||||
* will be returned. If a closure is registered, a closure's return value
|
||||
* will be returned. If nothing is registered then it will try to build an
|
||||
* instance with new $key(...).
|
||||
*
|
||||
* $parameters will be passed to closure or class constructor.
|
||||
*
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $parameters
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function build($key, $parameters = array())
|
||||
{
|
||||
// If we have a singleton instance registered the just return it
|
||||
if (array_key_exists($key, $this->singletons)) {
|
||||
return $this->singletons[$key];
|
||||
}
|
||||
|
||||
// If we don't have a registered object with the key then assume user
|
||||
// is trying to build a class with the given key/name
|
||||
|
||||
if (!array_key_exists($key, $this->registry)) {
|
||||
$object = $key;
|
||||
} else {
|
||||
$object = $this->registry[$key]['object'];
|
||||
}
|
||||
|
||||
|
||||
$instance = $this->instanciate($object, $parameters);
|
||||
|
||||
// If the key is registered as a singleton, we can save the instance as singleton
|
||||
// for later use
|
||||
if (isset($this->registry[$key]['singleton']) && $this->registry[$key]['singleton'] === true) {
|
||||
$this->singletons[$key] = $instance;
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an instance of the given type.
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $parameters
|
||||
*
|
||||
* @throws \Exception
|
||||
* @return mixed
|
||||
*/
|
||||
protected function instanciate($key, $parameters = null)
|
||||
{
|
||||
|
||||
if ($key instanceof \Closure) {
|
||||
return call_user_func_array($key, $parameters);
|
||||
}
|
||||
|
||||
$reflection = new \ReflectionClass($key);
|
||||
return $reflection->newInstanceArgs($parameters);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php namespace FluentMail\App\Services\DB\Viocon;
|
||||
|
||||
|
||||
class VioconException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php defined('ABSPATH') or die;
|
||||
|
||||
|
||||
if (! function_exists('FluentSmtpDb')) {
|
||||
/**
|
||||
* @return \FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler
|
||||
*/
|
||||
function FluentSmtpDb()
|
||||
{
|
||||
static $FluentSmtpDb;
|
||||
|
||||
if (! $FluentSmtpDb) {
|
||||
global $wpdb;
|
||||
|
||||
$connection = new \FluentMail\App\Services\DB\Connection($wpdb, ['prefix' => $wpdb->prefix]);
|
||||
|
||||
$FluentSmtpDb = new \FluentMail\App\Services\DB\QueryBuilder\QueryBuilderHandler($connection);
|
||||
}
|
||||
|
||||
return $FluentSmtpDb;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user