Skip to content

Instantly share code, notes, and snippets.

@DaveRandom
Created November 1, 2014 22:00
Show Gist options
  • Save DaveRandom/6286b4083bac4ea2c9bf to your computer and use it in GitHub Desktop.
Save DaveRandom/6286b4083bac4ea2c9bf to your computer and use it in GitHub Desktop.
<?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);
}
}
}
<?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;
}
}
<?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