Обработка операций sql с помощью функции и массива php

Нужны советы по созданию класса, который обрабатывает операции запроса sql с заданным массивом (имея полный контроль над массивом и получение информации о записи). Мой код работает правильно (это просто пример), но нужно проверить его безопасность, или есть лучший способ достичь результата. Хочу отметить, что все опасные символы перед добавлением в массив очищаются другой функцией (для предотвращения инъекции). Итак, основная идея — создать массив со структурой запроса, затем перейти к функции и выполнить операцию запроса sql. Спасибо, ценю все ответы.

**
ЭТО ТОЛЬКО ДЛЯ УЧЕБНЫХ ЦЕЛЕЙ
**

Создан класс, обрабатывающий операции:

class my_op
  {
    private $pdo;
    
    public function __construct($pdo)
      {
        $this->pdo = $pdo;
      }
    
    private $safe_code = "@@@";
    
    private function safe_chars($first_string, $second_string)
      {
        $safe_chars   = array(
            "A",
            "B",
            "C",
            "D",
            "E",
            "F",
            "G",
            "H",
            "I",
            "J",
            "K",
            "L",
            "M",
            "N",
            "O",
            "P",
            "Q",
            "R",
            "S",
            "T",
            "U",
            "V",
            "W",
            "X",
            "Y",
            "Z",
            "a",
            "b",
            "c",
            "d",
            "e",
            "f",
            "g",
            "h",
            "i",
            "j",
            "k",
            "l",
            "m",
            "n",
            "o",
            "p",
            "q",
            "r",
            "s",
            "t",
            "u",
            "v",
            "w",
            "x",
            "y",
            "z",
            "1",
            "2",
            "3",
            "4",
            "5",
            "6",
            "7",
            "8",
            "9",
            "0",
            ",",
            "'",
            "?",
            "!",
            " ",
            "_",
            "-",
            ":",
            "(",
            ")",
            "="
        );
        $unsafe_count = null;
        if (strlen($first_string) > 0):
            foreach (str_split($first_string) as $char_check)
              {
                if (!in_array($char_check, $safe_chars))
                  {
                    $unsafe_count++;
                  }
              }
        endif;
        if (strlen($second_string) > 0):
            foreach (str_split($second_string) as $char_check)
              {
                if (!in_array($char_check, $safe_chars))
                  {
                    $unsafe_count++;
                  }
              }
        endif;
        return $unsafe_count;
      }
    
    private function build_query($my_array)
      {
        
        $structure_allowed = array(
            "UPDATE",
            "DELETE FROM",
            "INSERT INTO",
            "WHERE",
            "SET",
            "VALUES",
            "ORDER BY"
        );
        
        $allowed_arrays = 3;
        $K_LEAST        = 1;
        $K_MOST         = 4;
        $KEY_S          = "STRUCTURE";
        $KEY_V          = "SETPARAM";
        
        if (is_array($my_array) && sizeof($my_array) > 0):
            $secure_purposes      = implode('|', $structure_allowed);
            $filtered_array_my_op = filter_var_array($my_array, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_HIGH);
            $ats = false;
            try
              {
                if (sizeof($my_array) <= $allowed_arrays):
                    $op_made  = null;
                    $op_chars = null;
                    $op_stop  = null;
                    foreach (is_array($filtered_array_my_op) || is_object($filtered_array_my_op) ? $filtered_array_my_op : array() as $splited_array)
                      {
                        $parsed_values    = array();
                        $parsed_structure = array();
                        $s                = null;
                        $v                = null;
                        $param_list       = null;
                        $values_list      = null;
                        $f_mode           = null;
                        
            
    foreach ($splited_array as $parsed_keys_g => &$parsed_values_g)
      {
        
        foreach ($parsed_values_g as $direct_key => &$direct_value)
          {
            switch ($parsed_keys_g)
            {
                
                case "STRUCTURE":
                    if ($parsed_keys_g == $KEY_S && strlen($direct_value) > 0 && in_array($direct_key, $structure_allowed)):
                 if ($this->safe_chars($direct_key, $direct_value) <= 0):
                        $parsed_structure[$direct_key] = $direct_value;
                        $param_list .= "$direct_key $direct_value";
                         $s++;
                    else:
                    $op_chars++;
                    endif;
                    elseif ($parsed_keys_g == $KEY_S && strlen($parsed_s_value) < 0 || $parsed_keys_g == $KEY_S && !in_array($parsed_s_key, $structure_allowed)):
                    $op_stop++;
                    endif;
                    break;
                
                case "SETPARAM":
                     if ($parsed_keys_g == $KEY_V && strlen($direct_value) > 0):
                                    if ($this->safe_chars($direct_key, $direct_value) <= 0):
                                        $parsed_values[$direct_key] = $direct_value;
                                        $values_list .= "$direct_key $direct_value ";
                                        $v++;
                                    else:
                                        $op_chars++;
                                    endif;
                                elseif ($parsed_keys_g == $KEY_V && strlen($direct_value) < 0):
                                    $op_stop++;
                                endif;
                break;
                case "FMODE":
                    $f_mode = $direct_value;
                    break;
                    
            }
          }
      }
                        $op_made++;
                        if ($s >= $K_LEAST && $s <= $K_MOST && $v > 0 && preg_match("($secure_purposes)", $param_list) && array_key_exists($KEY_S, $splited_array) && $op_stop <= 0):
                            try
                              {
                             //   $this->pdo->beginTransaction();
                               // $my_operation = $this->pdo->prepare("$param_list");
                            //    $bindcount = 0;
                              //  foreach ($parsed_values as $value_key_parsed => $value_parsed) {
                               // $bindcount++;
                                //$my_operation->bindValue($value_key_parsed, $value_parsed);
                                //}
                                //switch ($f_mode) {
                                //case "execute":  
                                //$my_operation->execute($parsed_values);
                                //break;
                                //}
                                //if ($my_operation->execute($params)) {
                                $ats .= "$f_mode Success #$op_made #$bindcount on //params($s) values($v) // PARAMLIST: " . $param_list . "//<BR> VALUESLIST: [" . $values_list . "] [" . sizeof($my_array) . "] <BR>";
                            //    } else { 
                              //  $ats .= "Failed #$op_made #$bindcount on //params($s) values($v) // ".$param_list."// [".$values_list."] [".sizeof($my_array)."]<BR>";
                            //    } 
                              //  $my_operation = null;
                            //    $this->pdo->commit();
                              //  $this->pdo = null;
                              }
                            catch (Exception $e)
                              {
                                $ats .= 'Something wrong: ' . $e->getMessage();
                              }
                        else:
                            if ($op_chars > 0):
                                throw new Exception("Unsafe chars cannot be tolerated ");
                            endif;
                            if ($op_stop > 0):
                                throw new Exception("Some keys or values do not match requirements. //v $v //s $s");
                            endif;
                            if ($s <= $K_LEAST):
                                throw new Exception("Structure keys count failed (least). //v $v //s $s");
                            endif;
                            if ($s >= $K_MOST):
                                throw new Exception("Structure keys count failed (most). //v $v //s $s");
                            endif;
                            if ($v > 0 && preg_match("($secure_purposes)", $param_list) && array_key_exists($KEY_V, $splited_array) && array_key_exists($KEY_S, $splited_array)):
                                throw new Exception("Missing some structure. //v $v //s $s #$op_stop");
                            endif;
                        endif;
                      }
                else:
                    throw new Exception("Limited operations.");
                endif;
              }
            catch (Exception $e)
              {
                $ats .= 'Something wrong: ' . $e->getMessage();
              }
            return $ats;
        else:
            return false;
        endif;
      }
    
    
    public function handle_query($my_array, $safe_code)
      {
        $private_safe_code = $this->safe_code;
        try
          {
            if ($private_safe_code == $safe_code): // some user security check
                $get_private_func = $this->build_query($my_array);
                return $get_private_func;
            else:
                throw new Exception("Something wrong.");
            endif;
          }
        catch (Exception $e)
          {
            return $e->getMessage();
          }
      }
  }

