Php и mysql удаление файла в pdo. Основы работы с расширением PDO. Соединение с MySQL


3 июня 2018 Андрей Чернышов Перевод Туториал 2025 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 .



мета-тег keywords пример (4) Цель

Как я вижу, ваша цель в этом случае двоякая:

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

$provider = function() { $instance = new PDO("mysql:......;charset=utf8", "username", "password"); $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); return $instance; }; $factory = new StructureFactory($provider);

Затем в другом файле или ниже в том же файле:

$something = $factory->create("Something"); $foobar = $factory->create("Foobar");

Сама фабрика должна выглядеть примерно так:

Class StructureFactory { protected $provider = null; protected $connection = null; public function __construct(callable $provider) { $this->provider = $provider; } public function create($name) { if ($this->connection === null) { $this->connection = call_user_func($this->provider); } return new $name($this->connection); } }

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

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

Имейте в виду, что это чрезвычайно упрощенный пример . Вам также может понравиться смотреть два следующих видео:

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

Время от времени я вижу вопросы о подключении к базе данных.
Большинство ответов - это не то, как я это делаю, или я просто не могу правильно ответить. Так или иначе; Я никогда не думал об этом, потому что способ, которым я это делаю, работает для меня.

Но вот сумасшедшая мысль; Возможно, я делаю все это неправильно, и если это так; Мне бы очень хотелось знать, как правильно подключиться к базе данных MySQL с помощью PHP и PDO и сделать ее доступной.

Вот как я это делаю:

Во-первых, вот моя файловая структура (урезанная) :

Public_html/ * index.php * initialize/ -- load.initialize.php -- configure.php -- sessions.php

index.php
На самом верху я require("initialize/load.initialize.php"); ,

load.initialize.php

# site configurations require("configure.php"); # connect to database require("root/somewhere/connect.php"); // this file is placed outside of public_html for better security. # include classes foreach (glob("assets/classes/*.class.php") as $class_filename){ include($class_filename); } # include functions foreach (glob("assets/functions/*.func.php") as $func_filename){ include($func_filename); } # handle sessions require("sessions.php");

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

configure.php
Здесь я в основном просто переопределяю некоторые php.ini -properties и выполняю некоторые другие глобальные настройки для сайта

connect.php
Я установил соединение в класс, чтобы другие классы могли расширять этот...

Class connect_pdo { protected $dbh; public function __construct() { try { $db_host = " "; // hostname $db_name = " "; // databasename $db_user = " "; // username $user_pw = " "; // password $con = new PDO("mysql:host=".$db_host."; dbname=".$db_name, $db_user, $user_pw); $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $con->exec("SET CHARACTER SET utf8"); // return all sql requests as UTF-8 } catch (PDOException $err) { echo "harmless error message if the connection fails"; $err->getMessage() . "
"; file_put_contents("PDOErrors.txt",$err, FILE_APPEND); // write some details to an error-log outside public_html die(); // terminate connection } } public function dbh() { return $this->dbh; } } # put database handler into a var for easier access $con = new connect_pdo(); $con = $con->dbh(); //

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

sessions.php
Помимо обработки обычных сеансов, я также инициализирую некоторые классы в сеансе следующим образом:

If (!isset($_SESSION["sqlQuery"])){ session_start(); $_SESSION["sqlQuery"] = new sqlQuery(); }

Таким образом, этот класс доступен повсеместно. Это может быть не очень хорошая практика (?) ...
Во всяком случае, это то, что этот подход позволяет мне делать везде:

Echo $_SESSION["sqlQuery"]->getAreaName("county",9); // outputs: Aust-Agder (the county name with that id in the database)

Внутри моего класса sqlQuery , который extends мой класс connect_pdo , у меня есть открытая функция getAreaName которая обрабатывает запрос в моей базе данных.
Думаю, довольно аккуратный.

Работает как шарм
Так вот в основном, как я это делаю.
Кроме того, всякий раз, когда мне нужно что-то извлекать из моего БД из класса, я просто делаю что-то похожее на это:

$id = 123; $sql = "SELECT whatever FROM MyTable WHERE id = :id"; $qry = $con->prepare($sql); $qry -> bindParam(":id", $id, PDO::PARAM_INT); $qry -> execute(); $get = $qry->fetch(PDO::FETCH_ASSOC);

Поскольку я вставляю соединение в переменную внутри connect_pdo.php , я просто ссылаюсь на нее, и мне хорошо идти. Оно работает. Я получаю ожидаемые результаты...

Но независимо от этого; Я был бы очень признателен, если бы вы, ребята, могли сказать мне, если я уйду отсюда. Вместо этого я должен был бы изменить области, которые я мог или должен изменить для улучшения и т. Д. ...

Я очень хочу учиться...

$dsn = "mysql:host=your_host_name;dbname=your_db_name_here"; // define host name and database name $username = "you"; // define the username $pwd="your_password"; // password try { $db = new PDO($dsn, $username, $pwd); } catch (PDOException $e) { $error_message = $e->getMessage(); echo "this is displayed because an error was found"; exit(); }

Недавно я пришел к аналогичному ответу / вопросу самостоятельно. Это то, что я сделал, в случае, если кто-то заинтересован: