250 lines
7.3 KiB
PHP
250 lines
7.3 KiB
PHP
<?php
|
|
namespace nur\sery\db\_private;
|
|
|
|
use nur\sery\cl;
|
|
use nur\sery\str;
|
|
use nur\sery\ValueException;
|
|
|
|
abstract class _base {
|
|
protected static function consume(string $pattern, string &$string, ?array &$ms=null): bool {
|
|
if (!preg_match("/^$pattern/i", $string, $ms)) return false;
|
|
$string = substr($string, strlen($ms[0]));
|
|
return true;
|
|
}
|
|
|
|
/** fusionner toutes les parties séquentielles d'une requête */
|
|
protected static function merge_seq(array $query): string {
|
|
$index = 0;
|
|
$sql = "";
|
|
foreach ($query as $key => $value) {
|
|
if ($key === $index) {
|
|
$index++;
|
|
if ($sql && !str::ends_with(" ", $sql) && !str::starts_with(" ", $value)) {
|
|
$sql .= " ";
|
|
}
|
|
$sql .= $value;
|
|
}
|
|
}
|
|
return $sql;
|
|
}
|
|
|
|
protected static function is_sep(&$cond): bool {
|
|
if (!is_string($cond)) return false;
|
|
if (!preg_match('/^\s*(and|or|not)\s*$/i', $cond, $ms)) return false;
|
|
$cond = $ms[1];
|
|
return true;
|
|
}
|
|
|
|
static function parse_conds(?array $conds, ?array &$sql, ?array &$bindings): void {
|
|
if (!$conds) return;
|
|
$sep = null;
|
|
$index = 0;
|
|
$condsql = [];
|
|
foreach ($conds as $key => $cond) {
|
|
if ($key === $index) {
|
|
## séquentiel
|
|
if ($index === 0 && self::is_sep($cond)) {
|
|
$sep = $cond;
|
|
} elseif (is_bool($cond)) {
|
|
# ignorer les valeurs true et false
|
|
} elseif (is_array($cond)) {
|
|
# condition récursive
|
|
self::parse_conds($cond, $condsql, $bindings);
|
|
} else {
|
|
# condition litérale
|
|
$condsql[] = strval($cond);
|
|
}
|
|
$index++;
|
|
} elseif ($cond === false) {
|
|
## associatif
|
|
# condition litérale ignorée car condition false
|
|
} elseif ($cond === true) {
|
|
# condition litérale sélectionnée car condition true
|
|
$condsql[] = strval($key);
|
|
} else {
|
|
## associatif
|
|
# paramètre
|
|
$i = false;
|
|
if ($bindings !== null && array_key_exists($key, $bindings)) {
|
|
$i = 2;
|
|
while (array_key_exists("$key$i", $bindings)) {
|
|
$i++;
|
|
}
|
|
}
|
|
# value ou [operator, value]
|
|
$condprefix = $condsep = $condsuffix = null;
|
|
if (is_array($cond)) {
|
|
$condkey = 0;
|
|
$condkeys = array_keys($cond);
|
|
$op = null;
|
|
if (array_key_exists("op", $cond)) {
|
|
$op = $cond["op"];
|
|
} elseif (array_key_exists($condkey, $condkeys)) {
|
|
$op = $cond[$condkeys[$condkey]];
|
|
$condkey++;
|
|
}
|
|
$op = strtolower($op);
|
|
$condvalues = null;
|
|
switch ($op) {
|
|
case "between":
|
|
# ["between", $upper, $lower]
|
|
$condsep = " and ";
|
|
if (array_key_exists("lower", $cond)) {
|
|
$condvalues[] = $cond["lower"];
|
|
} elseif (array_key_exists($condkey, $condkeys)) {
|
|
$condvalues[] = $cond[$condkeys[$condkey]];
|
|
$condkey++;
|
|
}
|
|
if (array_key_exists("upper", $cond)) {
|
|
$condvalues[] = $cond["upper"];
|
|
} elseif (array_key_exists($condkey, $condkeys)) {
|
|
$condvalues[] = $cond[$condkeys[$condkey]];
|
|
$condkey++;
|
|
}
|
|
break;
|
|
case "in":
|
|
# ["in", $values]
|
|
$condprefix = "(";
|
|
$condsep = ", ";
|
|
$condsuffix = ")";
|
|
$condvalues = null;
|
|
if (array_key_exists("values", $cond)) {
|
|
$condvalues = cl::with($cond["values"]);
|
|
} elseif (array_key_exists($condkey, $condkeys)) {
|
|
$condvalues = cl::with($cond[$condkeys[$condkey]]);
|
|
$condkey++;
|
|
}
|
|
break;
|
|
case "null":
|
|
case "is null":
|
|
$op = "is null";
|
|
break;
|
|
case "not null":
|
|
case "is not null":
|
|
$op = "is not null";
|
|
break;
|
|
default:
|
|
if (array_key_exists("value", $cond)) {
|
|
$condvalues = [$cond["value"]];
|
|
} elseif (array_key_exists($condkey, $condkeys)) {
|
|
$condvalues = [$cond[$condkeys[$condkey]]];
|
|
$condkey++;
|
|
}
|
|
}
|
|
} elseif ($cond !== null) {
|
|
$op = "=";
|
|
$condvalues = [$cond];
|
|
} else {
|
|
$op = "is null";
|
|
$condvalues = null;
|
|
}
|
|
$cond = [$key, $op];
|
|
if ($condvalues !== null) {
|
|
$parts = [];
|
|
foreach ($condvalues as $condvalue) {
|
|
if (is_array($condvalue)) {
|
|
$first = true;
|
|
foreach ($condvalue as $value) {
|
|
if ($first) {
|
|
$first = false;
|
|
} else {
|
|
if ($sep === null) $sep = "and";
|
|
$parts[] = " $sep ";
|
|
$parts[] = $key;
|
|
$parts[] = " $op ";
|
|
}
|
|
$param = "$key$i";
|
|
$parts[] = ":$param";
|
|
$bindings[$param] = $value;
|
|
if ($i === false) $i = 2;
|
|
else $i++;
|
|
}
|
|
} else {
|
|
$param = "$key$i";
|
|
$parts[] = ":$param";
|
|
$bindings[$param] = $condvalue;
|
|
if ($i === false) $i = 2;
|
|
else $i++;
|
|
}
|
|
}
|
|
$cond[] = $condprefix.implode($condsep, $parts).$condsuffix;
|
|
}
|
|
$condsql[] = implode(" ", $cond);
|
|
}
|
|
}
|
|
if ($sep === null) $sep = "and";
|
|
$count = count($condsql);
|
|
if ($count > 1) {
|
|
$sql[] = "(" . implode(" $sep ", $condsql) . ")";
|
|
} elseif ($count == 1) {
|
|
$sql[] = $condsql[0];
|
|
}
|
|
}
|
|
|
|
static function parse_set_values(?array $values, ?array &$sql, ?array &$bindings): void {
|
|
if (!$values) return;
|
|
$index = 0;
|
|
$parts = [];
|
|
foreach ($values as $key => $part) {
|
|
if ($key === $index) {
|
|
## séquentiel
|
|
if (is_array($part)) {
|
|
# paramètres récursifs
|
|
self::parse_set_values($part, $parts, $bindings);
|
|
} else {
|
|
# paramètre litéral
|
|
$parts[] = strval($part);
|
|
}
|
|
$index++;
|
|
} else {
|
|
## associatif
|
|
# paramètre
|
|
$param = $key;
|
|
if ($bindings !== null && array_key_exists($param, $bindings)) {
|
|
$i = 2;
|
|
while (array_key_exists("$key$i", $bindings)) {
|
|
$i++;
|
|
}
|
|
$param = "$key$i";
|
|
}
|
|
# value
|
|
$value = $part;
|
|
$part = [$key, "="];
|
|
if ($value === null) {
|
|
$part[] = "null";
|
|
} else {
|
|
$part[] = ":$param";
|
|
$bindings[$param] = $value;
|
|
}
|
|
$parts[] = implode(" ", $part);
|
|
}
|
|
}
|
|
$sql = cl::merge($sql, $parts);
|
|
}
|
|
|
|
protected static function check_eof(string $tmpsql, string $usersql): void {
|
|
self::consume(';\s*', $tmpsql);
|
|
if ($tmpsql) {
|
|
throw new ValueException("unexpected value at end: $usersql");
|
|
}
|
|
}
|
|
|
|
abstract protected static function verifix(&$sql, ?array &$bindinds=null, ?array &$meta=null): void;
|
|
|
|
function __construct($sql, ?array $bindings=null) {
|
|
static::verifix($sql, $bindings, $meta);
|
|
$this->sql = $sql;
|
|
$this->bindings = $bindings;
|
|
$this->meta = $meta;
|
|
}
|
|
|
|
/** @var string */
|
|
protected $sql;
|
|
|
|
/** @var ?array */
|
|
protected $bindings;
|
|
|
|
/** @var ?array */
|
|
protected $meta;
|
|
}
|