Пользователь
0,0
рейтинг
6 февраля 2012 в 09:42

Разработка → Почему стоит пользоваться PDO для работы с базой данных перевод


Перевод статьи Why you Should be using PHP’s PDO for Database Access.

Множество PHP-разработчиков привыкли использовать для работы с базами данных расширения mysql и mysqli. Но с версии 5.1 в PHP существует более удобный способ — PHP Data Objects. Этот класс, сокращенно именуемый PDO, предоставляет методы для работы с объектами и prepared statements, которые заметно повысят вашу продуктивность!

Введение в PDO


«PDO – PHP Data Objects – это прослойка, которая предлагает универсальный способ работы с несколькими базами данных.»

Заботу об особенностях синтаксиса различных СУБД она оставляет разработчику, но делает процесс переключения между платформами гораздо менее болезненным. Нередко для этого требуется лишь изменить строку подключения к базе данных.

Эта статья написана для людей, которые пользуются mysql и mysqli, чтобы помочь им в переходе на более мощный и гибкий PDO.

Поддержка СУБД


Это расширение может поддерживать любую систему управления базами данных, для которой существует PDO-драйвер. На момент написания статьи доступны следующие драйвера:
  • PDO_CUBRID ( CUBRID )
  • PDO_DBLIB ( FreeTDS / Microsoft SQL Server / Sybase )
  • PDO_FIREBIRD ( Firebird/Interbase 6 )
  • PDO_IBM ( IBM DB2 )
  • PDO_INFORMIX ( IBM Informix Dynamic Server )
  • PDO_MYSQL ( MySQL 3.x/4.x/5.x )
  • PDO_OCI ( Oracle Call Interface )
  • PDO_ODBC ( ODBC v3 (IBM DB2, unixODBC and win32 ODBC) )
  • PDO_PGSQL ( PostgreSQL )
  • PDO_SQLITE ( SQLite 3 and SQLite 2 )
  • PDO_SQLSRV ( Microsoft SQL Server )
  • PDO_4D ( 4D )
Впрочем, не все из них есть на вашем сервере. Увидеть список доступных драйверов можно так:
print_r(PDO::getAvailableDrivers());


Подключение


Способы подключения к разным СУБД могут незначительно отличаться. Ниже приведены примеры подключения к наиболее популярным из них. Можно заметить, что первые три имеют идентичный синтаксис, в отличие от SQLite.
try {  
  # MS SQL Server и Sybase через PDO_DBLIB  
  $DBH = new PDO("mssql:host=$host;dbname=$dbname", $user, $pass);  
  $DBH = new PDO("sybase:host=$host;dbname=$dbname", $user, $pass);  
  
  # MySQL через PDO_MYSQL  
  $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);  
  
  # SQLite  
  $DBH = new PDO("sqlite:my/database/path/database.db");  
}  
catch(PDOException $e) {  
    echo $e->getMessage();  
}

Пожалуйста, обратите внимание на блок try/catch – всегда стоит оборачивать в него все свои PDO-операции и использовать механизм исключений (об этом чуть дальше).

$DBH расшифровывается как «database handle» и будет использоваться на протяжении всей статьи.

Закрыть любое подключение можно путем переопределения его переменной в null.
# закрывает подключение  
$DBH = null;

Больше информации по теме отличительных опций разных СУБД и методах подключения к ним можно найти на php.net.

Исключения и PDO


PDO умеет выбрасывать исключения при ошибках, поэтому все должно находиться в блоке try/catch. Сразу после создания подключения, PDO можно перевести в любой из трех режимов ошибок:
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );  
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );  
$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

Но стоит заметить, что ошибка при попытке соединения будет всегда вызывать исключение.

PDO::ERRMODE_SILENT


Это режим по умолчанию. Примерно то же самое вы, скорее всего, используете для отлавливания ошибок в расширениях mysql и mysqli. Следующие два режима больше подходят для DRY программирования.

PDO::ERRMODE_WARNING


Этот режим вызовет стандартный Warning и позволит скрипту продолжить выполнение. Удобен при отладке.

PDO::ERRMODE_EXCEPTION


В большинстве ситуаций этот тип контроля выполнения скрипта предпочтителен. Он выбрасывает исключение, что позволяет вам ловко обрабатывать ошибки и скрывать щепетильную информацию. Как, например, тут:
# подключаемся к базе данных  
try {  
  $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);  
  $DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );  
  
  # Черт! Набрал DELECT вместо SELECT!  
  $DBH->prepare('DELECT name FROM people')->execute();  
}  
catch(PDOException $e) {  
    echo "Хьюстон, у нас проблемы.";  
    file_put_contents('PDOErrors.txt', $e->getMessage(), FILE_APPEND);  
}

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

Insert и Update


Вставка новых и обновление существующих данных являются одними из наиболее частых операций с БД. В случае с PDO этот процесс обычно состоит из двух шагов. (В следующей секции все относится как к UPDATE, так и INSERT)

Тривиальный пример вставки новых данных:
# STH означает "Statement Handle"  
$STH = $DBH->prepare("INSERT INTO folks ( first_name ) values ( 'Cathy' )");  
$STH->execute();

Вообще-то можно сделать то же самое одним методом exec(), но двухшаговый способ дает все преимущества prepared statements. Они помогают в защите от SQL-инъекций, поэтому имеет смысл их использовать даже при однократном запросе.

Prepared Statements


Использование prepared statements укрепляет защиту от SQL-инъекций.

Prepared statement — это заранее скомпилированное SQL-выражение, которое может быть многократно выполнено путем отправки серверу лишь различных наборов данных. Дополнительным преимуществом является невозможность провести SQL-инъекцию через данные, используемые в placeholder’ах.

Ниже находятся три примера prepared statements.
# без placeholders - дверь SQL-инъекциям открыта!  
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ($name, $addr, $city)");  
  
# безымянные placeholders  
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)"); 
 
# именные placeholders 
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");

Первый пример здесь лишь для сравнения, его стоит избегать. Разница между безымянными и именными placeholder’ами в том, как вы будете передавать данные в prepared statements.

Безымянные placeholder’ы


# назначаем переменные каждому placeholder, с индексами от 1 до 3  
$STH->bindParam(1, $name);  
$STH->bindParam(2, $addr);  
$STH->bindParam(3, $city);  
  
# вставляем одну строку  
$name = "Daniel"  
$addr = "1 Wicked Way";  
$city = "Arlington Heights";  
$STH->execute();  
  
# вставляем еще одну строку, уже с другими данными  
$name = "Steve"  
$addr = "5 Circle Drive";  
$city = "Schaumburg";  
$STH->execute();

Здесь два шага. На первом мы назначаем всем placeholder’ам переменные (строки 2-4). Затем назначаем этим переменным значения и выполняем запрос. Чтобы послать новый набор данных, просто измените значения переменных и выполните запрос еще раз.

Если в вашем SQL-выражении много параметров, то назначать каждому по переменной весьма неудобно. В таких случаях можно хранить данные в массиве и передавать его:
# набор данных, которые мы будем вставлять
$data = array('Cathy', '9 Dark and Twisty Road', 'Cardiff');  
  
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (?, ?, ?)");  
$STH->execute($data);

$data[0] вставится на место первого placeholder’а, $data[1] — на место второго, и т.д. Но будьте внимательны: если ваши индексы сбиты, это работать не будет.

Именные placeholder’ы


