function exec($cmds, $args = NULL, $ttl = 0, $log = TRUE, $stamp = FALSE)
{
$tag = '';
if (is_array($ttl)) {
list($ttl, $tag) = $ttl;
}
$auto = FALSE;
if (is_null($args)) {
$args = [];
} elseif (is_scalar($args)) {
$args = [1 => $args];
}
if (is_array($cmds)) {
if (count($args) < ($count = count($cmds))) {
// Apply arguments to SQL commands
$args = array_fill(0, $count, $args);
}
if (!$this->trans) {
$this->begin();
$auto = TRUE;
}
} else {
$count = 1;
$cmds = [$cmds];
$args = [$args];
}
if ($this->log === FALSE) {
$log = FALSE;
}
$fw = \Base::instance();
$cache = \Cache::instance();
$result = FALSE;
for ($i = 0; $i < $count; $i++) {
$cmd = $cmds[$i];
$arg = $args[$i];
// ensure 1-based arguments
if (array_key_exists(0, $arg)) {
array_unshift($arg, '');
unset($arg[0]);
}
if (!preg_replace('/(^\\s+|[\\s;]+$)/', '', $cmd)) {
continue;
}
$now = microtime(TRUE);
$keys = $vals = [];
if ($fw->get('CACHE') && $ttl && ($cached = $cache->exists($hash = $fw->hash($this->dsn . $cmd . $fw->stringify($arg)) . ($tag ? '.' . $tag : '') . '.sql', $result)) && $cached[0] + $ttl > microtime(TRUE)) {
foreach ($arg as $key => $val) {
$vals[] = $fw->stringify(is_array($val) ? $val[0] : $val);
$keys[] = '/' . preg_quote(is_numeric($key) ? chr(0) . '?' : $key) . '/';
}
if ($log) {
$this->log .= ($stamp ? date('r') . ' ' : '') . '(' . sprintf('%.1f', 1000.0 * (microtime(TRUE) - $now)) . 'ms) ' . '[CACHED] ' . preg_replace($keys, $vals, str_replace('?', chr(0) . '?', $cmd), 1) . PHP_EOL;
}
} elseif (is_object($query = $this->pdo->prepare($cmd))) {
foreach ($arg as $key => $val) {
if (is_array($val)) {
// User-specified data type
$query->bindvalue($key, $val[0], $val[1] == self::PARAM_FLOAT ? \PDO::PARAM_STR : $val[1]);
$vals[] = $fw->stringify($this->value($val[1], $val[0]));
} else {
// Convert to PDO data type
$query->bindvalue($key, $val, ($type = $this->type($val)) == self::PARAM_FLOAT ? \PDO::PARAM_STR : $type);
$vals[] = $fw->stringify($this->value($type, $val));
}
$keys[] = '/' . preg_quote(is_numeric($key) ? chr(0) . '?' : $key) . '/';
}
if ($log) {
$this->log .= ($stamp ? date('r') . ' ' : '') . '(' . sprintf('%.1f', 1000.0 * (microtime(TRUE) - $now)) . 'ms) ' . preg_replace($keys, $vals, str_replace('?', chr(0) . '?', $cmd), 1) . PHP_EOL;
}
$query->execute();
$error = $query->errorinfo();
if ($error[0] != \PDO::ERR_NONE) {
// Statement-level error occurred
if ($this->trans) {
$this->rollback();
}
user_error('PDOStatement: ' . $error[2], E_USER_ERROR);
}
if (preg_match('/(?:^[\\s\\(]*' . '(?:EXPLAIN|SELECT|PRAGMA|SHOW)|RETURNING)\\b/is', $cmd) || preg_match('/^\\s*(?:CALL|EXEC)\\b/is', $cmd) && $query->columnCount()) {
$result = $query->fetchall(\PDO::FETCH_ASSOC);
// Work around SQLite quote bug
if (preg_match('/sqlite2?/', $this->engine)) {
foreach ($result as $pos => $rec) {
unset($result[$pos]);
$result[$pos] = [];
foreach ($rec as $key => $val) {
$result[$pos][trim($key, '\'"[]`')] = $val;
}
}
}
$this->rows = count($result);
if ($fw->get('CACHE') && $ttl) {
// Save to cache backend
$cache->set($hash, $result, $ttl);
}
} else {
$this->rows = $result = $query->rowcount();
}
$query->closecursor();
unset($query);
} else {
$error = $this->errorinfo();
if ($error[0] != \PDO::ERR_NONE) {
// PDO-level error occurred
if ($this->trans) {
$this->rollback();
}
user_error('PDO: ' . $error[2], E_USER_ERROR);
}
}
}
if ($this->trans && $auto) {
$this->commit();
}
return $result;
}