Создание сайтов в Кировске, ЛНР. Отображение данных из MySQL в Интернете

Следующая статья представляет собой выдержку из PHP и MySQL: от новичка до ниндзя, 7-е издание, практического руководства по изучению всех инструментов, принципов и методов, необходимых для создания профессионального веб-приложения. В этом заключительном руководстве серии вы узнаете, как получить информацию, хранящуюся в базе данных MySQL, и отобразить ее на веб-странице для всеобщего обозрения.

Настройка среды разработки PHP с помощью Docker

Руководство для начинающих по PHP

Знакомство с MySQL: руководство для начинающих

Отображение данных из MySQL в Интернете: введение

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

Итак, вы написали свой первый PHP-код и изучили основы MySQL, механизма реляционной базы данных, и PHP, языка сценариев на стороне сервера.

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

Примечание: как и в главе 3, здесь я использую «MySQL» для обозначения протокола базы данных. Ваши PHP-скрипты будут делать то же самое. В этой главе и в PHP-коде, который вы напишете, есть множество ссылок на «MySQL», хотя на самом деле мы подключаемся к базе данных MariaDB.

Большая картинка

Прежде чем мы прыгнем вперед, стоит сделать шаг назад, чтобы получить четкое представление о нашей конечной цели. В нашем распоряжении есть два мощных инструмента: язык сценариев PHP и механизм базы данных MySQL. Важно понять, как они будут сочетаться друг с другом.

Целью использования MySQL для нашего веб-сайта является обеспечение возможности динамического извлечения контента из базы данных для создания веб-страниц для просмотра в обычном браузере. Итак, на одном конце системы у вас есть посетитель вашего сайта, использующий веб-браузер для запроса страницы. Этот браузер ожидает получить взамен стандартный HTML-документ. На другом конце у вас есть контент вашего сайта, который находится в одной или нескольких таблицах в базе данных MySQL, которая понимает только, как отвечать на SQL-запросы (команды).

Отношения между веб-сервером, браузером, PHP и MySQL

Как показано на изображении выше, язык сценариев PHP является посредником, говорящим на обоих языках. Он обрабатывает запрос страницы и извлекает данные из базы данных MySQL, используя SQL-запросы, точно такие же, как те, которые вы использовали для создания таблицы шуток в главе 3. Затем он динамически выдает их в виде хорошо отформатированной HTML-страницы, которую ожидает браузер.

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

Веб-браузер посетителя запрашивает веб-страницу с вашего веб-сервера.

Программное обеспечение веб-сервера (обычно Apache или NGINX) распознает запрошенный файл как сценарий PHP, поэтому сервер запускает интерпретатор PHP для выполнения кода, содержащегося в файле.

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

База данных MySQL отвечает, отправляя запрошенный контент сценарию PHP.

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

Интерпретатор PHP завершает работу, передавая копию созданного им HTML-кодавеб-серверу.

Веб-сервер отправляет HTML-код в веб-браузер, как обычный HTML-файл, за исключением того, что вместо того, чтобы поступать непосредственно из HTML-файла, страница представляет собой вывод, предоставляемый интерпретатором PHP. Однако браузер не имеет возможности узнать об этом. Что касается браузера, он запрашивает и получает веб-страницу, как и любую другую.

Создание учетной записи пользователя MySQL

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

Среда Docker уже содержит пользователя MySQL в главе 3, которого вы уже использовали для входа на сервер MySQL.

Вы можете подключиться к базе данных из своего PHP-скрипта, используя то же имя пользователя (v.je) и пароль (v.je), но полезно создать новую учетную запись, потому что, если у вас есть веб-сервер, вы можете использовать его для размещения более одного веб-сайта... Предоставив каждому веб-сайту собственную учетную запись пользователя, вы сможете лучше контролировать, кто имеет доступ к данным для любого данного сайта. Если вы работаете с другими разработчиками, вы можете предоставить им доступ к сайтам, над которыми они работают, но не более того.

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

Чтобы создать пользователя, откройте MySQL Workbench и подключитесь к своему серверу. Затем выполните следующие запросы:

CREATE USER 'ijdbuser’@'%' IDENTIFIED BY 'mypassword’;

GRANT ALL PRIVILEGES ON `ijdb`. * TO 'ijdbuser’@'%';

Первый запрос говорит сам за себя: он создает пользователя ijdbuserс паролем mypassword. Знак%после имени пользователя указывает, что к базе данных можно подключиться из любого места. Второй запрос дает пользователю полный доступ к ijdbсхеме, в результате этот пользователь может видеть и изменять все таблицы, столбцы и данные в ijdbсхеме, но не имеет доступа ни к чему за ее пределами.

Теперь, когда пользователь ijdbuserсоздан, мы можем использовать его для подключения к базе данных. С этим пользователем можно установить соединение в MySQL Workbench, но, поскольку права доступа ограничены, лучше оставить MySQL Workbench с использованием v.jeучетной записи. Вместо этого мы будем использовать нового пользователя при подключении из PHP-скрипта.

Подключение к MySQL с помощью PHP

Прежде чем вы сможете извлекать контент из базы данных MySQL для включения в веб-страницу, вы должны знать, как установить соединение с MySQL из скрипта PHP. До сих пор вы использовали приложение под названием MySQL Workbench для подключения к вашей базе данных. Точно так же, как MySQL Workbench может напрямую подключаться к работающему серверу MySQL, то же самое можно сказать и о ваших собственных PHP-скриптах.

Хотя в этой главе речь идет исключительно о подключении к MySQL из PHP, на самом деле мы подключаемся к базе данных MariaDB, о которой говорилось в предыдущей главе. PHP не видит никакой разницы между MySQL и MariaDB, поскольку они взаимозаменяемы. Я буду ссылаться на базу данных как на MySQL, потому что все используемые команды могут использоваться для подключения к серверу базы данных MySQL или MariaDB.

Первоначальная база данных MySQL предоставляла стандартизированный метод связи с сервером для таких клиентов, как MySQL Workbench и PHP. MariaDB скопировала этот стандарт, и все команды в PHP используют имя MySQL, поэтому для простоты я буду использовать термин MySQL в этой главе для обозначения базы данных.

Существует три способа подключения к серверу MySQL из PHP:

библиотека MySQL

библиотека MySQLi

библиотека PDO

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

Библиотека MySQL является старейшим методом подключения к базе данных и была представлена ​​в PHP 2.0. Содержащиеся в нем функции минимальны, и он был заменен MySQLi с версии PHP 5.0 (выпущенной в 2004 году).

Для подключения и запроса базы данных с помощью старой библиотеки MySQL используются такие функции, как mysql_connect () и mysql_query (). Эти функции объявлены устаревшими, то есть их следует избегать, начиная с PHP 5.5, и полностью удалены из PHP, начиная с PHP 7.0.

Хотя большинство разработчиков увидели причину изменения сразу после выпуска PHP 5.0, в Интернете все еще есть сотни статей и примеров кода, использующих эти ныне несуществующие mysql_*функции, несмотря на то, что MySQLi фактически была предпочтительной библиотекой на протяжении пятнадцати лет. годы.

Если вы встретите пример кода, содержащий строку mysql_connect (), проверьте дату статьи. Это, вероятно, из начала 2000-х, и в программировании вы никогда не должны доверять чему-то старому. Все постоянно меняется — вот почему эта книга вышла в седьмом издании!

В PHP 5.0 была выпущена библиотека MySQLi — сокращение от «MySQL Improved» — для устранения некоторых ограничений исходной библиотеки MySQL. Вы можете легко определить использование MySQLi, потому что код будет использовать такие функции, как mysqli_connect () и mysqli_query ().

Вскоре после выпуска библиотеки MySQLi в PHP 5.0 был выпущен PHP 5.1 со значительным количеством изменений, которые помогли сформировать то, как мы пишем PHP сегодня (в основном это относится к объектно-ориентированному программированию, о котором вы узнаете позже). в этой книге). Одним из основных изменений в PHP 5.1 было введение третьей библиотеки PDO (объекты данных PHP) для подключения к базам данных MySQL.

Между PDO и MySQLi есть несколько различий, но главное из них заключается в том, что вы можете использовать библиотеку PDO для подключения практически к любому серверу базы данных, например к серверу Oracle или Microsoft SQL Server. Для разработчиков самым большим преимуществом такого универсального подхода является то, что после того, как вы научитесь использовать библиотеку для взаимодействия с базой данных MySQL, вам будет очень просто взаимодействовать с другим сервером базы данных.

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

По этим причинам в большинстве последних PHP-проектов используется библиотека PDO, и именно ее я покажу вам, как использовать в этой книге. Для получения дополнительной информации о различиях взгляните на статью SitePoint «Повторное представление PDO — правильный способ доступа к базам данных в PHP «.

После этого небольшого урока истории вы, вероятно, захотите вернуться к написанию кода. Вот как вы используете PDO для установки соединения с сервером MySQL:

new PDO ('mysql: host=hostname; dbname=database’, 'username’,

'password’)

А пока представьте new PDOсебе встроенную функцию, как randфункцию, которую мы использовали в главе 2. Если вы думаете: «Эй, в именах функций не может быть пробелов!», вы умнее среднего медведя., и я объясню, что здесь происходит через мгновение. В любом случае он принимает три аргумента:

строка, указывающая тип базы данных (mysql:), имя хоста сервера (host=hostname;) и имя базы данных (dbname=database)

имя пользователя MySQL, которое вы хотите использовать в PHP

пароль MySQL для этого имени пользователя

Возможно, вы помните из главы 2, что PHP-функции обычно возвращают значение при вызове. Эта new PDO«функция» возвращает значение, называемое PDOобъектом, который идентифицирует установленное соединение. Поскольку мы намерены использовать соединение, мы должны сохранить это значение, сохранив его в переменной. Вот как это выглядит с заполненными значениями, необходимыми для подключения к вашей базе данных:

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,

'mypassword’) ;

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

Первый аргумент немного сложнее. Эта dbname=ijdbчасть указывает PDO использовать базу данных (также называемую схемой) с именем ijdb. Любой запрос, запущенный из PHP, по умолчанию будет использовать таблицы в этой схеме. SELECT * FROM jokeвыберет записи из jokeтаблицы в ijdbсхеме.

Даже если вы уже знакомы с PHP, PDO и MySQL, эта host=mysqlчасть выглядит запутанной. Обычно это будет host=localhost (ссылаясь на локальный компьютер, тот же компьютер, на котором работает PHP) или указание на определенное доменное имя, на котором размещена база данных, например host=sitepoint.com.

Почему это host=mysqlи что здесь имеется mysqlв виду? В Docker каждой службе дается имя. Если вы изучите docker-compose.ymlфайл, который настраивает сервер, служба базы данных называется mysql, а в Docker одна служба может подключаться к другой, используя имя другой службы.

Аргументы в сторону, здесь важно видеть, что значение, возвращаемое, new PDOхранится в переменной с именем $pdo.

Сервер MySQL — это полностью отдельная часть программного обеспечения от веб-сервера. Поэтому мы должны учитывать возможность того, что сервер может быть недоступен или недоступен из-за сбоя в сети, или из-за того, что предоставленная вами комбинация имени пользователя и пароля отклонена сервером, или из-за того, что вы просто забыли запустить свой сервер MySQL! В таких случаях new PDOне запускается и выдает исключение PHP.

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

Если вам интересно, что значит «генерировать исключение PHP», приготовьтесь! Вы собираетесь открыть для себя еще несколько возможностей языка PHP.

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

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

Примечание: если вы не поймаете исключение, PHP прекратит выполнение вашего PHP-скрипта и отобразит эффектно уродливое сообщение об ошибке. Это сообщение об ошибке даже покажет код вашего скрипта, вызвавшего ошибку. В этом случае этот код содержит ваше имя пользователя и пароль MySQL, поэтому особенно важно, чтобы сообщение об ошибке не было видно пользователям!

Чтобы перехватить исключение, вы должны окружить код, который может вызвать исключение, try... catchоператором:

try {

⋮ do something risky

}

catch (ExceptionType $e) {

⋮ handle the exception

}

Вы можете думать об try... catchоператоре как об if... elseоператоре, за исключением того, что второй блок кода — это то, что происходит, если первый блок кода не выполняется.

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

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,

’mypassword’) ;

$output = 'Database connection established.';

}

catch (PDOException $e) {

$output = 'Unable to connect to the database server.';

}

include __DIR__. '/... /templates/output.html.php’;

Как видите, этот код является try... catchоператором. В tryблоке вверху мы пытаемся подключиться к базе данных с помощью new PDO. Если это удается, мы сохраняем полученный объект PDO $pdo, чтобы мы могли работать с нашим новым подключением к базе данных. Если соединение установлено успешно, $outputпеременной присваивается сообщение, которое будет отображаться позже.

Важно отметить, что внутри try... catchинструкции любой код после создания исключения не будет выполняться. В этом случае, если при подключении к базе данных возникает исключение (возможно, неверный пароль или сервер не отвечает), $outputпеременной никогда не будет присвоено значение «Соединение с базой данных установлено».

Если наша попытка подключения к базе данных не удалась, PHP выдаст PDOExceptionисключение, которое является типом исключения new PDO. Таким образом, наш catchблок говорит, что он перехватит исключение PDOException (и сохранит его в переменной с именем $e). Внутри этого блока мы устанавливаем переменную $output, чтобы она содержала сообщение о том, что пошло не так.

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

Переменная $eсодержит сведения о возникшем исключении, включая сообщение об ошибке с описанием проблемы. Мы можем добавить это к выходной переменной, используя конкатенацию:

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,

'mypassword’) ;

$output = 'Database connection established.';

}

catch (PDOException $e) {

$output = 'Unable to connect to the database server: '. $e→getMessage () ;

}

include __DIR__. '/... /templates/output.html.php’;

Примечание: $eпеременная не строка, а объект. Вскоре мы придем к тому, что это значит. Однако на данный момент все, что вам нужно знать, это то, что код $e→getMessage () получает сообщение об ошибке в зависимости от возникшего конкретного исключения.

Как и if... elseоператор, одна из двух ветвей try... catchоператора гарантированно будет выполнена. Либо код в tryблоке будет выполнен успешно, либо код в catchблоке будет выполнен. Независимо от того, было ли соединение с базой данных успешным, в переменной будет сообщение $output— либо сообщение об ошибке, либо сообщение о том, что соединение было успешным.

Наконец, независимо от того, tryбыл ли блок успешным или catchблок выполняется, шаблон output.html.phpвключается. Это общий шаблон, который просто отображает некоторый текст на странице:

<! doctype html>

 

 

 

 

 

 

<? php echo $output;? >

 

 

Полный код можно найти в примере: MySQL-Connect.

Когда шаблон включен, он будет отображать либо сообщение об ошибке, либо сообщение «Соединение с базой данных установлено».

Я надеюсь, что вышеупомянутый код теперь имеет для вас какой-то смысл. Не стесняйтесь вернуться к началу этого раздела и прочитать его снова, если вы заблудились, так как там были некоторые хитрые концепции. Однако, как только вы прочно усвоите код, вы, вероятно, поймете, что я все еще оставил одну загадку необъясненной: PDO. Что такое new PDO, и когда я сказал, что он возвращает «объект PDO», что именно является объектом?

Примечание. Весь загруженный пример кода включает в себя схему с именем ijdb_sampleи пользователя с именем ijdb_sample, так что вы можете запустить его независимо от того, как вы назвали свою схему и пользователя. Файл, содержащий базу данных, предоставляется в формате database.sql, который вы можете импортировать.

Если вы используете предоставленное веб-средство просмотра кода примера, idbj_sampleбаза данных будет создана при загрузке образца, но любые изменения в этой схеме будут потеряны при просмотре другого образца. (Вы можете все испортить, и переключение на другой образец и обратно сбросит его, но если вы хотите сохранить любые сделанные вами изменения, внесите их в созданную вами схему.)

Если вы хотите загрузить образцы данных в свою схему с помощью MySQL Workbench, импортируйте database.sqlих из projectкаталога, выбрав Data Import/Restore. Затем выберите Импорт из автономного файла, перейдите к database.sqlи выберите имя схемы в целевой схеме по умолчанию. Если вы создали какие-либо таблицы с таким же именем, они будут перезаписаны, а все записи потеряны.

Ускоренный курс объектно-ориентированного программирования

Возможно, вы заметили, что слово «объект» начало проникать в мой словарный запас в предыдущем разделе. PDO является расширением PHP Data Objects и new PDOвозвращает объект PDO. В этом разделе я хотел бы объяснить, что такое объекты.

Возможно, вы сталкивались с термином объектно-ориентированное программирование (ООП) в своих собственных исследованиях PHP или программирования в целом. ООП — это продвинутый стиль программирования, который особенно подходит для создания действительно сложных программ, состоящих из множества частей. Большинство активно используемых сегодня языков программирования поддерживают ООП. Некоторые из них даже требуют, чтобы вы работали в стиле ООП. PHP немного проще в этом отношении и оставляет за разработчиком право решать, писать ли свои скрипты в стиле ООП.

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

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

Создание объекта очень похоже на вызов функции. На самом деле, вы уже видели, как это сделать:

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’,

'mypassword’) ;

Ключевое newслово сообщает PHP, что вы хотите создать новый объект. Затем вы оставляете пробел и указываете имя класса, которое сообщает PHP, какой тип объекта вы хотите создать. Класс — это набор инструкций, которым PHP будет следовать для создания объекта. Вы можете думать о классе как о рецепте, например, о торте, а об объекте как о самом торте, приготовленном по рецепту. Разные классы могут производить разные предметы, так же как разные рецепты могут производить разные блюда.

Точно так же, как PHP поставляется с набором встроенных функций, которые вы можете вызывать, PHP поставляется с библиотекой классов, из которых вы можете создавать объекты. new PDO, следовательно, говорит PHP создать новый PDOобъект, то есть новый объект встроенного PDOкласса.

В PHP объект — это значение, такое же, как строка, число или массив. Вы можете сохранить объект в переменной или передать его функции в качестве аргумента — все то же самое, что вы можете делать с другими значениями PHP. Однако у объектов есть несколько полезных дополнительных функций.

Во-первых, объект во многом похож на массив, поскольку действует как контейнер для других значений. Как мы видели в главе 2, вы можете получить доступ к значению внутри массива, указав его индекс (например, $birthdays['Kevin’]). Когда дело доходит до объектов, концепции схожи, но имена и коды разные. Вместо доступа к значению, хранящемуся в индексе массива, мы говорим, что обращаемся к свойству объекта. Вместо использования квадратных скобок для указания имени свойства, к которому мы хотим получить доступ, мы используем нотацию со стрелкой (→) — например, $myObject→someProperty:

$myObject = new SomeClass () ; // create an object

$myObject→someProperty = 123; // set a property’s value

echo $myObject→someProperty; // get a property’s value

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

Помимо хранения набора свойств и их значений, объекты могут содержать группу функций, предназначенных для предоставления нам более полезных функций. Функция, хранящаяся в объекте, называется методом (одно из самых запутанных названий в мире программирования, если вы спросите меня). Метод — это просто функция внутри класса. Еще больше сбивает с толку то, что когда мы приступаем к написанию собственных классов, методы определяются с помощью functionключевого слова! Даже опытные разработчики часто ошибочно используют функцию и метод как синонимы.

Чтобы вызвать метод, мы снова используем стрелочную нотацию — $myObject→someMethod ():

$myObject = new SomeClass () ; // create an object

$myObject→someMethod () ; // call a method

Как и отдельные функции, методы могут принимать аргументы и возвращать значения.

На данном этапе все это, вероятно, звучит немного сложно и бессмысленно, но поверьте мне: объединение наборов переменных (свойств) и функций (методов) в небольшие пакеты, называемые объектами, определенно приводит к гораздо более аккуратному и легко читаемому коду. задачи — работа с базой данных — лишь одна из них. Однажды вы, возможно, даже захотите разработать собственные классы, которые сможете использовать для создания объектов собственного изобретения.

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

Настройка подключения

До сих пор я показал вам, как создать PDOобъект для установления соединения с вашей базой данных MySQL и как отображать осмысленное сообщение об ошибке, когда что-то идет не так:

<? php

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb’, 'ijdbuser’, 'mypassword’) ;

$output = 'Database connection established.';

} catch (PDOException $e) {

$output = 'Unable to connect to the database server: '. $e→getMessage () ;

}

include __DIR__. '/... /templates/output.html.php’;

Если предположить, что соединение установлено успешно, вам необходимо настроить его перед использованием. Вы можете настроить свое соединение, вызвав некоторые методы вашего нового PDOобъекта.

Перед отправкой запросов в базу данных нам нужно настроить кодировку символов нашего соединения с базой данных. Как я кратко упоминал в главе 2, вы должны использовать текст в кодировке UTF-8 на своих веб-сайтах, чтобы максимизировать диапазон символов, которые пользователи имеют в своем распоряжении при заполнении форм на вашем сайте. По умолчанию, когда PHP подключается к MySQL, он использует более простую кодировку ISO-8859-1 (или Latin-1) вместо UTF-8. Если бы мы оставили все как есть, нам было бы нелегко вставлять китайские, арабские или большинство неанглийских символов.

Даже если вы на 100% уверены, что ваш сайт будут использовать только англоязычные пользователи, существуют и другие проблемы, связанные с неустановленным набором символов. Если ваша веб-страница не настроена на UTF-8, вы столкнетесь с проблемами, когда люди будут вводить определенные символы, такие как фигурные кавычки «, в текстовое поле, потому что они будут отображаться в базе данных как другой символ.

Поэтому теперь нам нужно настроить наш новый PDOобъект на использование кодировки UTF-8.

Мы можем указать PHP использовать UTF-8 при запросе к базе данных, добавив; charset=utf8mb4к строке подключения. В этом нет недостатков, при условии, что ваш PHP-скрипт также отправляется в браузер как UTF-8 (что по умолчанию в последних версиях PHP):

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’,

'mypassword’) ;

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

$pdo→exec ('SET NAMES «utf8»') ;

Это связано с тем, что до версии PHP 5.3.6 параметр charset применялся PHP неправильно. Поскольку это исправлено в любой версии PHP, которую вы фактически собираетесь использовать, установка кодировки как части строки подключения является предпочтительным вариантом.

Таким образом, полный код, который мы используем для подключения к MySQL и последующей настройки этого подключения, показан ниже.

Пример: MySQL-Connect-Complete

<? php

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;

$output = 'Database connection established.';

} catch (PDOException $e) {

$output = 'Unable to connect to the database server: '. $e→getMessage () ;

}

include __DIR__. '/... /templates/output.html.php’;

Запустите этот пример в своем браузере. (Если вы поместили свой код базы данных index.phpвнутри publicкаталога и output.html.phpфайл в templatesкаталоге, URL-адрес страницы будет https: //v.je/.)

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

Успешное подключение

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

Ошибка подключения

Благодаря нашему catchблоку на страницу попало сообщение об ошибке из базы:

catch (PDOException $e) {

$output = 'Unable to connect to the database server: '. $e→getMessage () ;

}

Метод getMessage () возвращает сообщение с описанием возникшей исключительной ситуации. Есть и другие методы, в том числе getFile () и getLine (), для возврата имени файла и номера строки, для которой было выбрано исключение. Вы можете создать очень подробное сообщение об ошибке, подобное этому:

catch (PDOException $e) {

$output = 'Unable to connect to the database server: '. $e→getMessage (). ' in '.

$e→getFile (). ':'. $e→getLine () ;

}

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

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

Установив соединение и выбрав базу данных, вы готовы начать использовать данные, хранящиеся в базе данных.

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

$pdo = null; // disconnect from the database server

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

Отправка запросов SQL с помощью PHP

В главе 3 мы подключились к серверу базы данных MySQL с помощью MySQL Workbench, что позволило нам вводить SQL-запросы (команды) и немедленно просматривать результаты этих запросов. Объект PDOпредлагает аналогичный механизм — метод exec:

$pdo→exec ($query)

Вот $queryстрока, содержащая любой SQL-запрос, который вы хотите выполнить.

Как вы знаете, если есть проблема с выполнением запроса (например, если вы допустили опечатку в своем SQL-запросе), этот метод выдаст a PDOException, чтобы вы могли ее поймать.

Рассмотрим следующий пример, в котором делается попытка создать таблицу шуток, которую мы создали в главе 3.

Пример: MySQL-Создать

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;

$sql = 'CREATE TABLE joke (

id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

joketext TEXT,

jokedate DATE NOT NULL

) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB’;

