query($sql) : mysql_query($sql); if ($result) return $result; else { if (isset($_SERVER['REQUEST_URI'])) { if (is_callable('http_response_code')) http_response_code(500); else // PHP 5.3 and older header("", TRUE, 500); } $trace = debug_backtrace(); $mysql_functions = array( 'mysql_getcell', 'mysql_getrow', 'mysql_getcolumn', 'mysql_gettable', 'mysql_write_row' ); if (isset($trace[1]) AND in_array($trace[1]['function'], $mysql_functions)) $level = 1; else $level = 0; $db_error = ($mysqli) ? mysqli_error($mysqli) : mysql_error(); $message = '

MySQL error in file '.$trace[$level]['file'].''. " at line " .$trace[$level]['line']." (function " . $trace[$level]['function'] ."):
" ."\n$db_error\n\n

$sql

"; trigger_error($message, E_USER_ERROR); } } function mysql_substitute($sql, $substitutions) { return preg_replace_callback( '/:(\w+)/', function ($matches) use ($substitutions) { $key = $matches[1]; $value = (isset($substitutions[$key])) // PHP 5.6 compatible ? $substitutions[$key] : NULL; return mysql_escape($value); }, $sql ); } function mysql_escape($value) { global $mysqli; if (is_array($value)) { $escaped = implode(',', array_map(__FUNCTION__, $value) ); // В качестве массивов могут быть не только JSON-поля, // но и множество значений фильтра для IN, // поэтому JSON "ловим" отдельно: // - при записи - в mysql_write_row() // - при чтении - в ORM } else { if ( is_object($value) AND ( get_class($value) === 'DateTime' OR is_subclass_of($value, 'DateTime') ) ) { $value = $value->format('Y-m-d H:i:s'); } if (is_string($value)) { $escaped = ($mysqli) ? "'" . mysqli_real_escape_string($mysqli, $value) . "'" : "'" . mysql_real_escape_string($value) . "'"; } elseif (is_numeric($value)) { $escaped = str_replace(',', '.', $value); } elseif (is_null($value)) { $escaped = 'NULL'; } else { $escaped = intval($value); } } return $escaped; } function mysql_getcell($sql, $substitutions = array()) { $tmp = mysql_getcolumn($sql, FALSE, $substitutions); $cell = ($tmp) ? reset($tmp) : FALSE ; return $cell; } function mysql_getrow($sql, $substitutions = array()) { $tmp = mysql_gettable($sql, FALSE, $substitutions); $row = ($tmp) ? reset($tmp) : array(); return $row; } function mysql_getcolumn($sql, $makehash = FALSE, $substitutions = array()) { $data = array(); $result = mysql_q($sql, $substitutions); $fn = is_resource($result) ? 'mysql_fetch_row' : 'mysqli_fetch_row' ; if (!$makehash) while ($row = $fn($result)) $data[] = $row[0]; else while ($row = $fn($result)) $data[$row[0]] = $row[1]; if (!is_resource($result)) $result->close(); return $data; } function mysql_gettable($sql, $keycol = FALSE, $substitutions = array()) { $data = array(); $result = mysql_q($sql, $substitutions); $fn = is_resource($result) ? 'mysql_fetch_assoc' : 'mysqli_fetch_assoc' ; if (!$keycol) while ($row = $fn($result)) $data[] = $row; else while ($row = $fn($result)) $data[$row[$keycol]] = $row; if (!is_resource($result)) $result->close(); return $data; } function mysql_write_row( $tablename, $data, $unique_key = FALSE, $mode = FALSE) { global $mysqli; if (!$unique_key) { // Уникальный идентификатор не указан - INSERT if (!$mode) $sql = "INSERT"; elseif ($mode == 'IGNORE') $sql = "INSERT IGNORE"; elseif ($mode == 'REPLACE') $sql = "REPLACE"; else { $trace = debug_backtrace()[0]; $message = " Uknown mode \"$mode\" given to $trace[function]() in file $trace[file] at line $trace[line]. Terminating function run. "; trigger_error($message, E_USER_WARNING); return FALSE; } $sql .= " INTO $tablename "; if ($data) { $sql .= " SET "; // JSON "ловим" здесь, т.к. mysql_escape должна поддерживать // множественные значения фильтров, поэтому там // не преобразование в JSON, а перечисление через запятую для IN // или AND для других случаев foreach ($data as $key => $value) { $sql .= mysql_convert_key_value_pair_for_writing($key, $value) . ', '; // $sql .= "`$key` = :$key, "; // метки не используем вообще, // т.к. вставка JSON в запрос происходит // до замены меток (поскольку mysql_substitute() // не заменяет метку JSON-форму массива для записи) } $sql = substr($sql, 0, -2); // убираем запятую и пробел } else $sql .= " VALUES () "; $result = mysql_q($sql); $out = ($mysqli) ? $mysqli->insert_id : mysql_insert_id(); } else { // UPDATE или INSERT ON DUPLICATE KEY UPDATE if (!is_array($unique_key)) { if (!$mode) { // обычный UPDATE // если указана скалярная величина - // воспринмаем её как 'id' $unique_key = array('id' => $unique_key); } else { // ON DUPLICATE KEY: здесь указан уникальный ключ $unique_key = array($unique_key); } } foreach ($unique_key as $k => $v) { // Проверяем, что указанные уникальные ключи присутствуют среди данных if (is_numeric($k) AND !isset($data[$v])) { $for_msg[] = $v; } if ($for_msg ?? false) { $msg = "Key" . (count($for_msg) > 1 ? "s" : "" ) . " " . "'" . implode("', '", $for_msg) . "'" . " of unique keys\n\n" . print_r($unique_key, 1) . "\n" . (count($for_msg) > 1 ? "are" : "is") . " absent among data keys:\n\n" . print_r($data, 1) . "\n\nTrace:\n\n" . print_r( array_map( function ($value) { unset($value['args']); return $value; }, debug_backtrace() ), 1 ); trigger_error($msg, E_USER_ERROR); } } unset($k, $v, $for_msg, $msg); if (!$mode) { // обычный UPDATE // В данном случае поля из второго аргумента подставляются в часть SET, // а поля из третьего - в часть WHERE $sql = "UPDATE $tablename SET "; // Чтобы одно и то же поле можно было использовать // и в части SET, и в части WHERE с разными значениями, например // UPDATE table // SET col1 = 'A', col2 = 'B' // WHERE col1 = 'C' // подстановку значений в запрос проводим "вручную" - // без использования меток. foreach ($data as $key => $value) { // $sql .= "`$key` = " . mysql_escape($value) . ", "; $sql .= mysql_convert_key_value_pair_for_writing($key, $value) . ', '; } $sql = substr($sql, 0, -2); // убираем запятую и пробел if ($unique_key) { foreach ($unique_key as $key => $value) { $part = " `$key` "; $part .= (!is_null($value)) ? " = " . mysql_escape($value) : " IS NULL"; $parts[] = $part; } $sql .= " WHERE (" . implode(") AND (", $parts) . ")"; unset($parts, $part); } $result = mysql_q($sql); $out = ($mysqli) ? $mysqli->affected_rows : mysql_affected_rows() ; } elseif ($mode == 'DUPLICATE') { // INSERT ... ON DUPLICATE KEY UPDATE $append = is_string(key($unique_key)); // $append: если массив $unique_key ассоциативный, // значит, в них данные для уникальных полей - // включаем их в INSERT и в подставновку в mysql_q() // Если же массив числовой, значит // все необходимые данные переданы во втором аргументе, // а $unique_key содержит только имена полей, // которые следует исключить из ON DUPLICATE KEY if ($append) { $all_data = $data + $unique_key; // Все данные для ON DUPLICATE KEY UPDATE $data_to_update = $data; // есть в $data } else { $all_data = $data; $data_to_update = array_diff_key( // В $unique_key переданы имена полей, $data, // которые необходимо исключить array_fill_keys($unique_key, TRUE) // из части ON DUPLICATE KEY UPDATE ); } $sql = "INSERT INTO $tablename SET "; foreach ($all_data as $key => $value) { // $sql .= "`$key` = :$key, "; $sql .= mysql_convert_key_value_pair_for_writing($key, $value) . ', '; } $sql = substr($sql, 0, -2); // убираем запятую и пробел if ($data_to_update) { $sql .= " ON DUPLICATE KEY UPDATE " ; foreach ($data_to_update as $key => $value) { // $sql .= " `$key` = :$key, "; $sql .= mysql_convert_key_value_pair_for_writing($key, $value) . ', '; } $sql = substr($sql, 0, -2); // убираем запятую и пробел } $result = mysql_q($sql/*, $all_data*/); // Т.к. запрос INSERT - возвращает LAST_INSERT_ID() $out = ($mysqli) ? $mysqli->insert_id : mysql_insert_id(); } } return $out; } /** * Заменяет пару ключ-значение на SQL-выражение для записи. * 'поле', не массив - обычный случай * 'поле', массив - JSON-поле, переписываем полностью * 'поле.ключ', что-то - обновляем ключ у JSON-поля * @param string $key * @param mixed $value * @return string поле = значение */ function mysql_convert_key_value_pair_for_writing($key, $value) { $sql = ''; if (strpos($key, '.') === false) { $sql .= "`$key` = "; if (is_array($value)) { $v = json_encode($value, JSON_UNESCAPED_UNICODE); } else { $v = $value; } $sql .= mysql_escape($v); } else { // 'one.two.2.three' превратится в '$.one.two[2].three $tmp = explode('.', $key); $column = $tmp[0]; $rest = '.' . $tmp[1]; // точку возвращаем $path = preg_replace_callback( '/\.(\w+)/', function ($matches) { return (is_numeric($matches[1])) ? '[' . $matches[1] . ']' : '.' . $matches[1] ; }, $rest ); // из JSON-пути на всякий случай исключаем все лишние символы $path = preg_replace('/[^\w\.\[\]]/', '', $path); // все массивы вставляем как ассоциативные, // т.к. PHP все равно, а для JSON важно // добавляем IFNULL: JSON_SET(NULL, ...) всегда даст NULL // При вставке подмассива необходимо добавить JSON_EXTRACT // (или JSON_MERGE с пустым объектом); // в противном случае подмассив вставится // не как объект по структуре, а как JSON-строка // (с экранированными внутри кавычками) $sql .= "`$column` = JSON_SET(IFNULL(`$column`, '{}'), '\$$path', "; $sql .= (!is_array($value)) ? mysql_escape($value) : "JSON_EXTRACT(" . mysql_escape(json_encode($value, JSON_UNESCAPED_UNICODE)) . ", '$')"; // "JSON_MERGE('{}', " . mysql_escape(json_encode($value, JSON_UNESCAPED_UNICODE)) . ")"; $sql .= ")"; } return $sql; }