Recently wrote a class to work with MySql.
Please see if there are any holes in this class (sql injection).
Each line is signed to understand the code.

<?php Class bd{ private $mysqli; public function connect($cfg){//Функция подключения ,при вызове передаём настройки с данными от бд $this->mysqli = new mysqli($cfg['bd']['host'], $cfg['bd']['user'], $cfg['bd']['password'], $cfg['bd']['name']);//подключение к бд $this->mysqli->query("SET NAMES 'utf8'");//установка кодировки $this->mysqli->set_charset("utf8");//установка кодировки } public function get($table,$columns='*',$filter=false){//функция для получения данных из бд ,при вызове передаём: название таблица, [название столбцов], [дополнительный sql фильтр] $filter = ($filter !== false)? ' WHERE '.$this->mysqli->real_escape_string($filter):'';//получаем данные из бд return $this->changeToArray($this->mysqli->query("SELECT $columns FROM `$table`".$filter));//преобразовываем их в двух мерный массив и возращяем } public function add($table,$data){//добавления данных в бд, при вызове передаём: названия таблицы, массив вида 'название столбца'=>'данные' $keys = '';//тут будут хранится название столбцов $values = '';//тут будут хранится вносимые данные foreach($data as $key=>$val) {//перебор входящего массива if(gettype($val) !== 'string' || gettype($val) !== '"integer"') $val = json_encode($val);//если переданые данные не явлиются ,не строкой и не числом преобразовываем их в json-данные $keys .= '`'.$this->mysqli->real_escape_string($key).'`, ';//добавляем новое название столбца if(gettype($val) === 'integer') $values .= $this->mysqli->real_escape_string($val).' , ';//добавляем новые данные else $values .= '\''.$this->mysqli->real_escape_string($val).'\', ';//добавляем новые данные } $keys = substr($keys, 0, -2);//обрезаем лишний пробел и запятую $values = substr($values, 0, -2);//обрезаем лишний пробел и запятую $this->mysqli->query("INSERT INTO `$table` ($keys) VALUES ($values)");//добалвяем данные в БД } public function edit($table,$filter,$data){//функция редактирования, при вызове передаём: названия таблицы,фильтр, массив вида 'название столбца'=>'данные' $sql_data = '';//тут будет хранится sql запрос foreach($data as $key=>$val) {//переберам данные if(gettype($val) !== 'string' || gettype($val) !== '"integer"') $val = json_encode($val);//если переданые данные не явлиются ,не строкой и не числом преобразовываем их в json-данные if(gettype($val) === 'integer') $sql_data = '`'.$this->mysqli->real_escape_string($key).'`='.$this->mysqli->real_escape_string($val).', ';//добавляем новые данный в sql запрос else $sql_data = '`'.$this->mysqli->real_escape_string($key).'`=\''.$this->mysqli->real_escape_string($val).'\', ';//добавляем новые данный в sql запрос } $sql_data = substr($sql_data, 0, -1);//обрезаем линюю запятую $this->mysqli->query("UPDATE `$table` SET $sql_data WHERE ".$filter);//обновляем данные в бд } public function delete($table,$filter=false){//функция удаления ,при вызове передаём: название таблицы, [фильтр] $filter = ($filter !== false)? ' WHERE '.$this->mysqli->real_escape_string($filter):'';//если фильтр равен false присваевам ему постое значение $this->mysqli->query("DELETE FROM `$table`".$filter);//удаляем данные из бд } private function changeToArray($result) {//внутреняя функция для преобразования sql данных в двухмерный массив if($result===false) return array(); $results = array(); while(($row = $result->fetch_assoc()) != false) $results[] = $row; return $results; } public function __destruct() {//функция закрытия соединения. $this->mysqli->close(); } } ?> 
  • 3
    No work with placeholders => immediately in / dev / null. - user6550
  • 3
    Mr_Epic, for storing and uploading code to the public access, there are special services - closing google code, bitbucket, github. - etki
  • instead of bd should be db =) "data base". Here, filled in on pastebin: pastebin.com/X5Dq7DFi - user26699
  • everything is bad #ооп #кэнтБэк #мартин #codingStandards is to read - username

2 answers 2

I wanted to answer and forgot. I do not pretend to a complete analysis, I just show the places that, in my opinion, should be shipped as soon as possible.

 <?php Class bd{ 

The first problem is formatting. Class always written in small letters, the name of the class - with a large, so-called. StudlyCaps , while the class name should clearly indicate what it does (not bd , but DatabaseApi , at least). Opening quotes taken to move to a new line. These are all trifles, but I see no reason not to do them right away. The relevant standards are PSR-1 , PSR-2 , they are now used by the entire professional community. To check your own code for compliance with formatting standards, you can use the PHP Code Sniffer utility ( github / packagist ).
Here at this stage:

 public function get($table,$columns='*',$filter=false){ 

The code is already becoming unreadable. You understand that saving gaps does not make sense?

Further - these are the constructions.

 $filter !== false 

Must be:

 !$filter 

It makes no sense to check for an exact match for false , if all other false values ​​are exactly the same cannot be used. Ternary operators are often simplified:

 $string = $probablyFalse ?: ' WHERE ...'; 

With this use, it will return either false , which, when concatenated, will turn into an empty string, or the necessary string. If desired, you can still put (string) before ternarkoy.

In this case, $filter will take either the value false , or ' WHERE ...' , and when concatenated, false will become an empty string.

 //преобразовываем их в двух мерный массив и возвращаем ^ ^ //если переданные данные не являются ,не строкой и не числом преобразовываем их в json-данные ^ ^^ ^ 

The rules of an ordinary human language, unfortunately, must be maintained even in the comments.

 gettype($val) !== 'string' || gettype($val) !== '"integer"' 

Another wrong design. First, integer in quotes, and secondly, what happens if you make a mistake on a character in a string with a type (in fact, this is what happened with quotes)? The program will not work correctly, and debugging will be quite painful. At the same time, PHP has ready-made functions for checking any type:

 !is_string($val) || !is_int($val) 

The next thing that strained:

 $sql_data = substr($sql_data, 0, -1);//обрезаем лишнюю запятую 

You should not cut off the extra comma, you should not generate it at all . In all languages, there is a glueing function for a string array, in PHP this is an implode($delimiter, $list) function that will glue this array to you.

Concerning such constructions:

 "INSERT INTO `$table` ($keys) VALUES ($values)" 

There is a function sprintf , with which it is customary to do such things. It will, of course, produce an absolutely identical effect, but this is a somewhat more readable approach:

 $query = sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $table, $keys, $values); 

By variable name: write them completely, $value instead of $val , $config instead of $cfg . From the interpreter will not lose anything. But reading will be more pleasant.

The last formatting and general style is the forbidden trick:

 while(($row = $result->fetch_assoc()) != false) $results[] = $row; 

In an amicable way, it should look like this:

 while ($row = $result->fetch_assoc()) { // false-то зачем? $results[] = $row; } 

Write as if your potential reader raises the site for the twentieth hour or spilled half a bottle of whiskey (and so it will be sooner or later).

Now, by filling: you make a class for convenient work with the database, abstracting from it. But for some reason you are forced to transfer the condition and the selection of fields in a string. Why should I use such a class if I still write the half-line that I would have written in whole? In the opencard there is the same example, brought to the point of absurdity:

 // index.php?route=product/product&product_id=42&limit=2 $url = $this->url->link('product/product', 'product_id=42&limit=2'); 

Here is the condition:

 if(gettype($val) !== 'string' || gettype($val) !== '"integer"') $val = json_encode($val); 

Only by happy coincidence will it skip null , as it should, and at the same time will return false and true , and only the good will of MySQL would convert them to 0 and 1 respectively. "Would" - because the line below the code will perceive them and process them as a line (plus, again, you need a sprintf here, it is difficult to read):

 else $sql_data = '`'.$this->mysqli->real_escape_string($key).'`=\''.$this->mysqli->real_escape_string($val).'\', '; 

As a result, if false sent to the database, the string 'false' will return from there, which will never be perceived by PHP as false . This is data loss.

According to the names of the methods: there is a generally accepted unwritten convention of the name CRUD (create, read, update, delete), why call the methods differently?

The last one is this one:

 public function connect($cfg){//Функция подключения ,при вызове передаём настройки с данными от бд $this->mysqli = new mysqli($cfg['bd']['host'], $cfg['bd']['user'], $cfg['bd']['password'], $cfg['bd']['name']);//подключение к бд 

First, why is there an extra level of nesting with the 'bd' key at all? Secondly, why not prevent errors at an early level and force the user to pass the required parameters?

 public function connect($host, $name, $user, $password = null) 

And, of course, they have already said about injections and placeholders.

  • Thank you very much for such a detailed answer to the connect ($ cfg) account: I am writing it for myself, and I always use cfg in scripts, of one kind, so I did this. - Mr_Epic

Class for working with mysql!
I love classes for working with mysql! Especially for lunch!

In fact, there are only a few mistakes, only two:

  1. The principal architectural: it is, in fact, no class for working with mysql, but rather such an under-CRUD.
  2. Purely technical: this class cannot be used as a principle - it’s enough to pass a condition containing a string to WHERE and see what happens.

Well, he does not provide any protection against injections, but this is a trifle, against the background of the rest.

On the first point. The class for working with a database should provide only the execution of a query with data substitution through placeholders, and the return of the result in the correct format. As we can see, the class does neither the one nor the other, in particular, the data format is only one. Although they should be at least 4, but in an amicable way and more.

At the same time, a class is threatening a CRUD / query builder, but without a foundation in the form of a class for working with a database, it is simply not viable.

On the second issue. The problem comes from a very common misconception that the mysql (i) _real_escape_string function protects against any kind of injection, which, of course, is completely different.

To remedy the situation, I recommend practicing the use of PDO, which is just a full-fledged class for working with DB. And you can even try to write a CRUD on its basis, but only without the monstrous get function, about which I will write a little later.

Another of the minor, but important notes - in the class is missing error reporting. Fortunately, in mysqli it is literally one line,

 mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); 

Which is written BEFORE the connection