$pdo→exec ($sql) ;

$output = 'Joke table successfully created.';

}

catch (PDOException $e) {

$output = 'Database error: '. $e→getMessage (). ' in '.

$e→getFile (). ':'. $e→getLine () ;

}

include __DIR__. '/... /templates/output.html.php’;

Обратите внимание, что мы используем ту же try... catchтехнику операторов для обработки возможных ошибок, вызванных запросом. Можно было бы использовать несколько try... catchблоков для отображения разных сообщений об ошибках — один для соединения и один для запроса, — но это может привести к значительному объему дополнительного кода.

Вместо этого я решил использовать один и тот же tryоператор для соединения и запроса. Блок try... catchпрекратит выполнение кода, как только произойдет ошибка, поэтому, если ошибка произойдет во время подключения к базе данных, $pdo→exec ($run) строка никогда не запустится, гарантируя, что при отправке запроса в базу данных соединение должно быть установлено.

Этот подход дает нам немного меньший контроль над отображаемым сообщением об ошибке, но избавляет от ввода try... catchоператора для каждой операции с базой данных. Позже в этой книге мы разобьем их на разные блоки, а пока держите все операции с базой данных в одном tryблоке.

Этот пример также использует getMessageметод для получения подробного сообщения об ошибке с сервера MySQL. На следующем изображении показана ошибка, которая отображается, например, когда таблица шуток уже существует.

Запрос CREATE TABLE не выполняется, поскольку таблица уже существует.

Для запросов DELETE, INSERT, и UPDATE (которые служат для изменения сохраненных данных) execметод возвращает количество строк таблицы (записей), затронутых запросом. Рассмотрим следующую команду SQL, которую мы использовали в главе 3 для установки дат всех шуток, содержащих слово «программист».

Пример: MySQL-обновление

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;

$sql = 'UPDATE joke SET jokedate="2021-04-01"

WHERE joketext LIKE «%programmer%»';

$affectedRows = $pdo→exec ($sql) ;

$output = 'Updated '. $affectedRows.' rows.';

}

catch (PDOException $e) {

$output = 'Database error: '. $e→getMessage (). ' in '.

$e→getFile (). ':'. $e→getLine () ;

}

include __DIR__. '/... /templates/output.html.php’;

Сохранив значение, возвращаемое методом exec, в $affectedRows, мы можем использовать переменную в $outputпеременной для печати в шаблоне.

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

Отображается количество обновленных записей базы данных

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

MySQL дает вам знать, когда вы тратите свое время впустую

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

Обработка SELECTнаборов результатов

Для большинства SQL-запросов этот execметод работает просто отлично. Запрос что-то делает с вашей базой данных, и вы получаете количество затронутых строк (если есть) из возвращаемого значения метода. SELECTзапросы, однако, требуют чего-то более сложного, чем exec. Вы помните, что SELECTзапросы используются для просмотра сохраненных данных в базе данных. Вместо того, чтобы воздействовать только на базу данных, SELECTзапросы имеют результаты — и нам нужен метод для их возврата.

Метод запроса выглядит точно так же exec, как и в том смысле, что он принимает запрос SQL в качестве аргумента для отправки на сервер базы данных. Однако он возвращает PDOStatementобъект, представляющий набор результатов, содержащий список всех строк (записей), возвращенных из запроса:

<? php

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;

$sql = 'SELECT `joketext` FROM `joke`';

$result = $pdo→query ($sql) ;

} catch (PDOException $e) {

$error = 'Unable to connect to the database server: '. $e→getMessage (). ' in '.

$e→getFile (). ':'. $e→getLine () ;

}

При условии, что при обработке запроса не возникло ошибок, этот код сохранит результирующий набор (в виде PDOStatementобъекта) в переменную $result. Этот результирующий набор содержит текст всех шуток, хранящихся в jokeтаблице. Поскольку практического ограничения на количество шуток в базе данных нет, результирующий набор может быть довольно большим.

Я упоминал еще в главе 2, что whileцикл является полезной управляющей структурой, когда нам нужно выполнить цикл, но неизвестно, сколько раз. Мы не можем использовать forцикл, потому что не знаем, сколько записей вернул запрос. Действительно, здесь можно использовать whileцикл для обработки строк результирующего набора по одной за раз:

