Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Зацените класс для работы с БД

31K
19 января 2009 года
WarLooK
29 / / 23.06.2008
Собственно сабж

Идея родилась давно, некоторые аспекты навеяны реализацией Д. Котерова - DBSimple


db_generic.php
Код:
<?php

class db_generic
{  
    function &connect($link)
    {
        $parsedLink = db_generic::parseLink($link);
       
        if (!$parsedLink) return null;
       
        $class = 'db_'.$parsedLink['engine'];
        if (!class_exists($class))
        {
            $file = $class.".php";
            $base = basename($file);
            $dir = dirname(__FILE__);
            if (@is_file($path = "$dir/$base"))
            {
                require_once($path);
            }
            else
            {
                trigger_error("Error loading database driver: no file \"$file;\"", E_USER_ERROR);
                return null;
            }
        }
       
        $object = new $class($parsedLink);
        return $object;
    }
   
    function parseLink($link)
    {
        $parsedLink = null;
        $parsedLink = explode("/",$link);
        if (!$parsedLink) return null;
       
        $result['engine'] = $parsedLink[0];
        $result['host']   = $parsedLink[1];
        $result['db']     = $parsedLink[2];
        $result['user']   = $parsedLink[3];
        $result['pass']   = $parsedLink[4];
       
        return $result;
    }  
   
}
   
?>




db_mysql.php
Код:
<?php

require_once dirname(__FILE__).'/db_generic.php';

class db_mysql
{
    var $link;
    var $database;

    function __construct($link)
    {
        $ok = $this->link = @mysql_connect(
            $link['host'],
            $link['user'],
            $link['pass'],
            true
        );
        if (!$ok) trigger_error("Could not connect to database", E_USER_ERROR);
       
        $ok = @mysql_select_db($link['db'], $this->link);
        if (!$ok) trigger_error("Could not select database", E_USER_ERROR);
       
        $this->database = $link['db'];
    }
   
    function tableExists($name)
    {
        // Получаем список таблиц
        $result = mysql_list_tables($this->database);
        $rcount = mysql_num_rows($result);
        // Проверяем каждый элемент списка на совпадения
        for ($i=0;$i<$rcount;$i++)
        {
            if (mysql_tablename($result, $i)==$name) return true;
        }
        return false;
    }
   
    function arrayToString($array)
    {
        if (!is_array($array)) exit;
       
       
        $quote = '';
        if (is_string($array[0])) $quote = "'";
       
        $str='';
        foreach ($array as $a)
        {
            $str .= ", $quote".$a.$quote;
        }
       
        $str = substr($str,2);
        return $str;
    }
   
    function parseQuery($query, $args)
    {
       
        $acount = count($args);
        $parsedQuery = $query;
        echo '<hr><b>debug info db_mysql.php [62]:</b> <i>'.$parsedQuery.'</i><hr>';
        $parsedQuery = str_replace('?', '#?#', $parsedQuery);
       
        echo '<hr><b>debug info db_mysql.php [65]:</b> <i>'.$parsedQuery.'</i><hr>';
       
        for ($i=0;$i<$acount;$i++)
        {
            if (is_string($args[$i]))
            {
                $parsedQuery = preg_replace('/#{1}[\?]{1}#{1}/', "'".$args[$i]."'", $parsedQuery, 1);
            }
            if (is_int($args[$i]) or is_float($args[$i]))
            {
                $parsedQuery = preg_replace('/#{1}[\?]{1}#{1}/', $args[$i], $parsedQuery, 1);
            }
            if (is_array($args[$i]))
            {
                $parsedQuery = preg_replace('/#{1}[\?]{1}#{1}/', "(".$this->arrayToString($args[$i]).")", $parsedQuery, 1);
            }
        }
        echo '<hr><b>debug info db_mysql.php [82]:</b> <i>'.$parsedQuery.'</i><hr>';
        return $parsedQuery;
    }
   
    function isSelect($query)
    {
        $pos = stripos($query, 'select');
        if ($pos !== false)
            return true;
        else
            return false;
    }
   
