Verification: a143cc29221c9be0

Php class for pdo database

Что такое реляционная база данных

Реляционная база данных – это база данных основанная на реляционной модели (от англ. relationship — отношение, связь) выражаясь по другому, это некий набор данных с определёнными связями между ними. Обычно такие базы организованны в виде таблиц. В отдельных таблицах хранится сгруппированная по определённому смыслу информация о какой-либо сущности, например данные о покупателях (сущность — покупатель) или данные о товарах (сущность — товар) и т.д.  Каждый столбец таблицы отвечает за хранение одного свойства сущности, внутри ячейки хранится значение свойства сущности, например столбец — цена, значение — 1000.

Отношения между таблицами в БД

Преимущественно отдельные столбцы имеют строго определённый тип данных (числовой, логический, текстовый, дату и время и т.д.). Каждая стока таблицы это набор значений свойств относящихся к определённой сущности (напр. определённому пользователю может соответствовать строка где хранит его имя, фамилия, телефон, email, возраст и пол.). Каждая строка в таблице должна иметь уникальный идентификатор, а так же так называемый «первичный ключ» (часто первичный ключ = уникальный идентификатор). При помощи первичного ключа, несколько таблиц базы данных могут быть связаны, например в таблице users — хранятся пользователи, а в таблице orders — заказы пользователей сделанные в интернет-магазине. По первичному ключу user_id  (идентификатор пользователя) мы можем узнать что этот пользователь заказал, т.е. получить данные из таблицы orders.

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

СУБД MySQL

Что же такое MySQL? Это надежная. быстрая и универсальная система управления базами данных (СУБД), пожалуй  самая популярная в мире (в 2019 году 39% разработчиков использовали MySQL в своих проектах). Несмотря на более слабый функционал в сравнении с PostgreSQL, данная СУБД прекрасно масштабируется и отлично подходит для большинства проектов.

MySQL СУБД

MySQL — идеальный выбор для разработке веб-приложений. Данная СУБД входит в стандартный набор: Linux, Apache HTTP Server, MySQL и PHP (LAMP — набор программ с открытым исходным кодом). Так же не основе MySQL работает большинство популярных CMS ( Content Management System — система управление содержимым), таких как WordPress, OpenCart, 1С Битрикс и т.д.

К преимуществам этой СУБД можно отнести:

  • Простота: Mysql легко установить на любую платформу (Linux, Windows, MacOS), под эту СУБД написано множество настольных приложений позволяющих работать с базой в визуальном режиме (очень удобно особенно для новичков), к подобным системам относятся Workbench, Navicat, PHPMyAdmin и другие.
  • Много функций: MySQL поддерживает весь функционал реализованный на  языке SQL.
  • Безопасность: в СУБД MySQL встроено много функций безопасности, например Access Control Lists и поддержка SSH-зашифрованных соединений.
  • Масштабируемость: MySQL достаточно легко и не дорого масштабируется, что позволяет разрабатывать на ней довольно крупные и высоко-нагруженные проекты.

В большинстве языков программирования встроена поддержка СУБД MySQL, PHP так же не стал исключением.

PHP PDO

PHP PDO
 PDO — это специализированный класс реализующий интерфейс доступа к базе данных. Его отличает универсальность, т.к. вам без разницы какая база данных будет использована.

Подключение к БД

Для подключения к БД необходимо создать экземпляр класса PDO и передать в него параметры подключения:


/* Подключение к базе данных MySQL с помощью PDO */
$dsn = 'mysql:dbname=testdb;host=localhost';
$user = 'root';
$password = '123456';

//Чтобы перехватить ошибки подключения используем исключения try {...} catch () {...}
try {
    $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
	//В случае ошибки выводим сообщения из перехваченного исключения
    echo 'Подключение к БД не удалось: ' . $e->getMessage();
}

Конструктор PDO так же поддерживает и четвёртый параметр options — это массив ключ=>значение специфичных для драйвера (например MySQL или PostgreSQL) настроек подключения. Например сразу после подключения можно выполнить MySQL запрос указав это в параметре options:


//Чтобы перехватить ошибки подключения используем исключения try {...} catch () {...}
try {
    $dbh = new PDO(
		$dsn, 
		$user, 
		$password, 
		[PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"] //Команда которую необходимо выполнить сразу после подключения к серверу
	);
} catch (PDOException $e) {
	//В случае ошибки выводим сообщения из перехваченного исключения
    echo 'Подключение к БД не удалось: ' . $e->getMessage();
}

Полный перечень параметров доступных в options вы найдёте в документации.

Базовый набор операций (CRUD)

Большинство веб-приложений работающих с базой данных, так или иначе реализуют базовый набор операций, а именно Create — создание, Read — чтение, Update — обновление и Delete — удаление (CRUD). Давайте рассмотрим как этот набор операций сделать в PDO.

Создание

Для теста, давайте создадим таблицу с пользователями сайта и заполним её данными. Таблица будет называться просто users и иметь 4 поля:

  1. id — автоинкрементный идентификатор записи
  2. name — имя пользователя
  3. email — адрес электронной почты
  4. phone — контактный телефон

try {
    //Соединяемся с базой данных
    $dbh = new PDO('mysql:dbname=test_db;host=localhost', $user, $password);

    //запрос на создание таблицы
    $sqlCreateTable = 'CREATE TABLE `users` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `email` VARCHAR(255) NOT NULL, `phone` VARCHAR(255) NOT NULL, PRIMARY KEY (`id`)) ';
    $dbh->exec($sqlCreateTable);
    echo 'Создана таблица users';

} catch(PDOException $e) {
    echo 'Ошибка: ' . $e->getMessage();
}

Здесь мы используем метод exec() он запускает запрос на выполнение возвращая количество затронутых строк. Метод не предназначен для выборки данных из таблицы. Если всё сделано верно у вас будет создана таблица пользователей. Давайте заполним её данными.


/*Данные пользователей, в реальном проектк они могут
 быть получены из JSON, файла импорта или из формы регистрации */
$usersData = [
    [
        'name'=>'Иван Иванов',
        'email'=>'ivan@mail.ru',
        'phone'=>'+79001234567'],
    [
        'name'=>'Пётр Петров',
        'email'=>'petrov@yandex.ru',
        'phone'=>'+79281534874'],
    [
        'name'=>'Олег Сидоров',
        'email'=>'sidor@gmail.com',
        'phone'=>'+79508975248'
    ],
];

try {
    //Соединяемся с базой данных
    $dbh = new PDO('mysql:dbname=test_db;host=localhost', $user, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    //Подготовка запроса, обратите внимание что ключи в запросе совпадают с ключами массив отдельного пользователя
    $data = $dbh->prepare('INSERT INTO users (name, email, phone) VALUES(:name, :email, :phone)');

    //Перебираем массив пользователей
    foreach ($usersData as $user) {
        //Подставляем данные пользователей в запрос
        $data->execute($user);
    }

} catch(PDOException $e) {
    echo 'Ошибка: ' . $e->getMessage();
}

Если всё сделано верно, вы получите заполненную таблицу users:
Заполненная таблица users

Чтение

Прочитать записи в таблице БД можно несколькими способами, при помощи метода query и execute , давайте разберём оба:

Чтение таблицы при помощи метода query


$user = 'root';
$password = 'Ваш пароль';

//Чтобы перехватить ошибки подключения используем исключения try {...} catch () {...}
try {
    $dbh = new PDO('mysql:dbname=test_db;host=localhost', $user, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    //Выборка всех записей
    $users = $dbh->query('SELECT * FROM `users`');

    echo '
    '; foreach ($users as $user){ echo '
  • id: ' . $user['id'] . ' - name: ' . $user['name'] . ' - phone: ' . $user['phone'] . ' - email: '. $user['email'] .'
  • '; } echo '
'; } catch (PDOException $e) { //В случае ошибки выводим сообщения из перехваченного исключения echo 'Ошибка: ' . $e->getMessage(); }

В результате на экране вы увидите такую картину:
Результат чтения таблицы users
Обратите внимание на следующее:


$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Данные параметры устанавливают расширенный режим вывода ошибок и исключение PDO.

Чтение таблицы при помощи метода execute

Давайте выберем из БД одного пользователя по конкретному email адресу.


try {
    $dbh = new PDO('mysql:dbname=test_db;host=localhost', $user, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    //Вывод пользователя с email адресом ivan@mail.ru

    //Подготовка запроса
    $data = $dbh->prepare('SELECT * FROM `users` WHERE `email` = :user_email');
    // Выполнение с подстановкой параметра в запрос
    $data->execute(['user_email'=>'ivan@mail.ru']);

    //Получаем результат
    $users = $data->fetchAll();

    echo '
    '; foreach ($users as $user){ echo '
  • id: ' . $user['id'] . ' - name: ' . $user['name'] . ' - phone: ' . $user['phone'] . ' - email: '. $user['email'] .'
  • '; } echo '
'; } catch (PDOException $e) { //В случае ошибки выводим сообщения из перехваченного исключения echo 'Ошибка: ' . $e->getMessage(); }

В данном примере используется метод prepare т.е. подготовка запроса. Это позволят почти с 100% гарантией избежать SQL инъекций т.к. подставляемый параметры email не встраивается в зарос напрямую, в запросе есть лишь метка :email куда через метод execute будет подставлена переданная через ассоциативный массив переменная.

В результате выполнения запроса, получаем одну запись из таблицы users:

Чтение таблицы методом execute

Редактирование

Давайте теперь изменим контактный телефон для пользователя с id равным 2 и сразу посмотрим на результат (выведем на экран список всех пользователей).


try {
    //Соединяемся с базой данных
    $dbh = new PDO('mysql:dbname=test_db;host=localhost', $user, $password);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    //Подготовка запроса
    $data = $dbh->prepare('UPDATE  users SET phone = :phone WHERE id = :id');

    //Подстановка параметров
    $data->execute([
        'phone'=>'+7 900 000 00 00',
        'id' => 2
    ]);

    //Выведем на экран сколько строк затронул наш запрос
    echo 'Обновлено строк таблицы: ' . $data->rowCount();

    //И сразу посмотрим на результат, выведем всех пользователей
    $users = $dbh->query('SELECT * FROM `users`');

    echo '
    '; foreach ($users as $user){ echo '
  • id: ' . $user['id'] . ' - name: ' . $user['name'] . ' - phone: ' . $user['phone'] . ' - email: '. $user['email'] .'
  • '; } echo '
'; } catch(PDOException $e) { echo 'Ошибка: ' . $e->getMessage(); }

Как видите, запись обновилась:
Обновление записи в БД

Отлично, осталось понять как удалять запись. Давайте удалим того же пользователя, этот Пётр Петров мне надоел =)

Синтаксис

array PDO::errorInfo();  

Возвращаемое значение

Массив сведений об ошибке для последней операции с дескриптором базы данных. Этот массив состоит из следующих полей:

  • Код ошибки SQLSTATE.

  • Код ошибки, относящийся к драйверу.

  • Сообщение об ошибке, относящееся к драйверу.

Если ошибка отсутствует или не задано SQLSTATE, то поля, относящиеся к драйверу, будут иметь значение NULL.

PDO::errorInfo возвращает только сведения об ошибках для операций, выполненных непосредственно в базе данных. Используйте PDOStatement::errorInfo при создании экземпляра PDOStatement с помощью PDO::prepare или PDO::query.

Поддержка PDO была добавлена в версии 2.0 Драйверы Microsoft SQL Server для PHP.

Пример

В этом примере неправильно указано имя столбца (Cityx вместо City), что вызывает ошибку, о которой затем сообщается.

query($query);  
print $conn->errorCode();  
echo "\n";  
print_r ($conn->errorInfo());  
?>  

Дополнительные сообщения ODBC

При возникновении исключения драйвер ODBC может вернуть несколько ошибок, которые помогают в диагностике проблем. Однако PDO::errorInfo всегда выдает только первую ошибку. В ответ на этот отчет об ошибке функции PDO::errorInfo и PDOStatement::errorInfo были обновлены: теперь драйверы должны выводить по крайней мере следующие три поля:

0   SQLSTATE error code (a five characters alphanumeric identifier defined in the ANSI SQL standard).
1   Driver specific error code.
2   Driver specific error message.

Начиная с версии 5.9.0 функция PDO::errorInfo по умолчанию выводит дополнительные ошибки ODBC при их наличии. Пример.

setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $stmt = $conn->prepare("SET NOCOUNT ON; USE $database; SELECT 1/0 AS col1");
    $stmt->execute();
} catch (PDOException $e) {
    var_dump($e->errorInfo);
}
?>  

При выполнении приведенного выше скрипта должно было произойти исключение, и выходные данные должны выглядеть так:

array(6) {
  [0]=>
  string(5) "01000"
  [1]=>
  int(5701)
  [2]=>
  string(91) "[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Changed database context to 'tempdb'."
  [3]=>
  string(5) "22012"
  [4]=>
  int(8134)
  [5]=>
  string(87) "[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Divide by zero error encountered."
}

Если пользователь предпочитает прежнее поведение, отключить новое можно с помощью параметра конфигурации pdo_sqlsrv.report_additional_errors. Просто добавьте следующую строку в начало любого скрипта PHP:

ini_set('pdo_sqlsrv.report_additional_errors', 0);

В этом случае при выполнении того же примера скрипта будут показаны следующие сведения об ошибках:

array(3) {
  [0]=>
  string(5) "01000"
  [1]=>
  int(5701)
  [2]=>
  string(91) "[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Changed database context to 'tempdb'."
}

При необходимости пользователь может добавить следующую строку в файл php.ini, чтобы отключить эту функцию во всех скриптах PHP:

pdo_sqlsrv.report_additional_errors = 0

Предупреждения и ошибки

Начиная с версии 5.9.0 предупреждения ODBC больше не будут регистрироваться как ошибки. То есть коды ошибок с префиксом 01 регистрируются как предупреждения. Иными словами, если вы хотите регистрировать только ошибки, измените файл php.ini следующим образом:

[pdo_sqlsrv]  
pdo_sqlsrv.log_severity = 1

В этом случае в файле журнала не будет сообщений с предупреждениями. Ознакомьтесь с тем, как ведется журнал для пользователей pdo_sqlsrv.

1: Создание базы данных

Прежде всего мы создадим базу данных и таблицу. Подключитесь к серверу по SSH и войдите в MySQL как root:

sudo mysql -u root -p

Введите root-пароль сервера MySQL и нажмите Enter, чтобы продолжить. Затем выполните следующую команду, чтобы создать базу данных по имени cron_jobs.

CREATE DATABASE cron_jobs;

Создайте для базы данных пользователя без привилегий root. Учетные данные этого пользователя потребуются вам позже для подключения к базе данных cron_jobs из PHP. Не забудьте заменить EXAMPLE_PASSWORD надежным паролем:

CREATE USER 'cron_jobs_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
GRANT ALL PRIVILEGES ON cron_jobs.* TO 'cron_jobs_user'@'localhost';
FLUSH PRIVILEGES;

Затем перейдите в эту БД:

USE cron_jobs;
Database changed

После этого создайте таблицу tasks. В эту таблицу мы вставим несколько задач, которые будут автоматически выполняться демоном cron. Поскольку минимальный интервал между двумя задачами cron составляет 1 минуту, позже мы создадим сценарий PHP, который позволит нам преодолеть это ограничение и будет выполнять задачи с интервалом в 5 секунд.

А пока создайте таблицу:

CREATE TABLE tasks (
task_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
task_name VARCHAR(50),
queued_at DATETIME,
completed_at DATETIME,
is_processed CHAR(1)
) ENGINE = InnoDB;

Вставьте в таблицу три записи. Используйте функцию MySQL NOW() в столбце queued_at, чтобы записать текущую дату и время помещения задачи в очередь. Для столбца completed_at используйте функцию CURDATE(), чтобы установить время по умолчанию 00:00:00. Позже, – по мере выполнения задач, – ваш скрипт обновит этот столбец:

INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 1', NOW(), CURDATE(), 'N');
INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 2', NOW(), CURDATE(), 'N');
INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 3', NOW(), CURDATE(), 'N');

После выполнения каждой команды INSERT вы получите:

Query OK, 1 row affected (0.00 sec)
...

Убедитесь, что данные на своем месте, выполнив оператор SELECT для таблицы tasks:

SELECT task_id, task_name, queued_at, completed_at, is_processed FROM tasks;

Вы найдете список всех задач:

+---------+-----------+---------------------+---------------------+--------------+
| task_id | task_name | queued_at           | completed_at        | is_processed |
+---------+-----------+---------------------+---------------------+--------------+
|       1 | TASK 1    | 2021-03-06 06:27:19 | 2021-03-06 00:00:00 | N            |
|       2 | TASK 2    | 2021-03-06 06:27:28 | 2021-03-06 00:00:00 | N            |
|       3 | TASK 3    | 2021-03-06 06:27:36 | 2021-03-06 00:00:00 | N            |
+---------+-----------+---------------------+---------------------+--------------+
3 rows in set (0.00 sec)

В столбце completed_at установлено время 00:00:00, и далее этот столбец будет обновляться – после обработки задач сценарием PHP, который мы создадим вскоре.

Выйдите из командной строки MySQL:

QUIT;
Bye

Теперь у вас есть база данных cron_jobs и таблица tasks. Приступим к написанию сценария PHP, который обрабатывает задачи.

2: Создание PHP-скрипта

На этом шаге мы напишем сценарий, который комбинирует цикл PHP while(…){…} и функцию sleep, что позволяет ему выполнять задачи через каждые 5 секунд.

Откройте новый файл /var/www/html/tasks.php в корневом каталоге вашего веб-сервера:

sudo nano /var/www/html/tasks.php

Создайте новый блок try { после тега



try {

    $db_name     = 'cron_jobs';

    $db_user     = 'cron_jobs_user';

    $db_password = 'EXAMPLE_PASSWORD';

    $db_host     = 'localhost';

Объявите новый класс PDO (что значит PHP Data Object) и установите атрибут ERRMODE_EXCEPTION для перехвата ошибок PDO. Кроме того, нужно установить значение false для параметра ATTR_EMULATE_PREPARES, чтобы позволить собственному ядру базы данных MySQL обрабатывать эмуляцию. Эти операторы позволяют отправлять SQL-запросы и данные отдельно – для повышения безопасности и снижения вероятности SQL-инъекций:

$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Затем создайте переменную $loop_expiry_time и установите в качестве ее значения текущее время плюс 60 секунд. Затем откройте новый оператор PHP while(time()

$loop_expiry_time = time() + 60;

while (time() 

Затем объявите подготовленный SQL-оператор, который извлекает необработанные задачи из таблицы:

$data = [];
$sql  = "select
task_id
from tasks
where is_processed = :is_processed
";

Выполните команду SQL и выберите из таблицы tasks все строки, в которых для столбца is_processed установлено значение N – это означает, что строки не обрабатываются.

$data['is_processed'] = 'N';

$stmt = $pdo->prepare($sql);

$stmt->execute($data);

Затем выполните цикл по извлеченным строкам с помощью оператора while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {…} и создайте еще один SQL оператор. На этот раз команда будет обновлять столбцы is_processed и completed_at для каждой обработанной задачи. Это гарантирует, что скрипт не будет обрабатывать задачи более одного раза:

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

    $data_update   = [];

    $sql_update    = "update tasks set

                      is_processed  = :is_processed,

                      completed_at  = :completed_at

                      where task_id = :task_id

                      ";

    $data_update   = [

                     'is_processed' => 'Y',

                     'completed_at' => date("Y-m-d H:i:s"),

                     'task_id'      => $row['task_id']

                     ];

    $stmt = $pdo->prepare($sql_update);

    $stmt->execute($data_update);

}

Примечание: Если вам нужно обработать большую очередь (например, 100 000 записей в секунду), вы можете рассмотреть возможность создания очереди на сервере Redis, поскольку он быстрее, чем MySQL, и больше подходит для обработки подобных объемов.

Прежде чем закрыть первый оператор while (time()

Вы можете изменить этот интервал в зависимости от вашей бизнес-логики и того, насколько быстро вы хотите выполнять задачи. Например, если вы хотите, чтобы задачи обрабатывались 3 раза в минуту, установите в sleep значение 20.

Не забудьте добавить catch, чтобы перехватить сообщения об ошибках PDO внутри блока } catch (PDOException $ex) { echo $ex->getMessage(); }:

sleep(5);

        }

} catch (PDOException $ex) {

    echo $ex->getMessage();

}

Готовый скрипт tasks.php будет иметь следующий вид:



try {

    $db_name     = 'cron_jobs';

    $db_user     = 'cron_jobs_user';

    $db_password = 'EXAMPLE_PASSWORD';

    $db_host     = 'localhost';

    $pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);

    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    $loop_expiry_time = time() + 60;

    while (time() prepare($sql);

        $stmt->execute($data);

        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

            $data_update   = [];

            $sql_update    = "update tasks set

                              is_processed  = :is_processed,

                              completed_at  = :completed_at

                              where task_id = :task_id

                              ";

            $data_update   = [

                             'is_processed' => 'Y',

                             'completed_at' => date("Y-m-d H:i:s"),

                             'task_id'      => $row['task_id']

                             ];

            $stmt = $pdo->prepare($sql_update);

            $stmt->execute($data_update);

        }

        sleep(5);

        }

} catch (PDOException $ex) {

    echo $ex->getMessage();

}

Сохраните файл, нажав Ctrl + X, Y, затем Enter.

Завершив написание скрипта в файле /var/www/html/tasks.php, мы можем автоматизировать запуск этого скрипта с помощью демона cron. Демон будет запускать скрипт через 1 минуту на следующем шаге.

3: Планирование запуска PHP-скрипта в cron

В Linux вы можете запланировать автоматический запуск задач по истечении установленного времени. Для этого нужно добавить команду в файл crontab. На этом этапе мы настроим crontab для запуска сценария /var/www/html/tasks.php каждую минуту. Итак, откройте файл /etc/crontab с помощью nano:

sudo nano /etc/crontab

Затем добавьте в конец файла следующую строку, чтобы перезапускать http://localhost/tasks.php каждую минуту:

...
* * * * * root /usr/bin/wget -O - http://localhost/tasks.php

Сохраните и закройте файл.

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

Читайте также: Автоматизация задач с помощью Cron

Как говорилось ранее, демон cron запускает файл tasks.php только раз в 1 минуту, но после первого выполнения файла он будет перебирать открытые задачи в цикле еще 60 секунд. По истечении времени цикла демон cron снова запустит файл, и процесс продолжится.

Обновив /etc/crontab, демон crontab должен немедленно начать выполнение задач MySQL, которые вы вставили в таблицу tasks. Чтобы убедиться, что все работает должным образом, запросите свою базу данных cron_jobs.

4: Тестирование настройки

На этом этапе мы снова откроем свою базу данных и проверим, обрабатывает ли файл tasks.php поставленные в очередь задачи при автоматическом запуске через crontab.

Войдите на свой сервер MySQL как root:

sudo mysql -u root -p

Затем введите root пароль MySQL и нажмите Enter, чтобы продолжить. Затем перейдите в базу данных:

USE cron_jobs;
Database changed

Запустите оператор SELECT для таблицы tasks:

SELECT task_id, task_name, queued_at, completed_at, is_processed FROM tasks;

Вы получите примерно следующий результат. Задачи в столбце completed_at были обработаны с интервалом в 5 секунд. Кроме того, задачи отмечены как выполненные – в столбце is_processed теперь установлено значение Y, что означает YES.

+---------+-----------+---------------------+---------------------+--------------+
| task_id | task_name | queued_at           | completed_at        | is_processed |
+---------+-----------+---------------------+---------------------+--------------+
|       1 | TASK 1    | 2021-03-06 06:27:19 | 2021-03-06 06:30:01 | Y            |
|       2 | TASK 2    | 2021-03-06 06:27:28 | 2021-03-06 06:30:06 | Y            |
|       3 | TASK 3    | 2021-03-06 06:27:36 | 2021-03-06 06:30:11 | Y            |
+---------+-----------+---------------------+---------------------+--------------+
3 rows in set (0.00 sec)

Это значит, что PHP-скрипт работает правильно; задачи были запущены в более короткий интервал времени, переопределив ограничение crontab.

Создание Таблицы (По необходимости)

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

phpMyAdmin в Панели Управления Хостингом Hostinger

После входа на страницу phpMyAdmin вы увидите подобную картину:

Меню phpMyAdmin

Давайте создадим таблицу с названием Students для нашей базы данных u104357129_name. Создать новую таблицу можно, нажав на кнопку Create Table. После этого вы увидите новую страницу со всеми необходимыми полями для ввода данных.

Окно с Полями для Создания Таблицы MySQL

Поскольку создание таблицы не основная тема этого руководства, мы не будем углубляться в подробности. Если вам нужно больше информации о структуре таблицы/базы данных и о том, какие настройки вы можете использовать для каждого столбца, рекомендуем обратиться к официальной документации phpMyAdmin (англ.).

Здесь приведём лишь несколько простых пояснение столбцов, которые мы будем использовать:

  • Name — это имя столбца. Будет отображено в самом верху вашей таблицы.
  • Type — тип ваших данных. Вы можете указать любой тип: int, varchar, string и многие другие. Например, мы указали varchar, поскольку нам нужно ввести строку переменной длины, содержащую буквы, не цифры.
  • Length/Values — используется для задания максимальной длины вашей записи в столбце.
  • Index — для нашего поля “ID” мы используем индекс “Primary”. Когда создаётся таблица, рекомендуется иметь один столбец ID. Он используется для индексации записей в таблице, когда настраиваются взаимосвязи между таблицами. Здесь также можно отметить “A_I”, что означает Auto Increment. Эта настройки будет автоматически увеличивать индекс (1,2,3,4…).

Нажмите Save, чтобы сохранить вашу таблицу.

Создание PHP Кода и Добавление Записи в Таблицу MySQL (Insert)

Есть два способа вставить данные в вашу базу данных MySQL. Метод PHP MySQLi и PHP Data Object, или PDO.

Метод с MySQLi

Прежде всего, нужно установить соединение с базой данных. После этого мы можем продолжить с SQL запросом на добавление записи в таблицу MySQL — INSERT. Ниже показан полный пример PHP кода с методами подключения и вставки:

" . mysqli_error($conn);
}
mysqli_close($conn);

?>

Первая часть кода (строки 3 – 18) касается подключения к базе данных. Мы не станем углубляться, так как у нас есть отдельное подробное руководство по этой теме. Если вы хотите знать, что означает каждая строка, читайте, как подключиться к базе данных.

Начнём со строки 19:

$sql = "INSERT INTO Students (name, lastname, email) VALUES ('Test', 'Testing', 'Testing@tesing.com')";

Это наиболее важная строка PHP кода, поскольку именно она отвечает за добавление записи в таблицу MySQL. INSERT INTO — это выражение, которое добавляет запись в указанную таблицу базы данных MySQL. В нашем примере мы добавляем данные в таблицу Students.

Двигаясь дальше, в скобках, мы указываем имена столбцов таблицы, в которые хотим добавить значения: (name, lastname, email). Данные будут добавлены в определённом порядке. Если мы напишем (email, lastname, name), значения будут добавлены в неправильном порядке.

Следующая часть — выражение VALUES. Здесь мы задаём наши значения в ранее указанные поля. Таким образом, каждый столбец получит своё значение. Например, в нашем случае это будет что-то вроде: name = Test, lastname = Testing, email = Testing@testing.com.

Также стоит отметить, что мы только что выполнили SQL-запрос с использованием кода PHP. SQL-запросы должны быть заключены в кавычки. В нашем примере всё между кавычками и записанное после $sql = является SQL-запросом.

Следующая часть кода (20 – 22 строки) проверяет успешность нашего запроса:

if (mysqli_query($conn, $sql)) {
     echo "New record created successfully";
}

Мы увидим это сообщение, если запрос выполнен успешно.

И последняя часть ( строки 22 – 24) демонстрирует пример другого сообщения.

else {
     echo "Error: " . $sql . "
" . mysqli_error($conn); }

Вы увидите подобное сообщение в случае возникновения ошибки при попытке отправить запрос.

Метод с PHP Data Object (PDO)

Как и в предыдущем примере, нам нужно прежде всего выполнить подключение к базе данных, которое производится при создании нового объекта PDO. Как это сделать, показано в уже не раз упомянутом руководстве. Поскольку подключение к базе данных MySQL является объектом PDO, мы должны использовать различные PDO методы (любую функцию, которая является частью любого объекта) для подготовки и запуска запросов. Методы объектов вызываются слудеющим образом:

$the_Object->the_Method();

PDO позволяет вам подготовить SQL-код перед его выполнением. Перед запуском SQL-запрос оценивается и корректируется. Не секрет, что взломщики могут модифицировать запрос. SQL-инъекция может быть осуществлена просто путём ввода SQL-кода в поле формы. Например:

// Пользователь вписывает это в поле имени пользователя в форме для входа
john"; DROP DATABASE user_table;

// И вот, во что превращается окончательный запрос  
"SELECT * FROM user_table WHERE username = john"; DROP DATABASE user_table;

Поскольку SQL-код синтаксически правильный, точка с запятой делает из DROP DATABASE user_table новый SQL-запрос, а ваша таблица пользователей удаляется. Подготовляемые запросы, также известные как связываемые переменные не позволят завершить запрос, использовав символы  и ;. Как результат, вредоносная инструкция DROP DATABASE не будет выполнена.

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

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

В правильном виде код выглядит так:

 PDO::ERRMODE_EXCEPTION];

// Создаём новое соединение с базой данных MySQL с использованием PDO, $my_Db_Connection - это объект
try { 
  $my_Db_Connection = new PDO($sql, $username, $password, $dsn_Options);
  echo "Connected successfully";
} catch (PDOException $error) {
  echo 'Connection error: ' . $error->getMessage();
}

// Задаём переменные для человека, которого мы хотим добавить в базу данных
$first_Name = "Test";
$last_Name = "Testing";
$email = "Testing@testing.com";

// Здесь мы создаём переменную, которая вызывает метод prepare() объекта базы данных
// SQL-запрос, который вы хотите запустить, вводится как параметр, а плейсхолдеры записываются следующим образом - :placeholder_name
$my_Insert_Statement = $my_Db_Connection->prepare("INSERT INTO Students (name, lastname, email) VALUES (:first_name, :last_name, :email)");

// Теперь мы сообщаем скрипту, на какую переменную фактически ссылается каждый плейсхолдер, используя метод bindParam()
// Первый параметр - это плейсхолдер в приведённом выше выражении, второй параметр - это переменная, на которую он должен ссылаться
$my_Insert_Statement->bindParam(:first_name, $first_Name);
$my_Insert_Statement->bindParam(:last_name, $last_Name);
$my_Insert_Statement->bindParam(:email, $email);

// Выполните запрос, используя данные, которые мы только-что определили
// Метод execute() возвращает TRUE, если запрос был выполнен успешно и FALSE в противном случае, позволяя вам создавать собственные сообщения
if ($my_Insert_Statement->execute()) {
  echo "New record created successfully";
} else {
  echo "Unable to create record";
}

// На этом этапе вы можете изменить данные переменных и выполнить запрос снова, чтобы добавить больше данных в БД.
$first_Name = "John";
$last_Name = "Smith";
$email = "john.smith@email.com";
$my_Insert_Statement->execute();

// Выполните снова с другими переменными 
if ($my_Insert_Statement->execute()) {
  echo "New record created successfully";
} else {
  echo "Unable to create record";
}

В строках 28, 29 и 30 мы используем метод bindParam() объекта базы данных. Есть так же метод bindValue(), отличающийся от предыдущего.

  • bindParam() — связывает переменную PHP с параметром, достигнув execute(). Первый раз, когда скрипт доходит до метода execute() он видит, что $first_Name ссылается на “Test”, связывает это значение и выполняет запрос. Когда скрипт добирается до метода execute() второй раз, он смотрит, что $first_Name теперь ссылается на “John”, связывает это значение и запускает запрос опять с новым значением. Важно понимать, что мы создаём запрос один раз и повторно используем его, подставляя разные данные в разных местах скрипта.
  • bindValue() — этот метод связывает параметр с заданным значением. Поскольку заданное значение $first_Name — “Test”, при достижении метода bindValue(), оно будет использоваться каждый раз при вызове метода execute() для $my_Insert_Statement.

Обратите внимание, что мы повторно используем переменную $first_Name и задаём ей новое значение во второй раз. Если вы проверите свою базу данных после запуска этого скрипта, там будут оба заданных имени, даже если переменная $first_Name в конце скрипта равна “John”. Помните, что PHP оценивает содержимое всего скрипта перед его выполнением.

Если вы измените свой скрипт, заменив bindParam на bindValue, вы добавите в базу MySQL “Test Testing” дважды, а John Smith будет проигнорирован.

Проверка Статуса Выполнения и Устранение Распространённых Ошибок

Если запрос, который мы выполнили и вставили в базу данных MySQL, был успешным, мы увидим следующее сообщение:

Connect Successfully
New record created successfully

Устранение Распространённых Ошибок

Если же запись содержит ошибку, вы увидите соответствующее сообщение. Но не волнуйтесь, есть множество вариантов, как можно устранить эти ошибки mySQL.

MySQLi

Если вы столкнулись с ошибкой, работая с MySQLi, причина скорее всего в неправильном синтаксисе. Для примера давайте сделаем одну синтаксическую ошибку в нашем коде. В результате мы увидим что-то вроде этого:

Connect successfully
Error: INSERT INTO students {name, lastname, email} VALUES ('Test', 'Testing', 'Testing@testing.com')
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{name, lastname, email} VALUES ('Test', 'Testing', 'Test@testingcom')' at line 1"

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

"Error: INSERT INTO Students {name, lastname, email} VALUES ('Thom', 'Vial', 'thom.v@some.com') You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{name, lastname, email} VALUES ('Thom', 'Vial', 'thom.v@some.com')' at line 1"

Допущена синтаксическая ошибка, которая привела к невозможности выполнения нашего скрипта. Ошибка была допущена здесь:

$sql = "INSERT INTO Students {name, lastname, email} VALUES ('Thom', 'Vial', 'thom.v@some.com')";

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

PDO

В строке 7 подключения PDO, режим обработки ошибок установлен в «display all exceptions» (отображать все исключения). Если вы уберёте это из скрипта и запрос потерпит неудачу, вы не получите никакого сообщения об ошибке. Со включёнными исключениями, будут отображаться конкретные возникшие проблемы.

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

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '{name, lastname, email} VALUES ('Thom', 'Vial', 'thom.v@some.com')' at line 1"

Другие проблемы, с которым вы можете столкнуться:

  • Неверно указаны столбцы (несуществующие столбцы или ошибки в написании названий).
  • Несоответствие типа значения типу столбца. Например, когда мы хотим присвоить числовое значение 47 полю Name, мы получим ошибку, потому что предполагается, что значение будет строкой. Но, если вы укажете число в кавычках, например, “47”, ошибки не будет, потому что наше число будет записано как строка в этот столбец.
  • Попытка ввести данные в таблицу, которой не существует или ошибка в написании названия таблицы.

Все эти ошибки могут быть исправлены, следуя руководствам по исправлению ошибок или журнала ошибок (англ.).

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

Добавление Записи в Таблицу MySQL - Вид Таблицы в phpMyAdmin

Конструктор

$db = new \DB\SQL ( string $dsn [, string $user = NULL [, string $pw = NULL [, array $options = NULL ]]] );

Например, для подключения к базе данных MySQL синтаксис выглядит так:

$db=new \DB\SQL('mysql:host=localhost;port=3306;dbname=mysqldb','username','password');\
 

Подключение к базе данных SQLite будет выглядеть так:

$db=new \DB\SQL('sqlite:/path/to/db.sqlite');

Четвертый параметр - это набор параметров, которые вы можете использовать для установки дополнительных атрибутов PDO:

$options = array(
    \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, // generic attribute
    \PDO::ATTR_PERSISTENT => TRUE,  // we want to use persistent connections
    \PDO::MYSQL_ATTR_COMPRESS => TRUE, // MySQL-specific attribute
);
$db = new \DB\SQL('mysql:host=localhost;port=3306;dbname=mysqldb','username','password', $options);

Вот список ссылок на сведения о DSN-соединении для всех поддерживаемых в настоящее время движков на уровне SQL:

  • mysql : MySQL 5.x

  • sqlite : SQLite 3 и SQLite 2

  • pgsql : PostgreSQL

  • sqlsrv : Microsoft SQL Server / SQL Azure

  • mssql, dblib, sybase : FreeTDS / Microsoft SQL Server / Sybase

  • odbc : ODBC v3

  • oci : Oracle