while ($row = $result→fetch ()) {

⋮ process the row

}

Условия для whileцикла, вероятно, отличаются от условий, к которым вы привыкли, поэтому позвольте мне объяснить, как это работает. Рассмотрим условие как утверждение само по себе:

$row = $result→fetch () ;

Метод fetchобъекта PDOStatementвозвращает следующую строку результирующего набора в виде массива (мы обсуждали массивы в главе 2). Когда в результирующем наборе больше нет строк, вместо этого fetchвозвращается. false (Это один из случаев, когда если попросить объект PDO сделать что-то, что он не может сделать, например fetch, не может вернуть следующую строку, когда в результирующем наборе не осталось строк, не будет выброшено PDOException. Если бы это было так, мы бы нельзя использовать fetchметод в условиях whileцикла, как мы делаем здесь.)

Теперь приведенный выше оператор присваивает значение $rowпеременной, но в то же время оператор в целом принимает то же самое значение. Это то, что позволяет вам использовать оператор в качестве условия в whileцикле. Поскольку whileцикл будет повторяться до тех пор, пока его условие не будет оценено как false, этот цикл будет повторяться столько раз, сколько строк в результирующем наборе, $rowпринимая значение следующей строки каждый раз, когда цикл выполняется. Осталось выяснить, как извлекать значения из $rowпеременной при каждом выполнении цикла.

Строки результирующего набора, возвращаемого функцией fetch, представлены в виде ассоциативных массивов с индексами, названными в честь столбцов таблицы в результирующем наборе. Если $rowэто строка в нашем наборе результатов, $row['joketext’]это значение в joketextстолбце этой строки.

Наша цель в этом коде — сохранить текст всех шуток, чтобы мы могли отобразить их в шаблоне PHP. Лучший способ сделать это — сохранить каждую шутку как новый элемент в массиве $jokes:

while ($row = $result→fetch ()) {

$jokes[] = $row['joketext’];

}

Теперь, когда шутки извлечены из базы данных, мы можем передать их в шаблон PHP jokes.html.php.

Подводя итог, вот код контроллера для этого примера:

<? php

try {

$pdo = new PDO ('mysql: host=mysql; dbname=ijdb; charset=utf8mb4', 'ijdbuser’, 'mypassword’) ;

$sql = 'SELECT `joketext` FROM `joke`';

$result = $pdo→query ($sql) ;

while ($row = $result→fetch ()) {

$jokes[] = $row['joketext’];

}

} catch (PDOException $e) {

$error = 'Unable to connect to the database server: '. $e→getMessage (). ' in '.

$e→getFile (). ':'. $e→getLine () ;

}

include __DIR__. '/... /templates/jokes.html.php’;

Переменная $jokesпредставляет собой массив, в котором хранится список шуток. Если бы вы записали содержимое массива на PHP, это выглядело бы примерно так:

$jokes = [];

$jokes[0] = 'A programmer was found dead in the shower. The instructions read: lather, rinse, repeat.';

$jokes[1] = '! false — it\'s funny because it\'s true’;

$jokes[2] = 'A programmer\'s wife tells him to go to the store and «get a gallon of milk, and if they have eggs, get a dozen.» He returns with 13 gallons of milk.';

Однако данные извлекаются из базы данных, а не вводятся вручную в коде.

Вы заметили, что устанавливаются две разные переменные — $jokesи $error— в зависимости от того, успешно ли tryвыполнен блок.

В jokes.html.phpшаблоне нам нужно отобразить содержимое $jokesмассива или сообщение об ошибке, содержащееся в $errorпеременной.

Чтобы проверить, присвоено ли переменной значение, мы можем использовать issetфункцию, которую мы использовали ранее для проверки отправки формы. Шаблон может включать ifоператор, определяющий, отображать ли ошибку или список шуток:

if (isset ($error)) {

? >

<? php

echo $error;

? >

}

else {

: display the jokes

}

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

Наиболее распространенный способ обработки массива в PHP — использование цикла. Мы уже видели whileпетли и forпетли. Цикл foreachособенно полезен для обработки массивов:

foreach (array as $item) {

⋮ process each $item

}

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

Обычно в foreachшаблонах PHP используется цикл для отображения каждого элемента массива по очереди. Вот как это может выглядеть для нашего $jokesмассива:

<? php

foreach ($jokes as $joke) {

? >

⋮ HTML code to output each $joke

<? php

}

? >

При таком сочетании PHP-кода для описания цикла и HTML-кода для его отображения код выглядит довольно неаккуратно. Из-за этого обычно используется альтернативный способ написания foreachцикла, когда он используется в шаблоне:

foreach (array as $item):

⋮ process each $item

endforeach;

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

<? php foreach ($jokes as $joke):? >

⋮ HTML code to output each $joke

<? php endforeach;? >

То же самое можно сделать с ifоператором, чтобы было удобнее смотреть внутри шаблонов HTML, избегая фигурных скобок:

<? php if (isset ($error)):? >

<? php echo $error;? >

<? php else:? >

: display the jokes

<? php endif;? >

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

Пример: MySQL-ListJokes

<! doctype html>

<? php if (isset ($error)):? >

<? php echo $error;? >

<? php else:? >

<? php foreach ($jokes as $joke):? >

<? php echo htmlspecialchars ($joke, ENT_QUOTES, 'UTF-8')? >

<? php endforeach;? >

<? php endif;? >

Либо$error текст отображается на странице, либо каждая шутка отображается в абзаце (), заключенном в блок-кавычку (

), поскольку мы фактически цитируем автора каждой шутки на этой странице.

Поскольку шутки могут содержать символы, которые могут быть интерпретированы как HTML-код (например, <, >, или &), мы должны htmlspecialcharsубедиться, что они переведены в объекты символов HTML (то есть, <, > и &), чтобы они отображались правильно.

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

Список шуток из базы данных

Помните, как мы использовали whileцикл в нашем контроллере для выборки строк из PDOStatementнабора результатов по одной?

while ($row = $result→fetch ()) {

$jokes[] = $row['joketext’];

}

Оказывается, PDOStatementобъекты спроектированы так, чтобы вести себя точно так же, как массивы, когда вы передаете их в foreachцикл. Поэтому вы можете немного упростить код обработки базы данных, используя foreachцикл вместо whileцикла:

foreach ($result as $row) {

$jokes[] = $row['joketext’];

}

Я буду использовать этот аккуратныйforeach форму в остальной части этой книги.

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

<? php echo $variable;? >

Вместо этого мы можем использовать это:

<?=$variable? >

Это делает то же самое. <?=означает echoи дает вам немного более короткий способ печати переменных. Однако на это есть ограничение: если вы используете<?=, вы можете только печатать. Вы не можете включать ifоператоры, forоператоры и т. д., хотя вы можете использовать конкатенацию, и за ней может следовать вызов функции.

Вот обновленный шаблон, использующий сокращенное эхо.

Пример: MySQL-ListJokes-Shorthand

<! doctype html>

<? php if (isset ($error)):? >

<?=$error? >

<? php else:? >

<? php foreach ($jokes as $joke):? >

<?=htmlspecialchars ($joke, ENT_QUOTES, 'UTF-8')? >

<? php endforeach;? >

<? php endif;? >

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

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

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

Думая о будущем

В примере, который мы только что рассмотрели, мы создали шаблон, jokes.html.phpкоторый содержит весь HTML-код, необходимый для отображения страницы. Однако по мере роста нашего веб-сайта мы будем добавлять больше страниц. Нам, безусловно, нужна страница, на которой люди могли бы добавлять шутки на сайт, а также домашняя страница с вступительным текстом, страница с контактными данными владельца и, по мере роста сайта, возможно, даже страница, на которой люди могут войти на сайт.

Я немного забегаю вперед, но всегда стоит подумать о том, как будет расти проект. Если мы применим подход, который мы только что использовали jokes.html.php, к остальным шаблонам — addjoke.html.php, home.html.php, contact.html.php, login.html.php т. д. — мы получим много повторяющегося кода.

Для каждой страницы веб-сайта потребуется шаблон, который будет выглядеть примерно так:

<! doctype html>

<? php if (isset ($error)):? >

<?=$error? >

<? php else:? >

: do whatever is required for this page: show text,

: show a form, list records from the database, etc.

<? php endif;? >

Для программиста повторение кода — одна из худших вещей, которые вы можете сделать. На самом деле программисты часто ссылаются на DRY., который означает «Не повторяйся». Если вы обнаружите, что повторяете участки кода, почти наверняка есть лучшее решение.

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

<! doctype html>

<? php if (isset ($error)):? >

<?=$error? >

<? php else:? >

: do whatever is required for this page: show text,

: show a form, list jokes, etc.

<? php endif;? >

© IJDB 2021

Мы столкнемся с проблемой в 2022 году! Если шаблоны для всех страниц веб-сайта — например, jokes.html.php addjoke.html.phpи — содержат код в приведенной выше структуре, чтобы обновить год в уведомлении об авторских правах на «2022», вам потребуется открыть каждый из шаблонов и изменить Дата.home.html.phpcontact.html.phplogin.html.php

Мы могли бы быть умнее и динамически считывать дату с часов сервера (echo date ('Y’) ;если вам интересно!), чтобы избежать этой проблемы, но что, если бы мы захотели добавить

IJDB — Internet Joke Database

<? php include 'nav.html.php’;? >

<? php if (isset ($error)):? >

<?=$error? >

<? php else:? >

: do whatever is required for this page: show text,

: show a form, list jokes, etc.

<? php endif;? >

<? php include 'footer.html.php’;? >

Делитесь нашими материалами с друзьями!

 

 

Заказать разработку сайта