Затем вызовите функцию и выполните запрос.

//Insert into
$operation_examples[] = [
   "STRUCTURE" => [ // structure create only by programmer this part safe from inputs
       'INSERT INTO' => "basic_table (indx, values1, values2)",
       'VALUES' => " (:val1, :val2, :val3)"
   ], 
   "SETPARAM" => [// binding parameters (PDO prepared st.)
       ":val1" => "somevalue",
       ":val2" => "somevalue"
   ],
     "FMODE" => ["execute"] // switch to fetch if needed
];
//DELETE FROM 2 TIMES WITH INDEX
foreach (range(1, 2) as $i) {
$operation_examples[] = [
   "STRUCTURE" => [ // structure create only by programmer this part safe from inputs
       'DELETE FROM' => "basic_table",
       'WHERE' => "my_index = :myindex"
   ], 
   "SETPARAM" => [// binding parameters (PDO prepared st.)
       ":myindex" => "$i"
   ],
     "FMODE" => ["execute"] // switch to fetch if needed
];
}

$access = new my_op($pdo);
$doing_operation = $access->handle_query($operation_examples, "@@@");
print $doing_operation; // get created result with provided information.

Выход:

execute Success #1 # on //params(2) values(2) // PARAMLIST: INSERT INTO basic_table (indx, values1, values2)VALUES (:val1, :val2, :val3)//
VALUESLIST: [:val1 somevalue :val2 somevalue ] [3]
execute Success #2 # on //params(2) values(1) // PARAMLIST: DELETE FROM basic_tableWHERE my_index = :myindex//
VALUESLIST: [:myindex 1 ] [3]
execute Success #3 # on //params(2) values(1) // PARAMLIST: DELETE FROM basic_tableWHERE my_index = :myindex//
VALUESLIST: [:myindex 2 ] [3] 
```

1 ответ
1

Вы превратили простые подготовленные SQL-операторы в это чудовище, которое чрезвычайно трудно читать и которое открыто для SQL-инъекций. Убери это! Не идите по этому пути. Это совершенно не нужно.

Посмотрите, насколько простыми могут быть подготовленные операторы.

<?php

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'password', 'test');
$mysqli->set_charset('utf8mb4'); // always set the charset

$index = 1;
$stmt = $mysqli->prepare('UPDATE my_orders SET status = "waiting" WHERE myindex = ?');
$stmt->bind_param('s', $index);
$stmt->execute();

Вам не нужен этот сложный класс. Все, что вам нужно, это подготовленные отчеты. Если вам нужно что-то более простое с большей функциональностью, вы можете использовать PDO. Я настоятельно рекомендую по возможности использовать PDO.

Тот же код с использованием подготовленных операторов:

<?php

$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8mb4", 'user', 'password', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false
]);

$index = 1;
$stmt = $mysqli->prepare('UPDATE my_orders SET status = "waiting" WHERE myindex = ?');
$stmt->execute([$index]);

На несвязанном примечании, пожалуйста, используйте подходящую среду IDE с инструментом статического анализа, подсветкой и форматированием синтаксиса. Этот код в настоящее время не читается.

  • Что, если я изменю mysqli_query на подготовленные операторы pdo, но оставлю ту же функцию only_update, просто изменив ее синтаксисом и параметрами pdo?

    — Анархия

  • В чем смысл? MySQLi также подготовил операторы. Не создавайте функций, которые усложнят программирование. PDO проще по сравнению с MySQLi, но если вы сохраните текущий код, вы не увидите больших улучшений.

    — Дхарман

  • Я хочу делать операции только с функциями и массивами. Я редактирую свой код и изменяю его с помощью pdo, проверяю его и говорю, что вы думаете.

    — Анархия

  • @ZujisKarolis Это хуже, чем раньше. Вы по-прежнему не связываете никакие параметры. Имена переменных ничего не значат. Код все еще не отформатирован. У тебя очень странно filter_var_array($my_values,FILTER_SANITIZE_STRING,FILTER_FLAG_STRIP_HIGH);.

    — Дхарман

  • 1

    @ZujisKarolis Просто удали весь этот класс. Поверьте, вы действительно не хотите так поступать. Придерживайтесь старого доброго SQL. Что касается привязки параметров, я понятия не имею, что вы делаете, но она все еще уязвима для SQL-инъекций. Даже если вам удастся правильно их связать, весь класс все равно будет уязвим для SQL-инъекции. Не делай этого.

    — Дхарман

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *