Разделы сайта
Выбор редакции:
- Не торопитесь с покупкой DVB-T2 тюнеров
- QQ: регистрируемся и устанавливаем секретные вопросы Вот краткий перечень требований к email-маркетингу в Китае
- Как поставить комментарии вконтакте на wordpress
- Веб-камера ВКонтакте: новые функции
- Добавить в избранные закладки для всех браузеров Javascript добавить в избранное
- Как поставить музыку на звонок
- Обновление Андроид: как обновиться до новой версии, сделать откат?
- Что делать если не включается Nokia Lumia?
- Установка официальной прошивки на Lenovo RocStar (A319) Перепрошивка lenovo
- Обзор и тестирование игровой гарнитуры Kingston HyperX Cloud II
Реклама
Как работать с PDO? Полное руководство. Установка расширений OCI8 и PDO_OCI для PHP5 Установка pdo |
В настоящее время я работаю в компании, которая очень любит использовать в проектах на PHP СУБД Oracle, причем иногда версии 11g. Большая часть разработчиков этой компании работает под ОС Windows. За последний месяц несколько из них решили приобщиться к Linux и поставили себе Ubuntu. По прошествии нескольких дней после установки самой ОС, ребята столкнулись с задачей установки драйверов PHP для работы с СУБД Oracle - OCI8 и PDO_OCI на базе Oracle instant client 11.2, которую не смогли решить самостоятельно. Я не нашел подробного, полностью рабочего мануала на русском, по которому новичок в Linux смог бы выполнить все манипуляции сам. В результате мне пришлось несколько раз проделать серию одних и тех же действий на их машинах и написать мануал, который я вам и представляю. Мануал написан для пользователей Ubuntu Linux, но с некоторыми изменениями подойдет для пользователей большинства Linux"ов. Подготовка к установке
Установка Oracle instant clientСкачиваем instant client Oracle с официального сайта http://oracle.com для своей архитектуры процессора и ОС.Для Linux instant client поставляется в двух вариантах:
sudo mkdir -p /opt/oracle/ Перемещаем скачанные файлы в /opt/oracle и переходим в папку назначения (допустим что вы скачали «zip архивы» в папку «downloads» Вашего пользователя): Разархивируем все скачанные архивы: В итоге в каталоге /opt/oracle был создан, для Oracle instant client 11.2.0.2.0, каталог instantclient_11_2. Переименуем этот каталог в instantclient (если у вас другая версия/каталог измените команду) и перейдем в него: Далее необходимо создать несколько дополнительных каталогов и символьных ссылок для нормальной работы клиента (обратите внимание на версию и если она у вас другая измените команды): Sudo mkdir -p include/oracle/11.2/ Sudo mkdir -p lib/oracle/11.2/client Создаем конфигурационный файл, в котором будет указан каталог для поиска библиотек Oracle instant client, и подключаем его: Так как в Ubuntu нет каталога /usr/include/php, а клиент его все равно ищет создадим символьную ссылку на его эквивалент php5: Устанавливаем OCI8После всех наших манипуляций расширение oci8 замечательно устанавливается с помощью команды pecl :sudo pecl install oci8 нас просят ввести путь к Oracle instant client, на что необходимо ответить: instantclient,/opt/oracle/instantclient Создаём файл подключения расширения: Устанавливаем PDO_OCIДля установки PDO_OCI нам сначала необходимо его скачать из репозитория pear .Обновим список пакетов pear: sudo pecl channel-update pear.php.net Скачаем и поместим архив во временную директорию: Извлечем содержимое архива и перейдем к нему: Здесь нам необходимо скорректировать файл config.m4, так как в нем нет данных о нашей версии Oracle instant client, последние изменения датируются 2005 годом. Запускаем любимый редактор и вносим изменения отмеченные "+" (обратите внимание на версию и если она у вас другая измените строчки): Далее приведен diff двух файлов: Подготавливаем окружение для расширения php c помощью команды phpize (обратите внимание на версию, если она у вас другая измените): Конфигурируем установщик пакета и устанавливаем пакет (обратите внимание на версию, если она у вас другая измените): Создаём для него файл подключения: Подводим итогиПерезапускаем apache и проверяем наличие установленных расширений:sudo /etc/init.d/apache2 restart php -m ЗаключениеМануал основан на вот этом посте , который был несколько переработан - исправлены ошибки и внесены дополнения.Надеюсь, статья будет полезной не только моим коллегам по работе.
3 июня 2018 Андрей Чернышов Перевод Туториал 1736 0 PDO является акронимом для PHP Data Objects: это PHP расширение для работы с базами данных используя объекты. Одно из его преимуществ лежит в том, что оно не привязано напрямую к определенной базе данных: его интерфейс позволяет получить доступ к нескольким разным средам, включая: MySQL, SQLite, PostgreSQL, Microsoft SQL Server. Это руководство нацелено предоставить полный обзор PDO и провести читателя шаг за шагом от создания и подключения к базе данных, до выбора наиболее подходящих методов выборки, демонстрируя как создать подготовленные запросы и описывая возможные режимы ошибок. Создание тестовой базы данных и таблицыВ первую очередь, мы создадим базу данных: CREATE DATABASE solar_system; GRANT ALL PRIVILEGES ON solar_system.* TO "testuser"@"localhost" IDENTIFIED BY "testpassword"; Мы выдали пользователю testuser все привилегии в базе данных solar_system , используя testpassword для пароля. Теперь давайте создадим таблицу и заполним её какой-нибудь информацией: USE solar_system; CREATE TABLE planets (id TINYINT(1) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), name VARCHAR(10) NOT NULL, color VARCHAR(10) NOT NULL); INSERT INTO planets(name, color) VALUES("earth", "blue"), ("mars", "red"), ("jupiter", "strange"); Описание соединения DSN (Data Source Name)Теперь, когда у нас есть база данных, мы должны задать DSN . DSN расшифровывается как Data Source Name , и является набором информации, необходимой для подключения к базе данных, DSN имеет форму строки. Синтаксис отличается в зависимости от базы данных, подключение к которой требуется, но так как мы используем MySQL/MariaDB, нам нужно задать следующие:
Формат строки в нашем случае будет таким (мы будем хранить его в переменной $dsn): $dsn = "mysql:host=localhost;port=3306;dbname=solar_system;charset=utf8"; В первую очередь мы задали database prefix или префикс базы данных. В этом случае, так как мы подключаемся к базе данных типа MySQL/MariaDB, мы используем mysql . Затем мы отделили префикс от остальной строки двоеточием и каждая последующая секция отделена от остальных точкой с запятой. В следующих двух секциях мы задали hostname , на котором запущена база данных и port используемый для подключения. Если порт не указан, использован будет порт по умолчанию, в данном случае это 3306 . Сразу после database name указывается charset . Создание PDO объектаТеперь, когда наш DSN готов, мы приступим к созданию PDO object . Конструктор PDO использует строку DSN как первый параметр, имя пользователя базы данных вторым параметром, пароль – третьим, и необязательный массив настроек – четвертым. $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; $pdo = new PDO($dsn, "testuser", "testpassword", $options); Настройки так же можно задать и после создания объекта, пользуясь методом SetAttribute() : $pdo->SetAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); Настройка PDO повидения при ошибкахДавайте взглянем на некоторые опции доступные для PDO::ATTR_ERRMODE . Эти опции крайне важны, потому что они определяют поведение PDO в случае возникновения ошибок. Возможные опции: PDO::ERRMODE_SILENT Опция по умолчанию. PDO просто выдаст код ошибки и сообщение об ошибке. Их можно будет получить используя методы errorCode() и errorInfo() . PDO::ERRMODE_EXCEPTION Эта опция, на мой взгляд, рекомендуема для использования. С её помощью, помимо выдачи кода ошибки и информации, PDO выдаст исключение PDOException , которое прервет ход выполнения скрипта, а ещё она полезна при PDO transactions (их мы рассмотрим чуть позже). PDO::ERRMODE_WARNING С этой опцией, PDO выдаст код ошибки и сообщение о ней, как и при PDO::ERRMODE_SILENT , но еще и покажет предупреждение WARNING , которое не прерывает работу скрипта. Настройка метода выборки по умолчаниюЕще одна важная настройка, регулируется с помощью константы PDO::DEFAULT_FETCH_MODE . Она позволяет настроить по умолчанию работу метода fetch() , который будет использоваться для получения результатов запроса. Вот самые часто используемые опции: PDO::FETCH_BOTH При его использовании, полученные результаты будут индексированы и по целым числам, и по названиям столбцов. Использование его в методе для получения ряда из таблицы планет выдаст нам такие результаты: $stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_BOTH); Array ( => 1 => 1 => earth => earth => blue => blue) PDO::FETCH_ASSOC С этой константой, результаты будут записаны в ассоциативный массив в котором каждый ключ будет именем столбца, а каждое значение – обозначать определенное значение в ряду: $stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_ASSOC); Array ( => 1 => earth => blue) PDO::FETCH_NUM Используя PDO::FETCH_NUM константу мы получим 0-indexed array: Array ( => 1 => earth => blue) PDO::FETCH_COLUMN Эта константа полезна для получения только значений из столбца, а метод вернет все результаты внутри простого одномерного массива. Например, вот такой запрос: $stmt = $pdo->query("SELECT name FROM planets"); В результате: Array ( => earth => mars => jupiter) PDO::FETCH_KEY_PAIR Эта константа полезна, когда нужно получить значения из двух столбцов. Метод fetchAll() вернет результаты в виде ассоциативного массива. В этом массиве, данные из первого столбца будут указаны в форме ключей, а из второго – в качестве значений: $stmt = $pdo->query("SELECT name, color FROM planets"); $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); В результате: Array ( => blue => red => strange) PDO::FETCH_OBJECT При использовании константы PDO::FETCH_OBJECT , будет создан anonymous object за каждый полученный ряд. Его (публичные) свойства будут названы также, как и столбцы, а результаты запроса будут использованы в качестве значений. Использование этого метода для того же запроса, что и выше, приведет к следующему результату: $results = $stmt->fetch(PDO::FETCH_OBJ); stdClass Object ( => earth => blue) PDO::FETCH_CLASS Как и предыдущая константа, назначит значения столбцов свойствами объекта, но в этом случае мы должны настроить существующий класс, который будет использован для создания объекта. Для демонстрации, сначала мы создадим класс: Class Planet { private $name; private $color; public function setName($planet_name) { $this->name = $planet_name; } public function setColor($planet_color) { $this->color = $planet_color; } public function getName() { return $this->name; } public function getColor() { return $this->color; } } Не обращайте внимание на простоту кода, лучше займемся осмотром класса Planet, который мы создали: у него в свойствах прописано private и у класса отсутствует конструктор. Теперь же попробуем получить результаты. Используя fetch() с PDO::FETCH_CLASS необходимо использовать метод setFetchMode() на объект, перед тем как пытаться получить данные, например: $stmt = $pdo->query("SELECT name, color FROM planets"); $stmt->setFetchMode(PDO::FETCH_CLASS, "Planet"); Мы задаём константу PDO::FETCH_CLASS как первый аргумент метода setFetchMode() и название класса, использовано для создания объекта (в нашем случае «Planet») – вторым аргументом. Теперь запускаем код: $planet = $stmt->fetch(); Должен получиться объект Planet: Var_dump($planet); Planet Object ( => earth => blue) Заметьте, как значения, полученные из запроса, были назначены к соответствующим характеристикам объекта, несмотря на то, что они приватные. Назначение характеристик после создания объектаКласс «Planet», не обладал никаким определенным конструктором, так что проблем с назначением характеристик не возникло; но что если у класса есть конструктор, в котором характеристики задаются и изменяются? Так как значения назначены до запуска конструктора, они будут перезаписаны. PDO помогает предоставить константу FETCH_PROPS_LATE: при её использовании, значения будут назначены после создания объекта. Пример: Class Planet { private $name; private $color; public function __construct($name = moon, $color = grey) { $this->name = $name; $this->color = $color; } public function setName($planet_name) { $this->name = $planet_name; } public function setColor($planet_color) { $this->color = $planet_color; } public function getName() { return $this->name; } public function getColor() { return $this->color; } } Мы изменили наш класс Planet, создав конструктор, который возьмет два аргумента: name name и сolor . Эти аргументы имеют базовые значения: moon и gray, что значит, что, если других значений не будет задано, будут установлены эти. В этом случае, если мы не используем FETCH_PROPS_LATE , то не важно, какие значения будут получены из базы данных, все характеристики останутся базовыми, потому что в процессе создания объекта, они будут перезаписаны. Для того чтобы это проверить, запустим следующий запрос: $stmt = $pdo->query("SELECT name, color FROM solar_system WHERE name = "earth""); $stmt->setFetchMode(PDO::FETCH_CLASS, "Planet"); $planet = $stmt->fetch(); А теперь рассмотрим объект Planet и проверим, какие значения соответствуют его характеристикам: Var_dump($planet); object(Planet)#2 (2) { ["name":"Planet":private]=> string(4) "moon" ["color":"Planet":private]=> string(4) "gray" } Как и ожидалось, полученные из базы данных значения были перезаписаны значениями по умолчанию. Теперь, мы продемонстрируем решение проблем, используя константу FETCH_PROPS_LATE (и тот же запрос, что и предыдущий): $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "Planet"); $planet = $stmt->fetch(); var_dump($planet); object(Planet)#4 (2) { ["name":"Planet":private]=> string(5) "earth" ["color":"Planet":private]=> string(4) "blue" } Наконец, получен желаемый результат. Но что если у конструктора класса нет базовых значений, и они должны быть заданы? Это уже проще: мы можем задать параметры конструктора в форме массива, как третий аргумент, после имени класса, используя метод setFetchMode() . Например, давайте изменим конструктор: Class Planet { private $name; private $color; public function __construct($name, $color) { $this->name = $name; $this->color = $color; } [...] } Аргументы конструктора теперь обязательны, так что мы запускаем: $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "Planet", ["moon", "gray"]); В этом случае, указанные нами параметры служат только как базовые значения, требуемые для работы объекта без ошибок: они будут переписаны значениями из базы данных. Получение нескольких объектовКонечно же, возможно получить сразу несколько результатов в форме объектов, или используя метод fetch() , или посредством цикла: While ($planet = $stmt->fetch()) { // Что-то делам с результатами } Или получив все результаты сразу. В этом случае, как говорилось ранее, используя метод fetchAll() вам нужно будет указывать fetch режим не перед запуском метода, но в тот момент, когда он запустится: $stmt->fetchAll(PDO::FETCH_CLASS|PDO_FETCH_PROPS_LATE, "Planet", ["moon", "gray"]); PDO::FETCH_INTO При использовании этой константы, PDO не создает новый объект, взамен обновляя характеристики уже существующего, но только если он public или в случае использования метода __set() внутри объекта. Подготовленные против прямых запросовУ PDO есть два пути работы с запросами: использовать прямые и более надежный - подготовленные. Прямые запросы Для использования прямых запросов существует два главных метода: query() и exec() . Первый из них создает объект PDOStatemnt , доступ к которому можно получить через методы fetch() или fetchAll() : если вы используете их в случаях, когда таблица не меняется, таких как SELECT . Второй метод, взамен, возвращает номер ряда, который был изменен запросом: мы используем его в случаях, которые заменяют ряды, таких как INSERT , DELETE или UPDATE . Прямые запросы должны быть использованы, только в случаях отсутствия переменных в запросах, и в безопасности метода нет никаких сомнений. Подготовленные запросы PDO также поддерживает запросы исполняемые в два шага, подготовленные: они полезны, когда в запросах есть переменные, и более безопасны в целом, поскольку метод prepare() сделает все необходимые действия за нас. Давайте взглянем, как используются переменные. Представьте, что мы хотим вставить характеристики планеты в таблицу Planets . Для начала, подготовим запрос: $stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(?, ?)"); Как и говорилось ранее, мы используем метод prepare() который использует SQL запрос в качестве аргумента, используя временные значения для переменных. Временные значения могут быть двух типов: позиционные и именные. Позиционные Используя? позиционные временные значения, код получается более краткий, но мы должны задать данные, которые будут вставлены, в том же порядке, что и имена столбцов, в массиве представленном как аргумент метода execute() : $stmt->execute([$planet->name, $planet->color]); Именные Используя именные временные значения named placeholders , нам не нужен определенный порядок, но мы получим больше кода в результате. При запуске метода execute() , мы должны задать данные в форме ассоциативного массива, в котором каждый ключ – имя использованного временного значения, а ассоциирующееся значения будет тем, что перенесется в запрос. Например, предыдущий запрос станет таким: $stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(:name, :color)"); $stmt->execute(["name" => $planet->name, "color" => $planet->color]); Методы prepare() и execute() оба могут быть использованы для запросов, которые изменяют или просто получают информацию из базы данных. В первом случае, мы используем fetch методы перечисленные сверху для получения информации, а во втором – используя метод rowCount() . Методы bindValue() и bindParam()Для предоставления значений, которые будут вставлены в запрос, могут также использоваться методы bindValue() и bindParam() . Первый привязывает значение заданной переменной к позиционному или именному временному значению, использованному при подготовке запроса. Взяв за пример предыдущий случай, мы сделаем: $stmt->bindValue("name", $planet->name, PDO::PARAM_STR); Мы привязываем значение $planet->name к временному значению:name . Замете, что используя оба метода bindValue() и bindParam() мы можем также задать тип переменной, как третий аргумент, используя подходящую PDO константу, в этом случае PDO::PARAM_STR . Используя взамен bindParam() мы можем привязать переменную к подходящему временному значению, используемому в подготовке запроса. Заметьте, что в этом случае, переменная связана с reference и её значение будет изменено на временное, только когда запустится метод execute() . Синтаксис такой же, как и в прошлый раз: $stmt->bindParam("name", $planet->name, PDO::PARAM_STR) Мы привязали переменную, а не её значение $planet->name к:name ! Как сказано выше, замена произойдет только при запуске метода execute() , так что временное значение будет заменено на значение переменной в тот момент. PDO ТранзакцииТранзакции позволяют сохранить последовательность при запуске множественных запросов. Все запросы выполняются «партиями» и относятся к базе данных, только если они все удачно выполнены. Транзакции не будут работать со всеми базами данных, и не со всеми sql конструкциями, поскольку некоторые из них вызывают проблемы. В качестве экстремального и странного примера, представьте, что пользователь должен выбрать список планет и каждый раз, когда он делает новый выбор, вам нужно будет удалить предыдущий из базы данных, прежде чем вставлять новый. Что если удаление произойдет, а вставка – нет? Мы получим пользователя без планет! В основном, транзакции применяются так: $pdo->beginTransaction(); try { $stmt1 = $pdo->exec("DELETE FROM planets"); $stmt2 = $pdo->prepare("INSERT INTO planets(name, color) VALUES (?, ?)"); foreach ($planets as $planet) { $stmt2->execute([$planet->getName(), $planet->getColor()]); } $pdo->commit(); } catch (PDOException $e) { $pdo->rollBack(); } В первую очередь, метод beginTransaction() в объекте PDO отключает autocommit запроса, затем запросы запускаются в необходимом порядке. В этот момент, если не возникает исключение PDOException запросы автоматически пропускаются через метод commit() , в противном случае – через метод rollBack() транзакции отменяются и autocommit восстанавливается. Таким образом, при множественных запросах, всегда будет последовательность. Это довольно очевидно, но PDO транзакции могут быть использованы только PDO::ATTR_ERRMODE установлен на PDO::ERRMODE_EXCEPTION . Для начала создадим базу данных для этого руководства: CREATE DATABASE solar_system; GRANT ALL PRIVILEGES ON solar_system.* TO "testuser"@"localhost" IDENTIFIED BY "testpassword"; Пользователю с логином testuser и паролем testpassword предоставили полные права доступа к базе solar_system . Теперь создадим таблицу и заполним данными, астрономическая точность которых не подразумевается: USE solar_system; CREATE TABLE planets (id TINYINT(1) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), name VARCHAR(10) NOT NULL, color VARCHAR(10) NOT NULL); INSERT INTO planets(name, color) VALUES("earth", "blue"), ("mars", "red"), ("jupiter", "strange"); Описание соединенияТеперь, когда создана база, определим DSN () - сведения для подключения к базе, представленные в виде строки. Синтаксис описания отличается в зависимости от используемой СУБД. В примере работаем с MySQL/MariaDB, поэтому указываем:
Строка DSN в этом случае выглядит следующим образом: $dsn = "mysql:host=localhost;port=3306;dbname=solar_system;charset=utf8"; Первым указывается database prefix . В примере - mysql . Префикс отделяется от остальной части строки двоеточием, а каждый следующий параметр - точкой с запятой. Создание PDO-объектаТеперь, когда строка DSN готова, создадим PDO-объект. Конструктор на входе принимает следующие параметры:
Дополнительные параметры можно также определить после создания объекта с помощью метода SetAttribute: $pdo->SetAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); Определение метода выборки по умолчаниюPDO::DEFAULT_FETCH_MODE - важный параметр, который определяет метод выборки по умолчанию. Указанный метод используется при получении результата выполнения запроса. PDO::FETCH_BOTHРежим по умолчанию. Результат выборки индексируется как номерами (начиная с 0), так и именами столбцов: $stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_BOTH); После выполнения запроса с этим режимом к тестовой таблице планет получим следующий результат: Array ( => 1 => 1 => earth => earth => blue => blue) PDO::FETCH_ASSOCРезультат сохраняется в ассоциативном массиве, в котором ключ - имя столбца, а значение - соответствующее значение строки: $stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_ASSOC); В результате получим: Array ( => 1 => earth => blue) PDO::FETCH_NUMПри использовании этого режима результат представляется в виде массива, индексированного номерами столбцов (начиная с 0): Array ( => 1 => earth => blue) PDO::FETCH_COLUMNЭтот вариант полезен, если нужно получить перечень значений одного поля в виде одномерного массива, нумерация которого начинается с 0. Например: $stmt = $pdo->query("SELECT name FROM planets"); В результате получим: Array ( => earth => mars => jupiter) PDO::FETCH_KEY_PAIRИспользуем этот вариант, если нужно получить перечень значений двух полей в виде ассоциативного массива. Ключи массива - это данные первого столбца выборки, значения массива - данные второго столбца. Например: $stmt = $pdo->query("SELECT name, color FROM planets"); $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); В результате получим: Array ( => blue => red => strange) PDO::FETCH_OBJECTПри использовании PDO::FETCH_OBJECT для каждой извлеченной строки создаётся анонимный объект. Его общедоступные (public) свойства - имена столбцов выборки, а результаты запроса используются в качестве их значений: $stmt = $pdo->query("SELECT name, color FROM planets"); $results = $stmt->fetch(PDO::FETCH_OBJ); В результате получим: StdClass Object ( => earth => blue) PDO::FETCH_CLASSВ этом случае, как и в предыдущем, значения столбцов становятся свойствами объекта. Однако требуется указать существующий класс, который будет использоваться для создания объекта. Рассмотрим это на примере. Для начала создадим класс: Class Planet { private $name; private $color; public function setName($planet_name) { $this->name = $planet_name; } public function setColor($planet_color) { $this->color = $planet_color; } public function getName() { return $this->name; } public function getColor() { return $this->color; } } Обратите внимание, что у класса Planet закрытые (private) свойства и нет конструктора. Теперь выполним запрос. Если используется метод fetch с PDO::FETCH_CLASS , перед отправкой запроса на получение данных нужно применить метод setFetchMode: $stmt = $pdo->query("SELECT name, color FROM planets"); $stmt->setFetchMode(PDO::FETCH_CLASS, "Planet"); Первый параметр, который передаем методу setFetchMode , - константа PDO::FETCH_CLASS . Второй параметр - имя класса, который будет использоваться при создании объекта. Теперь выполним: $planet = $stmt->fetch(); var_dump($planet); В результате получим объект Planet: Planet Object ( => earth => blue) Значения, полученные в результате запроса, назначены соответствующим свойствам объекта, даже закрытым. Определение свойств после выполнения конструктораВ классе Planet нет явного конструктора, поэтому проблем при назначении свойств не будет. При наличии у класса конструктора, в котором свойство было назначено или изменено, они будут перезаписаны. При использовании константы FETCH_PROPS_LATE значения свойств будут присваиваться после выполнения конструктора: Class Planet { private $name; private $color; public function __construct($name = moon, $color = grey) { $this->name = $name; $this->color = $color; } public function setName($planet_name) { $this->name = $planet_name; } public function setColor($planet_color) { $this->color = $planet_color; } public function getName() { return $this->name; } public function getColor() { return $this->color; } } Мы изменили класс Planet , добавив конструктор, который принимает на входе два аргумента: name (имя) и color (цвет). Значения этих полей по умолчанию: moon (луна) и gray (серый) соответственно. Если не использовать FETCH_PROPS_LATE , при создании объекта свойства будут перезаписаны значениями по умолчанию. Проверим это. Сначала выполним запрос: $stmt = $pdo->query("SELECT name, color FROM solar_system WHERE name = "earth""); $stmt->setFetchMode(PDO::FETCH_CLASS, "Planet"); $planet = $stmt->fetch(); var_dump($planet); В результате получим: Object(Planet)#2 (2) { ["name":"Planet":private]=> string(4) "moon" ["color":"Planet":private]=> string(4) "gray" } Как и ожидалось, извлеченные из базы данных значения перезаписаны. Теперь рассмотрим решение задачи с помощью FETCH_PROPS_LATE (запрос аналогичный): $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "Planet"); $planet = $stmt->fetch(); var_dump($planet); В результате получим то, что нужно: Object(Planet)#4 (2) { ["name":"Planet":private]=> string(5) "earth" ["color":"Planet":private]=> string(4) "blue" } Если у конструктора класса нет значений по умолчанию, а они нужны, параметры конструктора задаются при вызове метода setFetchMode третьим аргументом в виде массива. Например: Class Planet { private $name; private $color; public function __construct($name, $color) { $this->name = $name; $this->color = $color; } [...] } Аргументы конструктора обязательны, поэтому выполним: $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "Planet", ["moon", "gray"]); Входящие параметры выступают также в роли значений по умолчанию, которые нужны для инициализации. В дальнейшем они будут перезаписаны значениями из базы данных. Получение нескольких объектовМножественные результаты извлекаются в виде объектов с помощью метода fetch внутри цикла while: While ($planet = $stmt->fetch()) { // обработка результатов } Или путём выборки всех результатов сразу. Во втором случае используется метод fetchAll , причём режим указывается в момент вызова: $stmt->fetchAll(PDO::FETCH_CLASS|PDO_FETCH_PROPS_LATE, "Planet", ["moon", "gray"]); PDO::FETCH_INTOПри выборе этого варианта выборки PDO не создаёт новый объект, а обновляет свойства существующего. Однако это возможно только для общедоступных (public) свойств или при использовании в объекте «магического» метода __set . Подготовленные и прямые запросыВ PDO два способа выполнения запросов:
Прямые запросыСуществует два метода выполнения прямых запросов:
Прямые операторы используются только в том случае, если в запросе отсутствуют переменные и есть уверенность, что запрос безопасен и правильно экранирован. Подготовленные запросыPDO поддерживает подготовленные запросы (prepared statements), которые полезны для защиты приложения от : метод prepare выполняет необходимые экранирования. Рассмотрим пример. Требуется вставить свойства объекта Planet в таблицу Planets . Сначала подготовим запрос: $stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(?, ?)"); Используем метод prepare , который принимает как аргумент SQL-запрос с псевдопеременными (placeholders). Псевдопеременные могут быть двух типов: неименнованые и именованные. Неименованные псевдопеременныеНеименованные псевдопеременные (positional placeholders) отмечаются символом? . Запрос в результате получается компактным, но требуется предоставить значения для подстановки, размещенные в том же порядке. Они передаются в виде массива через метод execute: $stmt->execute([$planet->name, $planet->color]); Именованные псевдопеременныеПри использовании именованных псевдопеременных (named placeholders) порядок передачи значений для подстановки не важен, но код в этом случае становится не таким компактным. В метод execute данные передаются в виде ассоциативного массива, в котором каждый ключ соответствует имени псевдопеременной, а значение массива - значению, которое требуется подставить в запрос. Переделаем предыдущий пример: $stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(:name, :color)"); $stmt->execute(["name" => $planet->name, "color" => $planet->color]); Методы prepare и execute используются как при выполнении запросов на изменение, так и при выборке. А информацию о количестве обработанных строк при необходимости предоставит метод rowCount . Управление поведением PDO при ошибкахПараметр выбора режима ошибок PDO::ATTR_ERRMODE используется для определения поведения PDO в случае ошибок. Доступно три варианта: PDO::ERRMODE_SILENT , PDO::ERRMODE_EXCEPTION и PDO::ERRMODE_WARNING . PDO::ERRMODE_SILENTВариант по умолчанию. PDO просто запишет информацию об ошибке, которую помогут получить методы errorCode и errorInfo . PDO::ERRMODE_EXCEPTIONЭто предпочтительный вариант, при котором в дополнение к информации об ошибке PDO выбрасывает исключение (PDOException). Исключение прерывает выполнение скрипта, что полезно при использовании транзакций PDO. Пример приведён при описании транзакций. PDO::ERRMODE_WARNINGВ этом случае PDO также записывает информацию об ошибке. Поток выполнения скрипта не прерывается, но выдаются предупреждения. Методы bindValue и bindParamДля подстановки значений в запросе можно также использовать методы bindValue и bindParam . Первый связывает значение переменной с псевдопеременной, которая использована при подготовке запроса: $stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(:name, :color)"); $stmt->bindValue("name", $planet->name, PDO::PARAM_STR); Связали значение переменной $planet->name с псевдопеременной:name . Обратите внимание, что при использовании методов bindValue и bindParam как третий аргумент указывается тип переменной, используя соответствующие константы PDO. В примере - PDO::PARAM_STR . Метод bindParam привязывает переменную к псевдопеременной. В этом случае переменная связана с псевдопеременной ссылкой, а значение будет подставлено в запрос только после вызова метода execute . Рассмотрим на примере: $stmt->bindParam("name", $planet->name, PDO::PARAM_STR); Транзакции в PDOПредставим необычный пример. Пользователю требуется выбрать список планет, причём каждый раз при выполнении запроса текущие данные удаляются из базы, а потом вставляются новые. Если после удаления произойдёт ошибка, то следующий пользователь получит пустой список. Чтобы этого избежать, используем транзакции: $pdo->beginTransaction(); try { $stmt1 = $pdo->exec("DELETE FROM planets"); $stmt2 = $pdo->prepare("INSERT INTO planets(name, color) VALUES (?, ?)"); foreach ($planets as $planet) { $stmt2->execute([$planet->getName(), $planet->getColor()]); } $pdo->commit(); } catch (PDOException $e) { $pdo->rollBack(); } Метод beginTransaction отключает автоматическое выполнение запросов, а внутри конструкции try-catch запросы выполняются в нужном порядке. Если не возникнет исключений PDOException , запросы выполнятся с помощью метода commit . В противном случае откатятся с помощью метода rollback , а автоматическое выполнение запросов восстановится. Таким образом появилась согласованность выполнения запросов. Очевидно, что для этого параметру PDO::ATTR_ERRMODE необходимо установить значение PDO::ERRMODE_EXCEPTION . ЗаключениеТеперь, когда работа с PDO описана, отметим его основные преимущества:
Александр Наливайко , переводчик
У PDO свой собственный хитровыдуманный способ соединения, называемый . Плюс во время коннекта можно задать хренову тучу опций, некоторые из которых чрезвычайно полезны. Полный список можно найти , но важными из них являются только несколько. Пример правильного соединения: $host
=
"127.0.0.1"
;
$dsn
=
"mysql:host=
$host
;dbname=
$db
;charset=
$charset
"
; Что здесь происходит? В $dsn задается тип БД, с которым будем работать (mysql), хост, имя базы данных и чарсет. При том что этот массив - чрезвычайно полезная, как уже говорилось выше, штука. Самое главное - режим выдачи ошибок надо задавать только в виде исключений. Плюс очень удобно задать FETCH_MODE по умолчанию, чтобы не писать его в КАЖДОМ запросе, как это очень любят делать прилежные хомячки. В результате мы получаем переменную $pdo, с которой и работаем далее на протяжении всего скрипта. Для выполнения запросов можно пользоваться двумя методами. Если же в запрос передаётся хотя бы одна переменная, то этот запрос в обязательном порядке должен выполняться только через подготовленные выражения
. Что это такое? Это обычный SQL запрос, в котором вместо переменной ставится специальный маркер - плейсхолдер. PDO поддерживает позиционные плейсхолдеры (?), для которых важен порядок передаваемых переменных, и именованные (:name), для которых порядок не важен. Примеры:
$stmt
=
$pdo
->
prepare
("SELECT name FROM users WHERE email = :email"
); Иногда, очень редко, может потребоваться второй способ, когда переменные сначала привязывают к запросу по одной, с помощью bindValue() / bindParam(), а потом только исполняют. В этом случае в execute() ничего не передается. Пример можно посмотреть в мануале После этого можно использовать PDO statement теми же способами, что и выше. Например, через foreach: ВАЖНО: Подготовленные выражения - основная причина использовать PDO, поскольку это единственный безопасный способ выполнения SQL запросов, в которых участвуют переменные. Также prepare() / execute() могут использоваться для многократного выполнения единожды подготовленного запроса с разными наборами данных. На практике это бывает нужно чрезвычайно редко, и особого прироста в скорости не приносит. Но на случай, если понадобится делать много однотипных запросов, то можно писать так:
$data = array( $stmt
=
$pdo
->
prepare
("UPDATE users SET bonus = bonus + ? WHERE id = ?"
); Здесь мы один раз подготавливаем запрос, а затем много раз выполняем. Мы уже выше познакомились с методом fetch(), который служит для последовательного получения строк из БД. Этот метод является аналогом функции mysq_fetch_array() и ей подобных, но действует по-другому: вместо множества функций здесь используется одна, но ее поведение задается переданным параметром. В подробностях об этих параметрах будет написано позже, а в качестве краткой рекомендации посоветую применять fetch() в режиме FETCH_LAZY
: Также у PDO statement есть функция-хелпер для получения значения единственной колонки. Очень удобно, если мы запрашиваем только одно поле - в этом случае значительно сокращается количество писанины: Но самой интересной функцией, с самым большим функционалом, является fetchAll(). Именно она делает PDO высокоуровневой библиотекой для работы с БД, а не просто низкоуровневым драйвером. FetchAll() возвращает массив, который состоит из всех строк, которые вернул запрос. Из чего можно сделать два вывода: Получение простого массива. Получение колонки. Получение пар ключ-значение. Получение всех строк, индексированных полем. Всего различных режимов получения данных в PDO больше полутора десятков. Плюс ещё их можно комбинировать! Но это уже тема для отдельной статьи. Работая с подготовленными выражениями, следует понимать, что плейсхолдер может заменять только строку или число. Ни ключевое слово, ни идентификатор, ни часть строки или набор строк через плейсхолдер подставить нельзя. Поэтому для LIKE надо сначала подготовить строку поиска целиком, а потом ее подставлять в запрос: $name
=
"%
$name
%"
; Ну, вы поняли. Тут тоже всё плохо. PDO не предоставляет вообще никаких средств для работы с идентификаторами, и их надо форматировать по-старинке, вручную (или посмотреть, все-таки, в сторону SafeMysql , в которой этот, как и многие другие вопросы, решены просто и элегантно). В mysql для ручного форматирования идентификатора необходимо выполнить два действия: $field
=
"`"
.
str_replace
("`"
,
"``"
,
$_GET
[
"field"
]).
"`"
; Однако, здесь есть один нюанс. Одного форматирования может быть недостаточно. приведенный выше код гарантирует нас от классической инъекции, но в некоторых случаях враг все равно может записать что-то нежелательное, если мы бездумно подставляем имена полей и таблиц прямиком в запрос. К примеру, есть в таблице users поле admin. Если входящие имена полей не фильтровать, то в это поле, при автоматическом формировании запроса из POST-а, любой дурак запишет любую гадость. Поэтому имена таблиц и полей, приходящие от юзера, желательно проверять на допустимость, как в приведённом ниже примере Любой код для вставки, который можно увидеть в многочисленных туториалах, навевает тоску и желание убиться апстену. Многокилометровые построения с повторением одних и тех же имен - в идексах $_POST-а, в именах переменных, в именах полей в запросе, в именах плейсхолдеров в запросе, в именах плейсходеров и именах переменных при привязке. Это можно сделать, если принять соглашение, по которому имена полей в форме будут соответствовать именам полей в таблице. Тогда эти имена можно будет перечислить только один раз (в целях защиты от подмены, о которой говорилось выше), и использовать небольшую функцию-хелпер для сборки запроса, которая, в силу особенностей mysql, годится как для INSERT, так и UPDATE запросов: function
pdoSet
($allowed
, &
$values
,
$source
= array()) { Соответственно, для вставки код будет $allowed
= array("name"
,
"surname"
,
"email"
);
// allowed fields А для апдейта - такой: $allowed
= array("name"
,
"surname"
,
"email"
,
"password"
);
// allowed fields Не слишком эффектно, но зато очень эффективно. Напомню, кстати, что если использовать Класс для безопасной и удобной работы с MySQL , то это всё делается в две строчки. PDO и ключевые слова
$dirs
= array("ASC"
,
"DESC"
); |
Популярное:
Новое
- QQ: регистрируемся и устанавливаем секретные вопросы Вот краткий перечень требований к email-маркетингу в Китае
- Как поставить комментарии вконтакте на wordpress
- Веб-камера ВКонтакте: новые функции
- Добавить в избранные закладки для всех браузеров Javascript добавить в избранное
- Как поставить музыку на звонок
- Обновление Андроид: как обновиться до новой версии, сделать откат?
- Что делать если не включается Nokia Lumia?
- Установка официальной прошивки на Lenovo RocStar (A319) Перепрошивка lenovo
- Обзор и тестирование игровой гарнитуры Kingston HyperX Cloud II
- Бесплатная проверка iPhone по IMEI