-
-
Save DaveRandom/6286b4083bac4ea2c9bf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace PDOPlus; | |
use PDO, PDOStatement, DateTimeInterface; | |
trait AugmentedParams | |
{ | |
/** | |
* @param mixed $value | |
* @return array | |
*/ | |
protected function getAutoTypedParam($value) | |
{ | |
if ($value === null) { | |
return [$value, PDO::PARAM_NULL]; | |
} else if (is_int($value)) { | |
return [$value, PDO::PARAM_INT]; | |
} else if (is_bool($value)) { | |
return [$value, PDO::PARAM_BOOL]; | |
} else if ($value instanceof DateTimeInterface) { | |
return [$value->format('Y-m-d H:i:s'), PDO::PARAM_STR]; | |
} else if (is_array($value)) { | |
throw new \LogicException('Array cannot be used as a parameter in this context'); | |
} else { | |
set_error_handler(function() { | |
throw new \LogicException(func_get_arg(1)); | |
}, E_RECOVERABLE_ERROR); | |
$value = (string)$value; | |
restore_error_handler(); | |
return [$value, PDO::PARAM_STR]; | |
} | |
} | |
/** | |
* @param PDOStatement $stmt | |
* @param array $params | |
*/ | |
protected function bindTypedParamMap(PDOStatement $stmt, array $params) | |
{ | |
foreach ($params as $placeholder => list($value, $type)) { | |
$stmt->bindValue($placeholder, $value, $type); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace PDOPlus; | |
use PDO; | |
class Connection extends PDO | |
{ | |
use AugmentedParams; | |
/** | |
* @param string $sql | |
* @param string $paramName | |
* @param array $values | |
* @return array | |
*/ | |
protected function expandListParams($sql, $paramName, array $values) | |
{ | |
$placeholderExpr = '/::' . preg_quote($paramName, '/') . '/'; | |
if (ctype_digit((string)$paramName)) { | |
throw new \LogicException('List expansion is only available with named parameters'); | |
} else if (preg_match_all($placeholderExpr, $sql) !== 1) { | |
throw new \LogicException('List expansion placeholder ::' . $paramName . ' must appear exactly once'); | |
} | |
$placeholders = $map = []; | |
$i = 0; | |
foreach ($values as $value) { | |
$placeholderName = $paramName . ++$i; | |
$placeholders[] = $placeholderName; | |
$map[$placeholderName] = $this->getAutoTypedParam($value); | |
} | |
$sql = preg_replace($placeholderExpr, ':' . implode(', :', $placeholders), $sql); | |
return [$sql, $map]; | |
} | |
/** | |
* @param string $sql | |
* @param array $params | |
* @return array | |
*/ | |
protected function prepareTypedParamMap($sql, array $params) | |
{ | |
$map = []; | |
foreach ($params as $placeholder => $value) { | |
if (is_array($value) || $value instanceof \stdClass) { | |
list($sql, $listParams) = $this->expandListParams($sql, $placeholder, (array)$value); | |
$map = array_merge($map, $listParams); | |
} else { | |
$map[$placeholder] = $this->getAutoTypedParam($value); | |
} | |
} | |
return [$sql, $map]; | |
} | |
/** | |
* @param string $dsn | |
* @param string $user | |
* @param string $pass | |
* @param array $options | |
*/ | |
public function __construct($dsn, $user = null, $pass = null, array $options = []) | |
{ | |
parent::__construct($dsn, $user, $pass, $options + [ | |
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, | |
PDO::ATTR_EMULATE_PREPARES => false, | |
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, | |
PDO::ATTR_STATEMENT_CLASS => [__NAMESPACE__ . '\Statement', [$this]], | |
]); | |
} | |
/** | |
* @param string $sql | |
* @param array $params | |
* @return int | |
* @throws \Exception | |
*/ | |
public function exec($sql, array $params = null) | |
{ | |
if (!isset($params)) { | |
return parent::exec($sql); | |
} | |
$stmt = $this->prepare($sql, $params); | |
$stmt->execute(); | |
return $stmt->rowCount(); | |
} | |
/** | |
* @param string $sql | |
* @param array $params | |
* @param array $driverOptions | |
* @return Statement | |
* @throws \Exception | |
*/ | |
public function prepare($sql, array $params = null, array $driverOptions = []) | |
{ | |
if (!isset($params)) { | |
return parent::prepare($sql, $driverOptions); | |
} | |
list($sql, $paramMap) = $this->prepareTypedParamMap($sql, $params); | |
$stmt = parent::prepare($sql, $driverOptions); | |
$this->bindTypedParamMap($stmt, $paramMap); | |
return $stmt; | |
} | |
/** | |
* @param string $sql | |
* @param array $params | |
* @return Statement | |
* @throws \Exception | |
*/ | |
public function query($sql, array $params = null) | |
{ | |
if (!isset($params)) { | |
return parent::query($sql); | |
} | |
$stmt = $this->prepare($sql, $params); | |
$stmt->execute(); | |
return $stmt; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace PDOPlus; | |
class Statement extends \PDOStatement | |
{ | |
use AugmentedParams; | |
/** | |
* @param array $params | |
* @return array | |
*/ | |
protected function prepareTypedParamMap(array $params) | |
{ | |
$map = []; | |
foreach ($params as $placeholder => $value) { | |
$map[$placeholder] = $this->getAutoTypedParam($value); | |
} | |
return $map; | |
} | |
/** | |
* @param string $name | |
* @param mixed $value | |
* @param int $type | |
* @return bool | |
*/ | |
public function bindValue($name, $value, $type = null) | |
{ | |
if (!isset($type)) { | |
list($value, $type) = $this->getAutoTypedParam($value); | |
} | |
return parent::bindValue($name, $value, $type); | |
} | |
/** | |
* @param string $name | |
* @param mixed $value | |
* @param int $type | |
* @return bool | |
*/ | |
public function bindParam($name, &$value, $type = null) | |
{ | |
if (!isset($type)) { | |
$type = $this->getAutoTypedParam($value)[1]; | |
} | |
return parent::bindParam($name, $value, $type); | |
} | |
/** | |
* @param array $params | |
* @return bool|void | |
*/ | |
public function execute(array $params = null) | |
{ | |
if (isset($params)) { | |
$paramMap = $this->prepareTypedParamMap($params); | |
$this->bindTypedParamMap($this, $paramMap); | |
} | |
return parent::execute(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment