СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Удобный php класс для работы с MySQL в черновиках Из песочницы

В последнее время задался вопросом написания php класса для работы с базой данных mysql. Мои требования – удобство формирования запроса к базе данных и возможность сортировки и записи данных через массив, что упрощает работу с БД.

В результате появился вот такой класс:

class Db {

    private $where;
    private $select = '*';
    private $limit;
    private $order_by;
    private $query;
    private $set;

    private function query_string($table){
        $query = ($this->query) ? $this->query : "SELECT $this->select FROM $table WHERE $this->where $this->order_by $this->limit";
        return $query;
    }
    function query($text){
        $this->query = $text;
        return $this;
    }
    function where_text($text){
        $operator = 'AND';
        if (!$this->where){
            $this->where = $text;
        } else {
            $this->where .= $operator." ".$text;
        }
        return $this;
    }
    function order_by_text($text){
        if (!$this->order_by){
            $this->order_by = ' ORDER BY '.$text;
        } else {
            $this->order_by .= ', '.$text;
        }
    }
    function connect($host,$login_mysql,$password_mysql,$baza_name) {
        $db = @mysql_connect("$host", "$login_mysql", "$password_mysql");
        mysql_set_charset('utf8',$db);
        if (!$db) exit("<p>Sorry, not available MySQL server</p>");
        if (!@mysql_select_db($baza_name,$db)) exit("<p>Unfortunately, the database is not available</p>");
    }
    function select($column){
        $this->select = $column;
        return $this;
    }
    function limit($start,$count){
        $this->limit = ' LIMIT '.$start.','.$count;
        return $this;
    }
    function order_by($key){
        if (is_array($key)){
            foreach ($key as $column => $value){
                if (!$this->order_by){
                    $this->order_by = ' ORDER BY '.$column.' '.$value;
                } else {
                    $this->order_by .= ', '.$column.' '.$value;
                }
            }
        } else {
            if (!$this->order_by){
                $this->order_by = ' ORDER BY '.$key;
            } else {
                $this->order_by .= ', '.$key;
            }
        }
        return $this;
    }
    function where($key,$compare = '='){
        $operator = 'AND';
        if (is_array($key)){
            foreach ($key as $column=>$value){
                if (!$this->where){
                    $this->where = $column.$compare."'".$value."' ";
                } else {
                    $this->where .= $operator." ".$column.$compare."'".$value."' ";
                }
            }
        } else {
            $key = explode(',',$key);
            if (!$this->where){
                $this->where = $key[0].$compare."'".$key[1]."' ";
            } else {
                $this->where .= $operator." ".$key[0].$compare."'".$key[1]."' ";
            }
        }
        return $this;
    }
    function search($column,$search){
        $operator = 'AND';
        $search = strtr($search,array(" "=>" +"));
        $search = '+'.$search;
        $w = 'MATCH('.$column.") AGAINST('".$search."' IN BOOLEAN MODE)";
        if (!$this->where){
            $this->where = $w;
        } else {
            $this->where .= $operator." ".$w;
        }
        return $this;
    }
    function lines($table,$ext = true){
        $msql_query = mysql_query($this->query_string($table));
        $line = mysql_fetch_assoc($msql_query);
        if ($ext)$this->exit_function();
        return $line;
    }
    function multiline($table,$ext = true){
        $msql_query = mysql_query($this->query_string($table));
        $multiline = array();
        while($arr = mysql_fetch_assoc($msql_query)) $multiline[] = $arr;
        if ($ext)$this->exit_function();
        return $multiline;
    }
    function num_rows($table,$ext = true){
        $msql_query = mysql_query($this->query_string($table));
        $num_rows = mysql_num_rows($msql_query);
        if ($ext)$this->exit_function();
        return $num_rows;
    }
    function insert($table){
        $query = 'INSERT '.$table.$this->set;
        $q = mysql_query($query);
        $this->exit_function();
        return  $q ? mysql_insert_id() : 'ERROR INSERT MYSQL';
    }
    function update($table){
        $query = 'UPDATE '.$table.$this->set.' WHERE '.$this->where;
        $this->exit_function();
        return mysql_query($query);
    }
    function set($value){
        $operator = ',';
        if (is_array($value)){
            foreach ($value as $column=>$val){
                if (!$this->set){
                    $this->set = ' SET '.$column.'='."'".$val."' ";
                } else {
                    $this->set .= $operator." ".$column.'='."'".$val."' ";
                }
            }
        } else {
            $value = explode(',',$value);
            if (!$this->set){
                $this->set = ' SET '.$value[0].'='."'".$value[1]."' ";
            } else {
                $this->where .= $operator." ".$value[0].'='."'".$value[1]."' ";
            }
        }
        return $this;
    }
    private function exit_function(){
        $this->where = '';
        $this->select = '*';
        $this->limit = '';
        $this->order_by = '';
        $this->query = '';
        return $this;
    }
}


Вот такой класс, а теперь подробнее о функциях.

Для подключения к базе данных (БД) объявляем класс Db и с помощью функции connect подключаемся к БД. Значения для функции: connect(Хост, Имя пользователя, пароль, имя базы данных);

$db = new Db;
$db->connect('localhost','root','',test);

После подключения можно приступать к работе. Для сортировки с помощью массива объявляю массив, а для записи данных использую функцию where, в которую передаю массив:

$where = array();
$where['status'] = 1;
$where['category'] = 2;
$db->where($where);

Далее функцию where опишу подробнее. А сейчас объявим из каких полей делать выборку, для этого используем функцію select, по умолчанию ее значение — * и ее можно не объявлять.

$db->select('msg,zag');

По умолчанию where использует знак = для сравнения, но если нужно другое значение, например больше или меньше то можно вторым значением передать нужный нам знак сравнения. В функции можно использовать как строчный вариант сравнения, например 'price,1000', так и массив как в примере выше и всему массиву будет подставлен переданный знак.

$db->where('price,1000','>');

Для сортировки значений внутри выборки используем функцию order_by, для нее также формируем массив:

$order_by = array();
$order_by['price'] = 'DESC';
$order_by['data'] = 'ASC';
$db->order_by($order_by);

Эта функция сформирует такой запрос: ORDER BY price DESC, data ASC. В ORDER BY можно подставлять свои аргументы используя функцию order_by_text():

$db->order_by_text('id DESC');

Теперь функции вывода данных — их три: num_rows, multiline, lines:

$q['num'] = $db->num_rows('data',false);

Функция num_rows показывает количество записей, соответствующих запросу, false — означает, что объявленные данные для сортировки стирать не нужно. Без false все объявленные данные стираются и их нужно объявлять заново. ‘data’ – таблица в БД к которой делаем запрос.

$q['multiline'] = $db->multiline('data',false);

Функция multiline выводит массивы данных соответствующих запросу.

$q['lines'] = $db->lines('data');

Функция lines выводит один первый массив соответствующий запросу.

Функции формируют такой запрос:
SELECT msg,zag FROM data WHERE status='1' AND category='2' AND price>'1000' ORDER BY price DESC, data ASC, id DESC

Функция limit — устанавливает LIMIT в запрос, где 2 — стартовый номер для вывода (т.е. вывод начнется со 2-го элемента соответствующего запросу), 4 — количество элементов в массиве.

$q['multiline'] = $db->select('id')->where($where)->where_text('id IN (1,2,3,4)')->order_by($order_by)->limit(2,4)->multiline('data');

Select, where и order_by объявляем заново, т.к. предыдущая функция lines была без false — это означает, что все данные для сортировки были стерты. Формирует такой запрос:

SELECT id FROM data WHERE status='1' AND category='2' AND id IN (1,2,3,4) ORDER BY price DESC, data ASC LIMIT 2,4

Здесь была использована еще одна функция where_text – она нужна для того, чтобы самому написать запрос в WHERE

Функция search:

$q['search'] = $db->search('test,text','тест поиска')->multiline('data');

Формирует такой запрос: SELECT * FROM data WHERE MATCH(test,text) AGAINST('+тест +поиска' IN BOOLEAN MODE). Ищем в таблице data, в полях test и text совпадения со словами тест и поиска.

Можно и так:

$db->select('id')->where($where)->search('test,text','тест поиска')->order_by($order_by)->multiline('data');

$order_by и $where берутся из предыдущих объявленных переменных и получаем такой запрос:

SELECT id FROM data WHERE status='1' AND category='2' AND MATCH(test,text) AGAINST('+тест +поиска' IN BOOLEAN MODE) ORDER BY price DESC, data ASC

Для записи в базу данных: используем функцию set — передача параметров сохранения массивом и insert — запись в базу данных:

$set['test'] = 'test';
$set['text'] = 'text';
$id = $db->set($set)->insert('data'); 

Функция вернет id записи в переменную $id. Запрос будет выглядеть так: INSERT data SET test='test', text='text'

Для внесения изменений в строку используем функцию update:

$set = array();
$set['category'] = 2;
$set['test'] = 'test change';
$set['text'] = 'text change';
$save = $db->where('id,1')->set($set)->update('data');
$message = $save ? "UPDATE OK" : "ERROR UPDATE"; 

Если запись прошла успешно, $message вернет UPDATE OK иначе ERROR UPDATE. Запрос будет таким:

UPDATE data SET test='test', text='text', category='2', test='test change', text='text change' WHERE id='1'

И еще одна функция query — в ней можно написать свой запрос select полностью:

$q = $db->query("SELECT * FROM data WHERE status='1' AND category='2'")->multiline('data');

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

$db2 = new Db();
$db2->connect('localhost','root','','test');
$q['db2'] = $db2->where('id,2')->lines('wp_posts');
print "<pre>";
print_r ($q);


Такой вот класс у меня получился. Не буду сравнивать его с другими класcами для работы с БД, знаю — их множество. Публикую его, т.к. думаю, что, может, кому-то пригодится, а также хочется конструктивной критики, которая позволит улучить этот код.

комментарии (28)

+14
bromzh ,   * (был изменён)
Я хоть и не пхпшник, но даже я знаю про PDO.

хочется конструктивной критики, которая позволит улучить этот код

надо научиться гуглить, прежде чем писать миллионный велосипед «MySQL класс для пхп»
+8
leviothan ,  
Я так понимаю, что вы фреймворки не используете? Рекомендую начать это делать прямо сегодня и забыть про этот класс.
+4
levchick ,   * (был изменён)
Если настолько хочется написать что-то свое с нуля, то в начале я бы посоветовал хоть немного разобраться с матчастью: почитать хорошие практики (PDO или mysqli на худой конец, разделение ответственности и тд), почитать про безопасность (всеми любимые инъекции например), посмотреть реализацию подобного в других библиотеках…
+8
HunterNNm ,  
mysql_connect? Вы шутите? Напомню — на улице 2016 год и уже вышел PHP 7…
+3
+4 –1
zenn ,   * (был изменён)
Зачем мои глаза это увидели, как теперь это забыть? Конструктивная критика, к сожалению тут не уместна, практически каждая строчка велосипеда выше нарушает какой-либо принцип, методику или подходы «best practice».
Я просто оставлю это здесь:
+3
kovalevsky ,  
Можно для начала хотя-бы www.phptherightway.com
+4
nazarpc ,  
mysql_query()

Шел 2016 год…
0
+4 –4
Akuma ,  
Ну что вы напали на человека, можно подумать никто так не делал :) Да все писали свой «класс для MySQL», а то и не один.

К автору:
Молодец, как ни странно. Прислушайся к комментариям выше и почитай указанные ссылки.
Все, что может делать твой класс умеет делать PDO, только лучше, быстрее и безопаснее. Поэтому лучше используй его или, как сказали выше, какую-нибудь ORM.
По коду: приучивайся писать комментарии. По качеству — да все нормально, никого не слушай, все так делали. Можешь ничего не исправлять даже, просто оставь этот файлик и открой через годик-два, сам все поймешь.
Ну и да, не используй этот класс на реальных проектах, т.к. будет больше проблем чем пользы.
+1
Lure_of_Chaos ,  
Настоящий PHPшник обязан в жизни написать свой фреймворк :)
Так что, есть к чему стремиться
+2
Pinsky ,  
Забыв про PSR и PHPDoc
0
+1 –1
Lure_of_Chaos ,  
Исходник — лучшая документация (ц)
0
+1 –1
sl4mmer ,   * (был изменён)
Мне, когда я впервые столкнулся с веб-разработкой вообще и php в частности ( 2004 или 2003 год не помню уже), SQL показался слишком сложным и я за пару недель написал свою базу данных, с храненим данных на диске и простым tcp-based протоколом. Простительный велосипед с учетом что мне было в районе шестнадцати лет
+2
Pinsky ,  
Написать свою БД — задача каждого программиста
+2
Blumfontein ,  
>> Ну что вы напали на человека, можно подумать никто так не делал :) Да все писали свой «класс для MySQL», а то и не один.