# первым аргументом является имя placeholder’а
# его принято начинать с двоеточия
# хотя работает и без них
$STH->bindParam(':name', $name);

Здесь тоже можно передавать массив, но он должен быть ассоциативным. В роли ключей должны выступать, как можно догадаться, имена placeholder’ов.
# данные, которые мы вставляем  
$data = array( 'name' => 'Cathy', 'addr' => '9 Dark and Twisty', 'city' => 'Cardiff' );  
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");  
$STH->execute($data);

Одним из удобств использования именных placeholder’ов является возможность вставки объектов напрямую в базу данных, если названия свойств совпадают с именами параметров. Вставку данных, к примеру, вы можете выполнить так:
# класс для простенького объекта  
class person {  
    public $name;  
    public $addr;  
    public $city;  
  
    function __construct($n,$a,$c) {  
        $this->name = $n;  
        $this->addr = $a;  
        $this->city = $c;  
    }  
    # так далее...  
}  
  
$cathy = new person('Cathy','9 Dark and Twisty','Cardiff');  
  
# а тут самое интересное  
$STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values (:name, :addr, :city)");  
$STH->execute((array)$cathy);

Преобразование объекта в массив при execute() приводит к тому, что свойства считаются ключами массива.

Выборка данных



Данные можно получить с помощью метода ->fetch(). Перед его вызовом желательно явно указать, в каком виде они вам требуются. Есть несколько вариантов:
  • PDO::FETCH_ASSOC: возвращает массив с названиями столбцов в виде ключей
  • PDO::FETCH_BOTH (по умолчанию): возвращает массив с индексами как в виде названий стобцов, так и их порядковых номеров
  • PDO::FETCH_BOUND: присваивает значения столбцов соответствующим переменным, заданным с помощью метода ->bindColumn()
  • PDO::FETCH_CLASS: присваивает значения столбцов соответствующим свойствам указанного класса. Если для какого-то столбца свойства нет, оно будет создано
  • PDO::FETCH_INTO: обновляет существующий экземпляр указанного класса
  • PDO::FETCH_LAZY: объединяет в себе PDO::FETCH_BOTH и PDO::FETCH_OBJ
  • PDO::FETCH_NUM: возвращает массив с ключами в виде порядковых номеров столбцов
  • PDO::FETCH_OBJ: возвращает анонимный объект со свойствами, соответствующими именам столбцов
На практике вам обычно хватит трех: FETCH_ASSOC, FETCH_CLASS, и FETCH_OBJ. Чтобы задать формат данных, используется следующий синтаксис:
$STH->setFetchMode(PDO::FETCH_ASSOC);

Также можно задать его напрямую при вызове метода ->fetch().

FETCH_ASSOC


При этом формате создается ассоциативный массив с названиями столбцов в виде индексов. Он должен быть знаком тем, кто использует расширения mysql/mysqli.
# поскольку это обычный запрос без placeholder’ов,
# можно сразу использовать метод query()  
$STH = $DBH->query('SELECT name, addr, city from folks');  
  
# устанавливаем режим выборки
$STH->setFetchMode(PDO::FETCH_ASSOC);  
  
while($row = $STH->fetch()) {  
    echo $row['name'] . "\n";  
    echo $row['addr'] . "\n";  
    echo $row['city'] . "\n";  
}

Цикл while() переберет весь результат запроса.

FETCH_OBJ


Данный тип получения данных создает экземпляр класса std для каждой строки.
# создаем запрос
$STH = $DBH->query('SELECT name, addr, city from folks');  
  
# выбираем режим выборки  
$STH->setFetchMode(PDO::FETCH_OBJ);  
  
# выводим результат
while($row = $STH->fetch()) {  
    echo $row->name . "\n";  
    echo $row->addr . "\n";  
    echo $row->city . "\n";  
}


FETCH_CLASS


При использовании fetch_class данные заносятся в экземпляры указанного класса. При этом значения назначаются свойствам объекта ДО вызова конструктора. Если свойства с именами, соответствующими названиям столбцов, не существуют, они будут созданы автоматически (с областью видимости public).

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

Для примера возьмем ситуацию, когда вам нужно скрыть часть адреса проживания человека.
class secret_person {  
    public $name;  
    public $addr;  
    public $city;  
    public $other_data;  
  
    function __construct($other = '') {  
        $this->addr = preg_replace('/[a-z]/', 'x', $this->addr);  
        $this->other_data = $other;  
    }  
}

При создании объекта все латинские буквы в нижнем регистре должны замениться на x. Проверим:
$STH = $DBH->query('SELECT name, addr, city from folks');  
$STH->setFetchMode(PDO::FETCH_CLASS, 'secret_person');  
  
while($obj = $STH->fetch()) {  
    echo $obj->addr;  
}

Если в базе данных адрес выглядит как ’5 Rosebud’, то на выходе получится ’5 Rxxxxxx’.

Конечно, иногда будет требоваться, чтобы конструктор вызывался ПЕРЕД присваиванием значений. PDO такое тоже позволяет.
$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'secret_person');

Теперь, когда вы дополнили предыдущий пример дополнительной опцией (PDO::FETCH_PROPS_LATE), адрес видоизменяться не будет, так как после записи значений ничего не происходит.

Наконец, при необходимости можно передавать конструктору аргументы прямо при создании объекта:
$STH->setFetchMode(PDO::FETCH_CLASS, 'secret_person', array('stuff'));

Можно даже передавать разные аргументы каждому объекту:
$i = 0;  
while($rowObj =  $STH->fetch(PDO::FETCH_CLASS, 'secret_person', array($i))) {  
    // что-то делаем
    $i++;
}


Другие полезные методы


Хотя эта статья не может (и не пытается) охватить все аспекты работы с PDO (это огромный модуль!), оставить без упоминания следующие несколько функций нельзя.
$DBH->lastInsertId();

Метод ->lastInsertId() возвращает id последней вставленной записи. Стоит заметить, что он всегда вызывается у объекта базы данных (в статье он именуется $DBH), а не объекта с выражением ($STH).
$DBH->exec('DELETE FROM folks WHERE 1');  
$DBH->exec("SET time_zone = '-8:00'");

Метод ->exec() используется для операций, которые не возвращают никаких данных, кроме количества затронутых ими записей.
$safe = $DBH->quote($unsafe);

Метод ->quote() ставит кавычки в строковых данных таким образом, что их становится безопасно использовать в запросах. Пригодится, если вы не используете prepared statements.
$rows_affected = $STH->rowCount();

Метод ->rowCount() возвращает количество записей, которые поучаствовали в операции. К сожалению, эта функция отказывалась работать с SELECT-запросами вплоть до PHP 5.1.6. Если обновить версию PHP не представляется возможным, количество записей можно получить так:
$sql = "SELECT COUNT(*) FROM folks";  
if ($STH = $DBH->query($sql)) {  
    # проверяем количество записей  
    if ($STH->fetchColumn() > 0) {  
    	  # делаем здесь полноценную выборку, потому что данные найдены!  
    }  
    else {  
        # выводим сообщение о том, что удовлетворяющих запросу данных не найдено
    }  
}


Заключение


Надеюсь, этот материал поможет кому-то из вас осуществить миграцию с расширений mysql и mysqli.
Перевод: Erik Wurzer
Денис @DenisioDelBoro
карма
46,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (161)

  • 0
    Если честно, я не думаю, что много кто пользуется mysql и mysqli. Как минимум PDO, как максимум — более развитый DBAL.
    • +16
      mysql — самый часто используемый модуль php, об этом говорят сами разработчики php.
      И это создает большие проблемы. Разработчики и рады бы выкинуть это «небезопасное старье», но из-за популярности модуля не могут этого сделать сразу. Поэтому запланированы несколько этапов по «отучению» пользователей от mysql модуля. Первый из них — повсеместные рекомендации в документации с призывом использовать mysqli или PDO. Например тут.
      PDO выигрывает у mysqli только возможностью, как сказано в данной статье, относительно быстрой и легкой смены БД в проекте.
      • –2
        Очень странно. Я бы по доброй воле ни за что не стал использовать mysql.
        • +2
          Почему?
          • –3
            Я имел ввиду не БД, а mysql_* функции PHP.
            • 0
              Во времена php3 это был единственный путь. С тех пор и повелось.
              • –5
                Возможно я ошибаюсь, но мне кажется, что большинство сайтов времен php3 уже умерли. Почему программисты пользуются этим сейчас — для меня загадка.
                • +29
                  Ну потому что так написано в книжке «создаем сайт с нуля за 10 дней».
                  • +1
                    за 10 дней
                    что-то долго)
                    • +1
                      Кстати автор у этой книги очень даже квалифицированный человек, взять вот например вот эту его книгу
                    • 0
                      384 страницы за 24 часа? омг.
                • +5
                  Сайты вымерли, программисты живут и размножаются )
                • 0
                  В таком случае какую СУБД вы бы выбрали? И главное (если можно) — почему не mysql? Сейчас у меня стоит вопрос выбора СУБД, которая будет являться основным релятивным хранилищем (далее в связке с nosql списками для поиска и memcached для статики и полустатики).

                  Заранее спасибо!
                  • 0
                    • 0
                      Прошу прощения, прочитав в понедельник утром понял всё наоборот. Эх, жаль — а я думал мне достанется готовое мнение)
                      • +1
                        Если нужна реляционная БД для средних размеров проекта, выбирайте postgres или mysql.
                        • +1
                          На стадии прототипа проект будет писаться быстро, и будет совсем небольшой.

                          А по итогу размер проекта будет крайне, крайне большой — и больше всего будет поисков групп объектов, помеченных идентификатором (ну типа идентификаторы всех сообщений написанных проснажами с такими то идентификаторами сортированные по дате). SELECT POST_ID FROM POSTS WHERE AUTHOR_ID IN (................). Это будет максимальная нагрузка на базу — остальное будет кэшироваться и маркироваться.

                          • +2
                            Это не поиск, это выборка. Вам подойдет любая БД )
                            • +1
                              Ок, спасибо!
                • +2
                  Ох зря вы так думаете.
      • 0
        В MySQLi не хватает поддержки хостерами модуля mysqlnd.dll для выполнения функции get_result() в работе с подготовленными выражениями.
        • 0
          А много хостеров вообще предлагют PHP под Windows? (Расширение dll намекает.)
          • 0
            Я просто искал как называется этот драйвер, а так у меня на «волшебном» хостинге *unix — проблема присутствует.
  • 0
    > Использование prepared statements укрепляет защиту от SQL-инъекций.

    Я хочу прояснить вот такой вопрос: если всегда для всех параметров использовать подготовленные выражения, то можно ли сказать, что я защищён от инъекций полностью? Не берём в расчёт драйвера, которые только имитируют работу с подготовленными выражениями. Чего стоит опасаться в таком случае?
      • +2
        Резюмируйте.
        • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      Защищает от инъекций первого порядка, но не защищает от инъекций второго порядка.

      Т.е. если вам в данные подсунут SQL, то он успешно заэскейпится и попадет в базу без вреда.

      Но когда вы эти данные извлечете из базы и захотите использовать в запросе, вас может ждать «сюрприз».
      • 0
        Имеется в виду получить данные в приложение и отправить их обратно в БД в другом запросе неэкранированными? Так это исключено по условию «всегда для всех параметров использовать подготовленные выражения». Или сюрприз может быть и при, например, использовании вложенных запросов?
        • 0
          Первое насколько я понял.
  • 0
    А я так и не увидел в этой статье, почему я должен им пользоваться, а не оставаться на своем Zend_Db.
    В одном проекте было требование использования pdo — пришлось его наследовать и расширять за неимением методов типа fetchPairs, insert, update и т.д. (AR/ORM тоже нельзя было использовать)
    • +7
      >>А я так и не увидел в этой статье, почему я должен им пользоваться, а не оставаться на своем Zend_Db.

      Вообще да, название статьи слишком громкое для ее содержания. Более адекватным было бы: «Основы PDO. Почему на него стоит обратить внимание.»
    • +3
      — Встроенные типы написаны на C, а не на PHP, а, следовательно, работают быстрее.
      — Встроенные типы стандартизированы, а следовательно вы будете меньше зависеть от используемой ORM.
      — В маленьких проектах не требующих сложных систем сокращается время запуска.
      • +1
        1 и 3: Между удобством и микроскопической разницей производительности(т.к. не узкое место) выберу удобство.
        2: какие такие типы? Какая ORM? Zend_Db — не ORM.
        • 0
          Микроскопическая разница в случае с бложиком, не такая уж и микроскопическая в случае нескольких тысяч запросов в секунду.
          Не все программисты в мире используют ZF, есть еще Doctrine, Propel и сотни менее популярных проектов. Вы хотите вникать в каждый из них при переключении с проекта на проект?
          • +1
            А что, разве не приходится вникать? Ни один из перечисленных вами проектов не поощряет прямые вызовы PDO.
            • +1
              Согласитесь, не на всех проектах требуется махина вроде Doctrine. И не все элементы системы обязаны содержать избыточный код сгенерированый вместе с нужным функционалом. У меня найдется с десяток сайтов, где я могу сделать простую деградацию до уровня PDO и выиграть за счет простоты.
              • +1
                Согласен, но зачем тогда Doctrine-то упоминать? Я просто в свете сказанного вами не пойму смысла фразы
                Не все программисты в мире используют ZF, есть еще Doctrine, Propel и сотни менее популярных проектов. Вы хотите вникать в каждый из них при переключении с проекта на проект?


                Если проект использует ORM или просто библиотеку доступа к данным, при внесении в проект изменений лучше использовать именно ее. А не то там через какое-то время… мягко говоря, будет сложновато разобраться. Представляю, часть использует Doctrine, пришел другой разработчик и для простоты использует PDO. Потом следующий пришел и решил все еще проще сделать. На mysqli…

                Выиграть за счет простоты можно, если в конечном итоге это не увеличит сложность… Вот такой каламбур :)
                • 0
                  Смысл фразы в том, что ORM, не имея стандартных интерфейсов, тащат очень много мусора в виде драйверов и собственных реализаций этих интерфейсов, в результате чего программист теряет контроль над кодом. Именно поэтому я объединил разные по сути продукты в одно.
                  • 0
                    в результате чего программист теряет контроль над кодом.

                    Не могли бы пояснить, как именно?
                    • 0
                      Если рассматривать ORM, то это софт который на входе получает соединение, а на выходе дает объекты, и по сути ORM по барабану откуда она эти данные получила: MySQL, XML, BSON, JSON, стандартный массив, консольный ввод (stdin), главное чтобы эти данные были доступны по условному интерфейсу Data.
                      Живой пример: у нас в фирме есть корпоративная cms, которая дополняется функционалом с использованием документарной базы данных, часть модулей не соответствующих табличной форме должны переехать в эту базу. Используемая ORM не поддерживает работу с документарными бд. Какой выход из ситуации: а) сменить ORM (аха-ха) б) завести 2 ORM на время перехода, в) переписать необходимый код без использования ORM, с тем чтобы потом перевести его на новую систему. г) Дополнить код самой ORM.

                      Все три метода имеют явные недостатки. Ввиду того, что ORM берет на себя слишком много обязанностей. Ну и кто кого теперь контролирует? Я код или код меня?
                      • 0
                        Но ведь с такой точки зрения использование любой внешней библиотеки (NIH) означает «потерю контроля». Более того, даже само использование PDO означает «потерю контроля», поскольку многие функции mysqli при использовании PDO недоступны.
                        • 0
                          Не совсем так. Все зависит от тесноты связывания, возьмем, такую сложную систему, как ОС, например unix: подавляющее большинство программ работают по принципу вы подаете данные на вход (stdin), получаете на выходе (stdout, stderr), по желанию они собираются в сложные цепочки с помощью различных оберток (shell-скрипты), все работает — стандартный интерфейс не ограничивает, а задает направление. И не важно, что именно вы подали на вход, главное, что эти данные могут интерпретироваться как валидные параметры для данной программы. В ситуации с обычными ORM у вас нету управляемых точек входа-выхода, только промежуточное воздействие.
                          • 0
                            Я думаю, все зависит от точки зрения. ORM — такая же программа, как и, скажем, find в Unix. Если find не умеет искать файлы с нарисованной в них собачкой — вы потеряли контроль. Придется писать алгоритм поиска собачки самому.
                            • 0
                              Отличная иллюстрация того о чем я говорю.

                              Find не должен искать собачек, если вам нужно найти фотографию с собачкой, вы сначала запускаете поиск по директориям, чтобы выбрать все изображения, получаете список файлов, а затем передаете его в программу поиска собачек в изображении (каждый делает то, что умеет).

                              Понятное дело, что недостающий функционал придется дописывать, в том случае, если он не стандартен, но существующий функционал вы можете комбинировать как угодно. ORM же замкнуты сами на себя.

                              Уточню, что в данной ситуации я говорю не про класс программ, а про их современные реализации.
                              • 0
                                Разве про современные ORM нельзя сказать то же самое:
                                Понятное дело, что недостающий функционал придется дописывать, в том случае, если он не стандартен, но существующий функционал вы можете комбинировать как угодно.

                                ?

                                Я плохо знаю Propel, но Doctrine, по меньшей мере, данному утверждению удовлетворяет, насколько я знаю.
                                • 0
                                  Возможно, Doctrine вырос из простого SQL-AL/ORM. Но, есть ли там возможность стандартными методами реализовать хранилище на файлах? Или удаленное соединение по tcp-туннелю с передачей данных в виде JSON?
                              • +1
                                > ORM же замкнуты сами на себя.
                                Поясните, пожалуйста.
                                • 0
                                  Лучше приведу пример. У меня есть библиотека (очень примитивная ORM) написанная на js, которая позволяет не изменяя кода работать как на клиенте так и на сервере. За счет чего это достигается? ORM, вместо того, чтобы управлять соединениями, получает объект реализующий стандартный интерфейс DataProvider. На клиенте это REST-провайдер, на сервере MongoDB-провайдер. Как вы понимаете работают они совершенно по-разному, но за счет использования стандартного интерфейса, я отделил ORM от источника данных.

                                  Попробуйте сделать это с Propel, например. Хотя Doctrine сильно изменился за это время. Но, сомневаюсь, что он позволяет использовать файловые бд или любые другие, так же легко.
    • +7
      Zend_Db может использовать PDO в качестве адаптера, так что вы спокойно можете использовать и то и другое одновременно.
      • 0
        Естественно может. А может и не использовать(mysqli), и разницы никакой. Zend_Db предоставляет свой интерфейс и нет разницы, какой драйвер он агрегирует.
        И этот интерфейс более высокого уровня, чем PDO.
        • +2
          Правильно, это интерфейс более высокого уровня. Зачем вы тогда спрашиваете, что из них использовать? Это все равно что спрашивать: «Мне использовать Windows или Photoshop?»
          В вашем случае, нужно смотреть на тесты производительности:
          PDO vs Mysqli — здесь паритет, они примерно равны
          Zend_Db on PDO vs Zend_Db on Mysqli — а вот тут нужно смотреть, вот один из примеров, когда возможности драйвера PDO на порядок эффективнее их эмуляции в драйвере Mysqli.
          • 0
            Порой этими инструментами решают одинаковые задачи, используя PDO как абстракцию и не расширяя ее. Поэтому в данном случае их корректно сравнивать. Это как сравнение Dart и JS, хотя первый основан на втором.

            Предоставьте ссылки на тесты. В качестве примера возьмите какую-нибудь CMS с использованием PDO и напишите враппер для Zend_Db с интерфейсом PDO. Мое предположение: разница в производительности минимальна.
            Приведенный выше пример не в тему, кстати.
            • +1
              PDO vs Zend_Db on PDO не представляет интереса. По крайней мере, для меня. Очевидно, что чистый PDO будет чуть-чуть быстрее, скорее всего на доли процента.
  • 0
    На вид все просто, но как обычно по нестандартным расширениям SQL тема не раскрыта, а использование нестандартных расширений не даст сменить бд сменой строки запроса. Тут разве что можно покрыть ORM подходом.

    И вот основываясь на своем опыте редко бывает случай, где можно только поменять строку запроса, часто приходится переписывать чуть ли не половину запросов.

    Но тут по крайней мере не придется переписывать код, использующий библиотеку.
  • +3
    Согласен, в pdo очень удобно пользоваться fetch_class.
    Статья неплохая, только с опозданием года на три, как минимум.
    • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    С MS Sql Server не все так просто с подключением. Для виндовой машины есть майкрософтовский драйвер, который работает очень даже не плохо — sqlsrv. Но для сервера на *nix совсем беда. Есть бесплатный FreeTDS, но есть небольшие отличия в работе от sqlsrv и запросы выполняются по очереди.
  • 0
    Всем хорош pdo. Пока вам не надо через pdo_oci (Oracle) работать с CLOB`ами :(
  • +3
    Абстракции от СУБД, ORM, Agile-методологии и прочие рефакторинги нужны для впаривания доверчивым гражданам литературных поделок сотен авторов, которых ссаными тряпками выгнали из практической разработки ©
    • +7
      <sarcasm>А настоящему крутому программисту достаточно goto!</sarcasm>
      • 0
        Программисты используют возможности технологии с нативными средствами, а не используют pdo с банальными запросами к БД в надежде, что потом проект вырастет и можно будет легко перейти с (say) MySQL на PostgreSQL / Oracle / etc.
        • +1
          Если я пишу «коробочный» продукт (движок), то зачем мне ограничивать его пользователей в выборе СУБД, если есть простая возможность не ограничивать (в каких-то пределах)? Это про DBAL типа PDO и более высокоуровневые. А с ORM просто удобнее работать, чем с PDO или MySQLi напрямую.
        • +1
          С помощью PDO вы можете не меньше чем с помощью функций того же модуля mysql и даже больше. Плюс он имеет более понятный и структурированный интерфейс. Плюс не нужно запоминать тонну функций с непредсказуемыми названиями (чем итак более PHP), а использовать один инструмент для любой СУБД, не ограничивая себя в нативных возможностях. Возможно, вы не совсем представляете, что такое PDO.

          Я даже скрипты из 10 строк пишу с помощью PDO.
          • 0
            Поздравляю. Вы на верном пути.
    • +1
      Ну еще они нужны более-менее опытным программистам, которым надоело таскать из проекта в проект велосипеды.
  • +2
    А как он разруливает ситуации когда синтаксис запроса для выборки не подерживается определённой БД?
    Например в MS SQL нету LIMIT из MySql.
    Вот бы создали что-то типа LINQ как в .NET тогда было-бы весьма интересно :)
    • +5
      Никак.
    • +1
      Для этого можно использовать, например, Doctrine.
    • 0
      Как и прочие синтаксические ошибки.
  • +6
    «Более мощный PDO», ага. Никогда универсальнее не будет мощнее того, что сделано под конкретную БД, у всех есть особенности, универсальный движок не позволит их использовать, по определению.

    Кроме того, mysqli производительнее и менее требователен к памяти (особенно, если вспомнить, что он сейчас работает через mysqlnd).
    • 0
      А мне нравится идея, где PDO позволяет привыкнуть к идентичным конструкциям для программирования под mysql, oracle, mssql — иначе получается что mysql и oci8 имеют приличное различие не только по синтаксису описания запросов, но даже по структурам получаемых ответов.
      • +1
        Мне тоже эта идея нравится и она даже работает в маленьких проектах. В больших же, когда нужна вся мощь конкретной БД, все её особенности и возможности, PDO — лишний. В Оракле есть CLOB, в MySQL у буферизированных запросов можно узнать количество строк, у кого-то есть асинхронные запросы, у кого-то — нет, ну и так далее.
        • 0
          Спасибо! Очень интересных ход мысли, буду разбираться дальше. Пока нашел интересную заметку по сравнению mysql_ с PDO, где PDO просто выигрывает по скорости, но видимо я потом сделаю такое профилирования для себя. ( ak33m.com/?p=59 )
          • 0
            Это не сравнение mysql_ с PDO, это сравнение работы Zend_Db на PDO и Zend_Db на mysqli.
    • 0
      теперь все работают через mysqlnd ^)
  • 0
    Это не сравнение mysql_ с PDO, это сравнение работы Zend_Db на PDO и Zend_Db на mysqli.
  • 0
    А уже прикрутили в PDO возможность просматривать исполненный прошлый запрос или так же всё и осталось с давних пор?
  • 0
    Добавлю. Стоит заметить, что mysql_real_escape_string и pdo->quote() работают по-разному — последние автоматически приписывают кавычки. Будьте внимательнее при переводе проектов.
  • +2
    PDO ограничивает в функционале, и сильно, как только вам нужно сделать что-то выходящее за рамки простых запросов.
    К примеру у меня есть долгоиграющий фоновый скрипт на сервере. Отсутствие ping функционала в PDO доставляет неистовый butthurt, как и некоторый другой функционал. А поскольку в реальности совместимость между базами нужна разве что для публичных OpenSource проектов, то использование mysqli очень даже оправдано — она гораздо более функциональна чем PDO.
    • 0
      Не только для опенсорс, совместимость с разными базами нужна для большинства коробочных проектов.
      • 0
        Ну да, так же и это.
  • +1
    Он исключения кидает, это многие не любят, лично я не приемлю такой подход к программированию в этом языке, как и сам механизм исключений по многим причинам. Кроме того, кто-то вообще не использует ООП.
    Я думаю что старые интерфейсы ещё долго будут в использовании по этим причинам. Учитывая то, что тенденция кодить на исключениях (в языках, которые изначально не проектировались для этого) набирает обороты.
  • –1
    А ещё есть DbSimple — простая и удобная библиотека для обращения к БД из PHP.
    • 0
      Из исходников DbSimple:

      $ok = $this->link = @mysql_connect

      То есть используется старое расширение mysql, которое почти все рекомендуют больше не использовать.
      • –1
        Ну так и библиотека не вчера появилась — ∇ 30, в которой автор когда-то писал про DbSimple, появилась в 2003 году — почти девять лет назад.
    • 0
      Я не думаю, что библиотека для PHP4 (даже, если работающая в PHP5...) сейчас будет использоваться для нового проекта… тем более, что используется устаревшее расширение mysql_ (и как следствие используются не подготовленные выражения, а только их эмуляция). И кто-то спустил собак на этот код.
  • 0
    Однозначно PDO лучше, так как это расширение унифицирует работу с различными БД, использует подготовленные запросы и как в следствии этого бинарный протокол передачи данных(result set row binary) и различные типы курсоров.
  • 0
    А почему вы используете prepared statements только для UPDATE и INSERT запросов? Добавили бы в виде примера SELECT с использованием WHERE и prepared statements для безопасного заполнения условий выборки.
    • 0
      Вы обращаетесь к автору или к переводчику?
      • +2
        А, не заметил иконку перевода в начале статьи. Когда уже ее сделают более заметной >_<
    • 0
      Потому, что при разных данных в запросах на селект вы получите разные данные, и время парсинга sql запроса будет копеечное по сравнению с временем выгребания результата.
      Зато на insert запросах в задачах типа логирования или складывания в базу на лету каких-то быстро работающих парсеров — выигрышь в десятки раз. При prepared передается запрос «в формате бинари протокола, по сути в нем при бинде выделено определенное количество байт под параметры нужных типов и длины» (в кавычках не совсем точное описание на трех пальцах левой руки). Когда вы вставляете в базу миллионы записей в час — экономия на парсинге существенная
      • 0
        Я это прекрасно знаю, но в SELECT-запросах prepared statements для условий выборки позволять избежать SQL-инъекций, если в условия подставляются сырые данные от пользователя. Я бы рассматривал именно эту сторону вопроса. Кроме того, существует достаточно узкий круг задач, например обработка больших объемов порциями, где prepared statements дадут и выигрыш в производительности.
        • 0
          Все еще доверяете сырым данным от пользователя? Тогда мы идем к Вам!

          Мы у себя в проектах предпочитаем 10 раз перепроверить пользовательские данные, привести к нужным типам, проверить наличие неожидаемых символов и т.п. прежде, чем пихать в запросы в базу. Это не только позволяет избежать инжектов, но и массы «глюков», которые могут возникать совершенно непредсказуемо — только потому, что юзеру почему-то понадобилось ввести свой логин на эльфийском, хотя надо только английский
          • 0
            Я что-то говорил о доверии к сырым данным и что я им доверяю? Мою точку зрения по этому поводу можете почитать в этом комментарии, например. Но в статье речь о PDO, и соответственно я хотел бы видеть в ней использование prepared statements как еще одного рубежа защиты от SQL-инъекций. Не всегда можно с уверенностью сказать, что разработчик, который использует твой код, будет проверять входящие данные.
          • 0
            А я предпочитаю писать сырые данные в базу, только заэкранировав их для SQL. Хочет пользователь логин на эльфийском — ради бога. Выведу на эльфийском заэкранировав вывод только для данного формата — в html применю один экран, в json другой, в yaml третий, в php serialize четвёртый, а не буду вводимую строку приводить к наибольшему общему для SQL, html, json, yaml и php. А еси завтра понадобится ещё какой-нибудь формат — перелопачивать всю БД? А если в результате получится, что в уникальном поле в том же логине из-за вырезания небезопасного для нового формата символа получится дублирование?
  • +4
    Важность быстрой смены СУБД весьма преувеличена. Реальные сайты не прыгают с одной СУБД на другую ежемесячно. Да и не поможет PDO сделать это быстро и легко, если в запросах используются, специфичные для конкретной СУБД конструкции и встроенные функции. Тем не менее поддержка нескольких типов СУБД довольно востребована в коробочных CMS.
    Мне в PDO нравится удобный ООП интерфейс, который, вполне самодостаточный и на значительной части проектов может быть использован без каких либо дополнительных абстрактных оболочек. Если есть необходимость расширить функционал, то можно создать класс производный от PDO.
  • –2
    Перепечатка теперь разрешена на хабре? o_O ruseller.com/lessons.php?rub=28&id=610
    • +1
      Почему перепечатка? Другой перевод
  • +1
    А как со скоростью и памятью обстоят дела у PDO? Я понимаю, что при выборке в 100-200 элементов из БД разницы особой не будет.

    Но, допустим, такая ситуация, что у нас одновременно к БД обращаются 1000 юзеров, которые получают по 10 000 строк из БД. Сколько памяти отъест PDO в таком случае?

    PS/ Прошу прощения если вопрос кому-то покажется глупым, но я ни разу не проводил подобные исследования, бегло прочитал статью и решил задать вопрос знатокам, спасибо.
    • 0
      Если сравнивать, то с чем?
      • 0
        С нативными mysql_query и mysql_fetch_assoc (например). Для меня интересно качественное сравнение — серьезно ли будет съедаться память? Ведь данные в объекте могут храниться разнообразными способами, не получится ли такой ситуации, что 256 мб оперативной памяти, которой будет с лихвой хватать для хранения выборки в массиве (после mysql_fetch_assoc) не будет достаточно для формирования BDO-объектов? Это чисто научный интерес.
        • 0
          Прошу прощения, описался «PDO-объектов».
  • +1
    Основной плюс Prepared statement — то что их можно однажды задать, а в дальнейшем много раз использовать, экономя ресурсы Mysql на парсинге. Причём экономя каждую секунду на каждом запросе для каждого посетителя.
    Где пример этого? Почему в примерах я вижу везде идущие подрят $STH = $DBH-> и exec(), без какого-либо кэширования prepared statements, и в итоге скорость как у обычного mysql_query? Где информация о том, как сохранить эти prepated statements один раз и навсегда для всех посетителей между обновлениями страницы?
    • 0
      А что сами пример не привели?
    • 0
      Хороший вопрос. Буржуйский сайт нам подсказывает ответ. Если коротко, то нельзя сохранять выражения и использовать их между соединениями.
      • 0
        Да, всё плохо. Тогда нет смысла использовать Prepared Statement, только если на одной странице не используется десятки одинаковых select from, что неправильно.
        Либо получается надо использовать php-daemon, где все ресурсы не очищаются в конце генерации страницы (что даст не только ускорение в Prepated Statement, но и все длительные процессы инициализации тоже будут выполняться только один раз), но это нужно переделывать абсолютно всё приложение.
        • +1
          Если приложение выглядит как
          $app = new App(); 
          $request = new Request();
          $response = $app->handleRequest($request);
          $response->send();
          то изменения не такие уж и большие нужно будет вносить.
        • 0
          Ну если процесс интерпретатора не завершается после обработки запроса, наподобие fastcgi, то можно кэшировать данные в его адресном пространстве в промежутках, или использовать что-то типа buletcache/memcache итд.
          • 0
            Есть подозрение, что многие объекты PDO являются несериализуемыми и потому записи куда-то между запросами не подлежат. Если, конечно, вы не предлгаете самое расширение PDO менять.
            • 0
              Ну можно всегда серриализовать не сам объект, а нужные данные в кэш. В любом случае доступ к памяти процесса (пускай и чужого), быстрее чем запрос к реляционной БД.
              • 0
                Мы вроде о подготовленных выражениях, а не данных. То есть один процесс подготовил SELECT * FROM users WHERE id = :id, а другие запрашивают его со своим значением id, а СУБД не тратит при каждом запросе время на парсинг запроса. Есть подозрения, что без изменения pdo/mysqlnd такую схему для mysql не реализовать.
                • 0
                  Почему не реализовать, очень даже реализовать.
                  • 0
                    То есть экземпляры PDOStatement спокойно сериализуются в одном процессе и десериализуются в другом? Это предположение или проверяли?
                    • 0
                      Я просто не вижу разницы откуда через интерфейс попадут данные в подготовленное выражение. Из стороннего процесса, или из своего процесса. У вас есть f(a, b) какая ей разница откуда попадут параметры? Главное, чтобы результат вернулся куда надо.

                      Если же вы тонко намекаете на то, что неплохо бы копировать само подготовленное выражение из процесса в процесс… Зачем? Если есть аккумулятор этой фигни. Но даже в этом случае, я более чем уверен, что можно и это скопировать. Ничего процессозависимого в скомпилированном выражении нет.
                      • 0
                        Я допускаю, что экземпляры PDOStatement внутри связаны с экземплярами PDO, которые в свою очередь связаны с соединениями с БД, которые принадлежат процессам. То есть это не простые PHP объекты, которые можно сохранить в кеш или сессию.

                        Быстрое гугление даёт docs.oracle.com/cd/E17952_01/refman-5.1-en/apis-php-mysqlnd.plugin.html по которому кажется видно, что средствами PHP задачу кэширования подготовленных выражений между запросами не решить, нужны Си-расширения, которые будут перехватывать вызовы самого PHP к драйверу mysqlnd.
                        • 0
                          Правильно, либо написать расширение php на Си, либо расширение MySQL. Если сторонний демон не устраивает (я бы написал тупо сторонний демон, который держал у себя подготовленные выражения и подставлял туда данные по требованию), тогда надо разобраться как компилируются эти выражения, где они хранятся (в процессе клиенте БД, либо в демоне и взаимодействуют через сокет).

                          Возможно и хучить ничего не придётся.
                          Да, кстати, сон не отменял никто :)
                    • 0
                      Та схема, которую вы привели выше, может быть реализована несколькими способами.
                      Результат будет один и тот же. Если вам интересно, то мы можем с вами написать пример и статью.
                      • 0
                        Давайте попробуем, только завтра :)
          • 0
            Дело совсем не в этом. Вы же можете создать подготовленное выражение и использовать его несколько раз подставляя в него разные значения. При этом каждый раз посылая новые значения вы не отправляете сам запрос и, соответственно, mysql не парсит этот запрос заново. Но это только в пределах одного соединения. А наступает новое соединение и этот запрос отправляется заново и парсится заново…
            • 0
              Я понял о чём вы. Я всё о том же, кто мешает не закрывать такие соединения и держать подготовленные выражения между выполнениями скрипта? Хотябы какое-то время, или до определённых действий?

              Кроме этого, мне казалось, что базу можно настроить таким образом, чтобы такие вещи попадали в кэш.
  • 0
    Предлагаю дополнить статью информацией о MYSQL_ATTR_MAX_BUFFER_SIZE. Как известно, по умолчанию этот параметр равен 1M а это значит, что из поля вы можете получить максимум 1М текста. При этом никаких ошибок не возникает.
    И заодно вспомнить про соответствующий баг PHP bugs.php.net/bug.php?id=38015 связанный с проблемой установки этого параметра.
  • +1
    Хорошо, однако, что в яве работу с базой изначально сделали в виде единообразного JDBC API — никакого разброда и шатания в стиле PHP нет.
  • +4
    В прошлом году присутствовал на IPC Berlin на выступлении Oracle разработчика MySQL Йоханнеса Шлютера как раз посвящённому сравнению PHP MySQL Connectors. Так вот он утверждал, что использовать нужно mysqli и ничего другого, т.к. mysql устарел, а PDO содержит баги (даже broken by design), не поддерживает все функции mysqlnd (напр. asynchronous queries, polling) и не разрабатывается постоянно дальше в отличии от mysqli, над которым активно работает непосредственно Oracle.

    Скажу честно, всегда использовал PDO и был в нём уверен, но после этой лекции задумался. Слайды «PHP and MySQL — The current state» можно найти тут schlueters.de/blog/archives/145-Slides-from-IPC-and-PHP-Barcelona.html
  • +1
    Честно говоря ожидал увидеть какие-то сравнения и бенчмарки, а не просто описание мана. Не убедило.
    По опыту скажу что на отдельным местах, особенно при большой выборке, mysqli выигрывает и по скорости и по потреблению памяти, так что «не убедило».
    Удобство в отдельных функциях — да, но никак не «используйте везде».
  • 0
    ИМХО, PDO нужно использовать, потому что это ООП и все его вышеописанные преимущества как раз и связаны с тем, что это ООП. В принципе, никто не мешает похожую обёртку написать для mysql_* (и функций других БД), но зачем, если уже всё сделано за нас?
    • 0
      Все сделано за вас: mysqli и pdo — оба имеют ООП, не говоря уж о всяких ORM и тд.
      Драйверы могут использовать одинаковые (рекомендуется mysqlnd)
  • +1
    По поводу Prepared statements из опыта на заметку:
    Составление плана запроса может (а для постгреса оказалось именно так) выполняться на уровне подготовки запроса (prepare), соответственно без учета параметров, которые вы забиндите после. За счет этого ваша РСУБД может выбрать не лучший путь выполнения запроса (не использовать нужные индексы, использовать их в неправильном порядке и т.п.) — стоит это учитывать.
  • 0
    У каждого своё мнение, у каждого свой подход и свои знания.
    Моя работа не связана с ИТ, но в свободное время люблю изучить что-то новое, да и вообще работа БД и ООП.
    Я не силён ни с БД ни с ООП, и для меня это замечательная статья. Возможно для профи это семечки, но я наконец понял, что к чему.

    Спасибо
  • 0
    Дабы сэкномить вам немного нервов с ошибкой «MySQL server has gone away», ребята из Digg написали обертку:
    code.google.com/p/digg/wiki/PDB

    Если вы используете pdo в каком-нибудь долго работающем крон-скрипте, есть вероятность, что пока скрипт обрабатывает данные, MySQL сервер посчитает соединение заброшенным и отключит клиента. PDO сам по себе не занимается восстановлением и переподключением.
  • 0
    В PDO при проектировании почему-то потеряли метод quoteIdentifier() (видимо авторы PDO привыкли запросы писать исключительно руками).

    Плейсхолдеры реализованы из рук вон плохо. Не напишешь вроде:

    $dbal->write('INSERT INTO ?table (?#) VALUES (?a)', $table, array( array_key($data), array_values($data) ))

    Не говоря о более сложных вещах. Так что все равно надо над ним свою обертку писать. Но в качестве основы годится.
  • 0
    Очень очень напоминает Perl DBI которому уже «сотня» лет
  • 0
    PDO губительно для хайлоада
    Только в самых минимальных ее проявлениях.

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

    • 0
      > PDO губительно для хайлоада
      > Только в самых минимальных ее проявлениях.

      Аргументы?
      • 0
        Скорость еп*та, еще какие то аргументы бывают?
        Я не против пдо но не в таком дебильном виде как, например, в зенде. Хотя красиво. Но какой ценой.
        Минимализм в именно данном случае оправдан.
        • +1
          Если использовать аргумент «Скорость еп*та», то можно под эту гребёнку многое чего подвести: $i++ губительно для хайлоада, echo $i.$k губительно для хайлоада, echo «текст» губительно для хайлоада и т.п.

          Аргумент может быть, например, такими: «проект выдерживал только 1000 запросов в минуту, после перевода с PDO на MySql стал держать 10000», или, например, таким: «при использовании PDO, где на странице выполняется 20 запросов к базе снижает скорость выполнения в 1.5 раза по сравнению с MySQL» и т.д.
          • 0
            в том то и дело что любая незначительная мелочь может обернуться мегатормозами.
            • 0
              Ваше мнение понятно.
    • 0
      >бд выбирается на этапе проектирования.

      Если с момента проектирования требования изменились?
      • 0
        А если решили язык сменить? А если..? Ну не аргумент ведь.
        И да, я согласен, что бд выбирается на стадии проектирования архитектуры приложения. Вот ни разу не видел, чтобы просто взяли и поменяли базу в работающем проекте. Попутно — видел (вместе с половиной приложения поменяли), но просто базу — никогда.
        • 0
          Вроде была волна замен, когда Oracle купила Sun и приобрела контроль над MySQL.
          • 0
            Во-первых, это редкое явление. Во-вторых, я таких проектов не видел, если честно.
            Даже если так, чаще меняли на производные от mysql, например mariadb.
            • 0
              Ну на хабре многие отписывались, что перейдут :)
              • 0
                На хабре всегда много пишут, но мало делают ;)
  • +2
    Вы так все увлеклись холиварами, что слона то вы и не заметили кучу синтаксических ошибок в исходном (не переведенном) и текущем вариантах вы и не заметили.
    Специально не буду писать в ЛК, ибо писал уже, надеясь что автор после этого перечитает свою статью и статью автора, которую весь интернет с ошибками процитировал.

    Скрины пойдут с сайта оригинала, дабы не уличать в ошибках автора перевода, но я надеюсь он быстро поправит, ну и жаль что автор так торопился об этом написать, что правило «доверяй но проверяй» как-то позабылось. Буду разжёвывать, ибо в топик могут заглянуть начинающие программеры, воспользоваться кодом, и после того как нихрена не заработает вернуться к старому дедовскому способу mysql_query.
    поехали:


    кавычки не том на месте, об этой ошибке я автору перевода, далее ТС (ТопикСтартеру) отписал


    подсветка синтаксиса как бы намекает, что строка продолжается, кавычка не закрыта.
    Ну и понеслось, где метод между $DBH-> и ( или мы должны сами перелопатить документацию и догадаться что там $DBH->prepare() ???



    то же самое, нет метода $DBH->prepare() и ошибка в синтаксисе INSERT INTO… VALUES


    об этой ошибке я тоже ТС сообщил, но он не правильно исправил на:


    От себя бы еще добавил два очень важных момента, ибо знать их надо:
    1.

    exec конечно хорошо, но почему ничего не сказано про driver options?
    например для pdo_mysql:
    $driver_options = array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET names = 'utf-8', lc_time_names = 'ru_RU',time_zone = 'Europe/Moscow'");
    $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass, $driver_options); 
    

    все три переменных установятся в одном запросе при соединении с БД

    2.
    execute экранирует все переменные, поэтому такой код с mysql выражением у вас не сработает:
    $data = array( 'id_user' => $id_user, 'visit_date' => 'NOW()' );  
    $STH = $DBH->("INSERT INTO `auth_logs` (id_user, visit_date) VALUES (:id_user, :visit_date)");  
    $STH->execute($data); 
    

    придётся обойтись без переменной:
    $data = array( 'id_user' => $id_user );  
    $STH = $DBH->("INSERT INTO `auth_logs` (id_user, visit_date) VALUES (:id_user, NOW())");  
    $STH->execute($data); 
    


    Извините если комментарий получился огромный, но информация об ошибках должна быть такой — видной и заметной! Кстати в оригинале до сих пор ошибки не поправлены, о них я тоже уведомлял. Печально всё это, для кого пишутся статьи на скорую руку?

    Ставлю автору большой минус за то, что не проверил размещенный им материал, даже после получения информации о двух опечатках. Я если честно поначалу подумал, что это ошибки ТС, ан нет.
    • 0
      Респект. Обычно читаешь в топиках семантику, а синтаксису доверяешь, рассчитывая, что он проверен… И холиваришь насчёт семантики, если речь не идёт о микросекундах, а о стиле :( Не говоря о том, чтобы сравнивать оригинал и перевод (я даже не заметил, что это перевод — 100500-й минус хабру за юзабилити)

      P.S. С Now() вообще всё сложно: вроде, с одной стороны, удобная конструкция, а с другой плохо кэшируется на уровне мускула и не гарантирует, что пришедший раньше на сервер запрос выберет из (или внесёт в) БД только те данные, которые были до начала запроса, потому считаю, что лчше без особых причин использовать
    • 0
      > execute экранирует все переменные
      Классическая ошибка или непонимание подготовленных выражений.
      • 0
        а например такой код через PDO как правильно реализовать?
        $vote = (int) $_POST['vote'];
        $id_user = (int) $_POST['id_user'];
        // классический вариант без PDO
        $sql = "UPDATE `photo_votes` SET `vote` = `vote`+" . $vote . "  WHERE `id_user` = '" . $id_user . "' ";
        $result = mysql_query($sql);
        
        • 0
          При чём тут это?
          • 0
            какое здесь должно быть подготовленное выражение?
            • 0
              Вы можете его сами прекрасно написать. Зачем Вам нужно, чтобы это сделал я? Поясните свою мысль без выходов из-за печки.
              • 0
                Вы пишите о не правильном понимании подготовленных выражений, мне хотелось бы увидеть вашу точку зрения на «понимание» как оно правильно на конкретном примере
                • 0
                  > execute экранирует все переменные, поэтому такой код с mysql выражением у вас не сработает:

                  Не экранирует ничего execute… Если СУБД поддерживает подготовленные выражения, то ничего никто не экранирует, а данные идут в базу отдельным пакетом. Если СУБД не поддерживает подготовленные выражения, то их эмулирует соответствующий драйвер(экранирует, кодирует — не важно). Если мы говорим про mysql, то используются именно полноценные подготовленные выражения. Вот пример:
                  # mysql
                  mysql> PREPARE stmt1 FROM 'SELECT ? AS `title`';
                  mysql> set @title = "--''''''''!@#$%^&*()_+////\"\\";
                  mysql> EXECUTE stmt1 USING @title;
                  +------------------------------+
                  | title |
                  +------------------------------+
                  | --''''''''!@#$%^&*()_+////"\ |
                  +------------------------------+
                  1 row in set (0.00 sec)


                  На всякий случай ссылка. Данные не экранировались, а вставились в запрос как есть.
                  • 0
                    Это всё понятно, вы меня не правильно поняли. Я имел ввиду экранизацию на уровне вставки из приложения, для защиты от SQL инъекций (аналог mysql_real_escape_string).

                    И мы тут как бы про PDO говорим, так и давайте в контексте PDO и делать запросы.
                    Вы здесь слукавили и заэкранировали кавычку \"
                    mysql> set @title = "\"";
                    

                    В мануале по execute в разделе input_parameters написано, что все данные будут интерпретированы как строки (PDO::PARAM_STR).

                    «An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.»


                    В связи с чем тут проблема в том, что иногда эту экранизацию в приложении надо отключать, например для конкретных SQL выражений смешанных с PHP переменными, но штатных способов через bindValue нету. Поэтому приходится прям в самом выражении по старинке вот так вот делать:
                    $user_id = $_GET['u'];
                    $user_ip = $_SERVER['REMOTE_ADDR'];
                    $user_agent = $_SERVER['HTTP_USER_AGENT'];
                    
                    $STH = $DBH->prepare("UPDATE `auth_login` SET `hash` = SHA1(CONCAT(`salt`), '".$user_ip."', '".$DBH->quote($user_agent)."') WHERE `id_user` = :id_user");
                    $STH->bindValue(':id_user', $user_id, PDO::PARAM_INT);
                    

                    когда как в Zend_Framework данная финча реализуется оборачиванием SQL выражения (скобками):
                    $db->update( 'auth_login', array( 'hash' => '(SHA1( CONCAT(`salt`, '".$user_ip."', '".$db->quote($user_agent)."') ))' ) );
                    


                    Вообщем в PDO не хватает константы PDO::PARAM_EXPR (либо есть какое то решение о котором я не знаю ?) для отключения экранизации через bindValue:
                    $STH->bindValue(':hash', 'SHA1( CONCAT(`salt`, '".$user_ip."', '".$DBH->quote($user_agent)."') )', PDO::PARAM_EXPR);
                    

                    надеюсь мысль понятна.
  • 0
    Так и не понял — чем PDO лучше mysqli.
    • 0
      Я так думаю реализацией и поддержкой. PDO — объетно-ориентированный подход, и как следствие абстрактные методы для работы с БД, благодаря которым можно легко мигрировать с одной БД на другую, использовать одинаковые методы для работы с любой базой (если она эти методы поддерживает), в mysqli — процедурный подход и работает эта библиотека только с MYSQL. Но судя по отзывам PDO сырой, в нём есть баги и этим расширением никто не занимается, в отличие от mysqli который более стабильный и поддержкой которого занимается Oracle.
      • 0
        чорт не минусуйте, в mysqli возможны оба варианта: и процедурный и ООП подход. Я им честно не пользовался напрямую, я так думаю здесь разница лишь в удобности мигрирования, хотя всю туже самую миграцию можно осуществить своими классами, как это сделали в Zend_Framework.
      • 0
        mysqli вполне себе поддерживает ОО подход.
        • 0
          да, ниже я отписал, прошу прощения за ложную инфу.
    • 0
      основных различий только два:
      1) PDO поддерживает много движков БД, а mysqli — только MySQL;
      2) PDO поддерживает именованные плэйсхолдеры (WHERE `id_user` = :id_user), а MySQLi — нет.
      Вот и всё. Отсутствие процедурного стиля работы с PDO особенно обсуждать не имеет смысла. PDO чуть-чуть медленнее MySQLi, но оба они проигрывают по скорости простому расширению mysql.
  • 0
    mysqli имеет и объектный интерфейс — раз.
    переходить на другую СУБД приходилось только один раз в жизни — в последний месяц. SQLite->MySQL.
    Страшно не было — правда :)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.