Лично мне очень нравится ООП в PHP, и возможности, которые оно дает для создания продукта, достаточно велики. Большинство вакансий PHP — разработчика включает в себя требования к владению ООП в разработке. Вы не сможете написать что-то вразумительное на современных PHP фреймворках, таких как Laravel, Symfony, или на таком популярном, как 1C Bitrix, если не владеете хотя бы основами ООП. В интернете существует множество гайдов, что такое ООП, есть некоторые недурные книги и чуть меньше именно по ООП в PHP. В последнее время я перевернул интернет в поисках примеров для студентов и не нашел практически ничего свеженького, что могло бы быть реально полезно.
Сегодня я хочу немного изменить эту игру, и посмотреть на теорию параллельно с реально работающими примерами. Конечно, идеального кода я сегодня на напишу, оно и понятно — реально большие PHP ООП проекты очень большие, поэтому по возможности я буду приводить компактные примеры. Кроме того, как ООП программу не напиши, в ней всегда можно будет найти недостатки.
Вступление
Несмотря на большое количество гайдов по ООП, в которых авторы не привязываются конкретно ни к какому языку, сама реализация объектов и классов, а главное, синтаксические возможности языков, разительно различаются. Если современное ООП в JavaScript у разработчиков из статических языков вызывает только раздражение (цитата моего знакомого Java-разработчика «из JavaScript мне нравится только TypeScript, но он потом все равно компилируется в JavaScript, поэтому он мне тоже не нравится») и дело не только в динамической типизации, но и развитии ООП, но с другой стороны ничего нелогичного здесь нет — JavaScript это полноценный, но скриптовый язык программирования, который в итоге через развитие интернета и разросся до неимоверных размеров. Но сейчас речь не о JavaScript или Java, сейчас речь о языке, который находится в своем положении где-то посередине — PHP.
Зачем там вообще в PHP ООП
До какого-то ключевого момента (примерно до 5 версии) в PHP не было полноценной поддержки ООП, хотя классы и объекты поддерживались уже с 3 версии. Конечно, в самом PHP заключаются некоторые фатальные недостатки, большинство из которых все еще остаются в языке даже в новейших версиях (полный список, если вы еще их не читали, можно почитать в этой статье ). Но непонятных статей на темы: «ООП в PHP совсем другое, и это хорошо», «не трогайте OOП в PHP, и вас ждет успех» и так далее много, так что давайте теперь перейдем к самому синтаксису в PHP.
Синтаксические возможности PHP в плане ООП
Если читатель хоть немного программировал в PHP, он знает, что этот язык поддерживает сразу две парадигмы: функциональную и объектно-ориентированную. Эта двойственность языка вроде как никого не смущает, но часто приводит к проблемам в выполнении кода, потому как у множества функций есть двойственный интерфейс, к примеру
mysqli
:#процедурный вариант
$connect = mysqli_connect("localhost", "root", "password", "db_example");
# не буду тут приводить обработку ошибок, подключаемся к localhost под root
# с паролем password к базе данных db_example
$res = mysqli_query($connect, "SELECT * from users WHERE id = 1");
$row = mysqli_fetch_assoc($res);
echo $row['msg'];
#тот же самый код, только в объектно-ориентированном виде
$mysqli = new connect("localhost", "root", "password", "db_example");
#тоже тут пропущу обработку ошибок
$res = $mysqli->query("SELECT * from users WHERE id = 1");
$row = $res->fetch_assoc();
echo $row['msg'];
Оба эти запроса к базе данных MySQL одинаково успешно выведут нам колонку сообщений данных пользователя с
id = 1
. Правда, в документации рекомендуется использовать объектно-ориентированный вариант, но зачем же тогда существует процедурный?Это был только один из примеров. Так какие ООП возможности PHP мы разберем сегодня:
- Конструктор, создание экземпляра из класса
- Ключевое слово static
- Наследование
Классы и экземпляры
Изучение объектно-ориентированной парадигмы часто начинается с изучения экземпляров и классов. Учитывая, что концепция объектно-ориентированного программирования сама по себе пришла к нам из реального мира, здесь можно приводить множество аналогий по типу: абстрактный класс — Автомобиль, экземпляр — «лада седан баклажан 2017 года выпуска».
Что касается PHP, то в нем классическое решение относительно классов->экземпляров, это перенести в класс какой-либо повторяющийся элемент, используя его как шаблон с повторяющимся контентом (ну и в общем примерно в таком же виде работает React с его компонентами, если все немного упростить). Очень часто в гайдах по ООП можно встретить пример с классом User, но сегодня я обращусь к примеру класса Post. Допустим у вас какой-то элементарный новостной сайт с простенькой админкой, в которой можно выкладывать свои новости.
class Post
{
#просто предположим, что у нашего поста всего есть изображение
public function __construct($id, $img, $header, $content)
{
$this->img = $img;
$this->header = $header;
$this->content = $content;
}
#здесь я делаю не совсем хорошую вещь, я отдаю представление вместо c html версткой. Но, положим, я не собираюсь развивать этот класс больше
public function return_view()
{
return "
<img src=" . $this->img . " >
<h3>" . $this->header . "</h3>
<p>" . $this->content . "</p>";
}
}
ООП в PHP часто начинают изучать с создания экземпляров объекта и затем назначения их полей, но я не вижу особого смысла в этом, думаю стоит сразу начать писать классы с конструкторами. Меня долго мучила причина, почему методы конструктора и другие называют именно «магическими», но вся магия состоит в том, что их не нужно вызывать вручную и они «магически» вызываются при создании экземпляра.
После того, как мы создали класс, мы можем его использовать, например в цикле:
require $_SERVER['DOCUMENT_ROOT'] . "/project_forum/classes/Card.class.php";
# подтягиваем файл с классами
echo "<section>";
for ($i = 0; $i < count($post_data["header"]); $i++) {
//здесь у нас цикл будет крутиться до той поры, пока есть заголовки
// предполагается что постов без заголовков у нас нет
$post = new Post($post_data[$img][$i], $post_data[$header][$i], $post_data[$content][$i]);
echo "<div class='post_data'>";
echo $post->return_view();
//выводим наше представление
echo "</div>";
}
echo " </section>";
Ключевое слово static
В PHP объявление свойств или методов класса статическими дает возможность обращаться к ним через двойное двоеточие без инициализации экземпляра. В данном примере я создам класс работы с базой данных, который дает возможность быстро подключаться к базе данных. Кроме того, особенность работы классов заключается в том, что вам не нужно реимпортировать классы каждый раз:
Наш файл
Db.class.php
:
class Db
{
public static function getdbconnect()
{
$connect = mysqli_connect("localhost", "root", "", "test_db") or die("Couldn't connect");
$connect->set_charset("utf8");
return $connect;
}
}
После вы сможете использовать этот класс прямо без создания экземпляра следующим образом:
class User
{
public function login()
{
$this->user_name = trim($user_name);
$this->user_password = hash('sha256', $user_password);
$result = Db::getdbconnect()->query("SELECT * FROM users WHERE Name = '$this->user_name'");
}
}
Наследование
Чаще всего наследование подразумевает по собой использование уже имеющегося шаблона класса с целью его расширения. По сути, это можно сравнить с комплектацией автомобилей в реальном мире: если вас интересует только базовая комплектация автомобиля и большего вам и не надо, тогда вы берете автомобиль с минимальным набором функций. Однако если вы много пользуетесь автомобилем и часто попадаете в различные ситуации, вам нужна расширенная функциональность.
Что касается PHP, то здесь есть даже абстрактные классы и функции, в которых описывать работы класса, однако от них даже нельзя создавать экземпляры.
Но сейчас не об этом. Если мы говорим о наследовании, то достаточно часто оно используется для создания набора пользователей, которые посещают ваш сайт. Положим, у вас есть пользователи, которые могут комментировать сообщения в вашем блоге и могут быть админы, которые тоже могут удалить комментарии пользователей. Таким образом, у нас получается следующая структура:
class User
{
# для примера возьму самый минималистичный вариант, которым можно обойтись в таких сервисах
protected $user_email;
protected $user_password;
public function register($user_name, $user_password)
{
// пользователь может зарегистрироваться
}
public function login()
{
#пользователь может зайти на ресурс
# под почтой и паролем, которые он использовал во время регистрации
}
public function logout($user_email, $user_password)
{
# пользователь может покинуть ресурс. Если у нас все написано на сессиях, тогда нам надо ее закончить
}
public function сomment($user_email)
{
#пользователь может оставлять комментарии в любом из постов
#для этого вам скорее всего нужно будет реализовать foreigh ключ
}
}
Дальше, у нас есть дополнительный функционал у класса Admin:
class Admin extends User
{
public function edit()
{
# удаляет комменты со страницы
}
}
Отлично, теперь вы можете создавать два вида пользователей и у каждого будет своя функциональность. Кроме того, никто не мешает вам потом создать, к примеру, суперадминстратора, который сможет добавлять/удалять администраторов, когда ваш сайт увеличится в размерах и придется набирать достаточно большую команду админов.
На этом все. Вот некоторые полезные ссылки:
- Введение в PHP из официальной документации
- Немного на абстрактных классах
- Полезная информация по нововедениям PHP7, если вам не очень понятно, чем 7 версия языка отличается от 5(если вы бродите по различным гайдам по ООП, пытаясь понять, о каком версии PHP идет речь)
Узнать подробнее о курсе
usdglander
Под «функциональной» Вы подразумевали «процедурную»?