Писали может и все, а на хабр постили не все…
+1
bat ,  
тогда и хабра не было )
+1
kroshanin ,  
хочется конструктивной критики, которая позволит улучить этот код

Про актуальность написания подобного класса говорить не будут, просто приведу несколько своих замечаний по коду:
01) Функции, которые могут кидать исключения (например, mysql_connect) следует оборачивать в блоки try-catch

02) Тексты запросов вида «SELECT $this->select FROM $table WHERE $this->where $this->order_by $this->limit» — я бы так не делал, поскольку это потенциальная угроза XSS-атак.

03) Сообщения об ошибках в html-разметке тоже плохая практика, поскольку вам не всегда будет требоваться выводить ошибки именно на экран. Тексты сообщений лучше вообще вынести в константы класса (имхо).

04) В строке $db = mysql_connect("$host", "$login_mysql", "$password_mysql"); не совсем понял, зачем нужно переменные заключать в кавычки, без них тоже все должно заработать. Хотя, возможно, я чего-то не знаю.
+4
zenn ,  
02) Тексты запросов вида «SELECT $this->select FROM $table WHERE $this->where $this->order_by $this->limit» — я бы так не делал, поскольку это потенциальная угроза XSS-атак.

С добрым утром, просыпайтесь, не XSS а SQL-Injection :)
0
TimsTims ,  
XSS тоже в целом не отменяет, если вывод идет прямо из базы :)
+1
+2 –1
Lure_of_Chaos ,  
Этот класс просто прекрасен… )
Кроме упомянутого, порадовало
exit("<p>Sorry, not available MySQL server</p>");

Что вы, какой MVC, какая локализация?
Я думаю, хакеру пользователю очень будет приятно узнать о Ваших проблемах :)
+3
HunterNNm ,  
У меня другое место «любимое» — при объявлении метода
function connect($host,$login_mysql,$password_mysql,$baza_name)

параметр
$baza_name
+1
Lure_of_Chaos ,  
:))) Датабаза )
+1
Lure_of_Chaos ,  
з.ы. не говоря уж о том, что страница при ошибке получится недозаполненной, в итоге — скособоченной :)
+1
maxru ,  
А я покушал :3
+1
MetaDone ,  
Хорошая попытка, опыт получен, а теперь посмотрите на кошерные примеры сборщика запросов
github.com/auraphp/Aura.Sql
github.com/auraphp/Aura.SqlQuery
+2
Pinsky ,  
После этого и не любят PHP-шников.

Решили повелосипедить — сделайте что-нибудь в духе RedBean, только лучше.

Сделайте вариант работающий с Postgress и SQLite, а не только мускуль.
PSR почитайте и PHPDoc пишите — это действительно важно.
0
+1 –1
enleur ,  
Шел 2016й год…
+1
Lure_of_Chaos ,  
https://habrahabr.ru/post/277095/#comment_8770471
А вы оригинальны как никогда
+1
medved6216 ,   * (был изменён)
Это просто фантастика! [Сарказм одобрен]
Уважаемый автор, на дворе 2016 год и мне стыдно за Вас. Начиная, что-то делать, ознакомьтесь пожалуйста с документацией по функциям, например. Там вроде четко написано:
Внимание
Данное расширение устарело, начиная с версии PHP 5.5.0, и будет удалено в будущем. Используйте вместо него MySQLi или PDO_MySQL. Смотрите также инструкцию MySQL: выбор API и соответствующий FAQ для получения более подробной информации. Альтернативы для данной

Мало того, что оно устарело, т.к. оно и не безопасно.
Если решили писать свой класс по работе с БД, то используйте лучше PDO и учитывайте не только MySQL-драйвер, а ещё и PostgreSQL — это минимум. У них разный синтаксис SQL в формировании некоторых запросов. И советую для себя разобрать класс отвечающий за БД в любом CMS.