Всем привет. Прямо сейчас открыт набор в новую группу курса «Backend-разработчик на PHP». В преддверии старта курса решили поделиться с вами интересной статьей, которую написал наш внештатный автор. Данная статья не имеет отношения к программе курса но, если вы хотите подробнее ознакомиться с курсом и на живом примере увидеть процесс преподавания, приглашаем на бесплатный урок по теме «Сине-зеленый деплой», который состоится 21 апреля.





Лично мне очень нравится ООП в 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()
    {
        # удаляет комменты со страницы
    }
}


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

На этом все. Вот некоторые полезные ссылки:




Узнать подробнее о курсе