    function query($query, $assoc = false, $args)
    {
        echo '<hr><b>debug info db_mysql.php [97]:</b> <i>'; print_r($args); echo'</i><hr>';
        $parsedQuery = $this->parseQuery($query, $args);
       
        $result = mysql_query($parsedQuery);
       
        if (!$result) exit(mysql_errno());
       
        $rows = null;
        if ($this->isSelect($parsedQuery))
        {
            if (!$assoc)
            {
                while ($row = mysql_fetch_row($result))
                    $rows[] = $row;
                return $rows;
            }
            else
            {
                while ($row = mysql_fetch_assoc($result))
                    $rows[] = $row;
                return $rows;
            }
        }
        else
        {
            echo '<hr><b>debug info db_mysql.php [97]:</b> <i> isSelect = '.$this->isSelect($parsedQuery).'</i><hr>';
            $rows = mysql_affected_rows();
            return $rows;
        }  
    }
   
    function selectCell($query, $assoc)
    {
        $args = func_get_args();
        $args = array_slice($args,2);
        $rows = $this->query($query, $assoc, $args);
        if (!is_array($rows)) return $rows;
        if (!count($rows)) return null;
        reset($rows);
            $row = current($rows);
            if (!is_array($row)) return $row;
            reset($row);
            return current($row);
    }
   
    function selectRow($query, $assoc)
    {
        $args = func_get_args();
        $args = array_slice($args,2);
        $rows = $this->query($query, $assoc, $args);
        if (!is_array($rows)) return $rows;
        if (!count($rows)) return array();
        reset($rows);
            return current($rows);
    }
   
    function select($query, $assoc)
    {
        $args = func_get_args();
        $args = array_slice($args,2);
        $rows = $this->query($query, $assoc, $args);
        return $rows;
    }
   
    function newId($table_name='', $id_name='')
    {
        if (!tableExists) return null;
       
        $newId = $this->selectCell("SELECT max($id_name) FROM $table_name");
        $newId++;
        return $newId;
    }
}

?>



Использование
 
Код:
$DB = db_generic::connect('mysql/localhost/test/root/'); // подключаемся к базе

$last_name = 'Petrov';
$name = 'Ivan'

$DB->select('SELECT * FROM some_table WHERE lastname = ? and first_name = ?', 1, $last_name, $name); // запрос к базе, результат возвращается в виде ассоциативного массива


Кому не лень - протестируйте плиз. Если будут вопросы - задавайте.

Лично мне не нравится в парсинге запросов подстановка параметров:
 
Код:
db_mysql->parseQuery

тока не знаю пока как сделать красивее и проще.

Принимается любая конструктивная критика и отзывы.
13
19 января 2009 года
RussianSpy
3.0K / / 04.07.2006
А еще было бы неплохо поддержку MS SQL Server и PostgreSQL. И метод обрабатывающий строки наподобие mysql_real_escape_string()... А пока выглядит как баловство для личных нужд ;)
5
19 января 2009 года
hardcase
4.5K / / 09.08.2005
Ничего особенного.
31K
20 января 2009 года
WarLooK
29 / / 23.06.2008
Цитата: RussianSpy
А еще было бы неплохо поддержку MS SQL Server и PostgreSQL. И метод обрабатывающий строки наподобие mysql_real_escape_string()... А пока выглядит как баловство для личных нужд ;)



Ну, собственно, это баловство и нужно было для личных нужд, просто чтобы писать в последствии не кучу строк кода на каждый запрос, а одну. Кроме MySQL не планируется ничего использовать в обозримом будущем, а поддержку mysql_real_escape_string() обязательно добавлю попозже.

244
20 января 2009 года
UAS
2.0K / / 19.07.2006
Ну ничего особого. Также лучше уже писать под PHP5. И использовать механизм исключений. Вот тогда все будет толково, а пока - поддержу RussianSpy
31K
20 января 2009 года
WarLooK
29 / / 23.06.2008
Цитата: UAS
Ну ничего особого. Также лучше уже писать под PHP5. И использовать механизм исключений. Вот тогда все будет толково, а пока - поддержу RussianSpy



Сейчас это скорее наметка, заготовка.
ToDo на ближайшую неделю:
1. Защита от SQL Injections
2. Нормальная обработка ошибок.

У меня есть вопрос, по поводу парсинга параметров SQL, сейчас это реализовано с помощью регулярных выражений и preg_replace заменяет все знаки "?" по очереди на переданные аргументы.

Возникает казус когда передается строковый параметр содержащий "?".

Во избежание этого вставлен такой костыль: "?" заменяется сначала на "#?#", с расчетом что такая комбинация в параметре - большая редкость, потом все идет по старой схеме.

Есть другие варианты решения? Потому что это далеко не идеальное и казус все же может возникнуть, хоть и вероятность минимальна. Хочется исключить этот баг полностью.

PS: всем высказавшимся большое спасибо.
PPS: сейчас это действительно "ничего особенного", но новичкам может быть полезно, да и мне хочется довести это дело до ума, чтобы была простая реализация и интуитивное использование.

244
20 января 2009 года
UAS
2.0K / / 19.07.2006
Цитата: WarLooK

Есть другие варианты решения? Потому что это далеко не идеальное и казус все же может возникнуть, хоть и вероятность минимальна. Хочется исключить этот баг полностью.


Исопльзовать sprintf и не париться. Т.е. все поступившие параметры обрабатываешь mysql_escape_string, затем обычным sprintf все вставляешь. Париться намного меньше и людям намного понятей, чем эти вопросики юзать.

Цитата: WarLooK

Во избежание этого вставлен такой костыль: "?" заменяется сначала на "#?#", с расчетом что такая комбинация в параметре - большая редкость, потом все идет по старой схеме.


Ну так все равно нельзя) А если будет такое?) Причем лишний раз будет пробегание по скрипту и замена. Вооот. Так что сделайте так, как я выше написал - самое оптимальное и разумное в данном случае

31K
20 января 2009 года
WarLooK
29 / / 23.06.2008
Цитата: UAS
Исопльзовать sprintf и не париться. Т.е. все поступившие параметры обрабатываешь mysql_escape_string, затем обычным sprintf все вставляешь. Париться намного меньше и людям намного понятей, чем эти вопросики юзать.

Ну так все равно нельзя) А если будет такое?) Причем лишний раз будет пробегание по скрипту и замена. Вооот. Так что сделайте так, как я выше написал - самое оптимальное и разумное в данном случае



Спасибо! Про sprintf - отлично. Про вопросики - у меня пережиток такой остался от java, очень привычно и %s, %d, ... даже на ум не приходили :)

mysql_escape_string - будет по умолчанию обязательно, сейчас еще хочу простенький errorHandler прикрутить, построенный на исключениях (чтобы была возможность эксепшены и на экран выводить и в логи писать по отдельности и вместе), или примерно так: обработчик выводит небольшое сообщение об ошибке, а в логи пишет полный stacktrace, дабы не показывать нехорошим людям что и где у нас сбоит, а для самих было бы все максимально прозрачно.

31K
20 января 2009 года
WarLooK
29 / / 23.06.2008
Со sprintf не все прозрачно.

Например, мне не понятно, как можно передать параметры в sprintf из другой функции, например:

 
Код:
function parse($query /*,...  тут может быть любое число параметров, которые нужно передать */)
{
    return sprintf($query /*,...  как все необязательные параметры передать сюда */);
}


Ранее использовалась func_get_args() и все параметры в парсер передавались как массив, тут же такая конструкция не прокатит, как быть?

Короче такой глобальный вопрос "как передавать необязательные параметры в виде необязательных параметров?".

Вынесу этот вопрос в отдельную тему пожалуй...
31K
21 января 2009 года
WarLooK
29 / / 23.06.2008
Если кому-то интересно, могу выложить обновления класса

Добавлено:

1. Настраиваемая обработка ошибок (отдельный, независимый класс), реализовано с помощью исключений, можно выводить на экран или писать в логи ошибку и ее стектрэйс.
2. Защита от SQL Injections.

Исправлено:

1. Парсинг запроса, теперь как sprintf.
400
26 января 2009 года
ArtemS2006
272 / / 12.01.2008
Мне было бы любопытно взгянуть, выкладывайте:)
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог