На нашем сайте мы используем cookie для сбора информации технического характера и обрабатываем IP-адрес вашего местоположения. Продолжая использовать этот сайт, вы даете согласие на использование файлов cookies. Здесь вы можете узнать, как мы пользуемся файлами cookies.
Я согласен
логотип upread.ru

Цепочка вызовов в PHP


У PHP не самая лучшая репутация среди языков программирования. Язык развивался постепенно с течением времени, и это видно. Но команда разработчиков PHP последовательно улучшает язык с каждым выпуском. PHP 7, в частности, добился больших успехов как в функциях, так и в производительности.

Другие преимущества языка включают в себя хорошую документацию, отсутствие проблем с хостингом и значительное количество онлайн-ресурсов для обучения. И да, все крупнейшие CMS-платформы (Wordpress, Drupal, Joomla) основаны на PHP.

Итак, для тех из нас, кто использует PHP и хочет писать красивый код, как этого можно достичь?

Скептики могут утверждать, что это невозможно, но я не согласен. Фреймворки вроде Laravel (“фреймворк PHP для веб-мастеров”) уже доказали их ошибочность. Один из паттернов, который часто реализуют Laravel и другие уважаемые фреймворки PHP, - это цепочка методов.

Вот пример того, о чем я говорю, API конструктора запросов Laravel:

$users = DB::table('users')
    ->where('votes', '>', 100)
    ->orWhere('name', 'John')
    ->get();
Этот синтаксис “цепочки” хорош тем, что позволяет разработчикам писать код, который делает одну вещь за раз выразительным и читаемым способом. В приведенном выше примере легко увидеть, что происходит:

  • Выбираем таблицу users.
  • Где голосов больше 100.
  • Или где имя пользователя - Джон.
  • Получаем результаты.
Красиво, правда? Давайте погрузимся в то, как этот паттерн может быть воплощен в жизнь.

Конкретный пример

Чтобы проиллюстрировать, как работает цепочка методов, давайте рассмотрим в качестве примера операции с массивами. PHP имеет много полезных функций массива, таких как array_map, array_filter, usort и т. д. Предположим, что у нас есть массив имен героев мультфильма Симпсоны, и мы хотим применить следующие преобразования:
  • Отфильтруйте все имена, которые не заканчиваются на “Симпсон”.
  • Сопоставьте каждое полное имя только с именем. Например, мы заменили бы “Лиза Симпсон” просто “Лиза”.
  • Отсортируйте наши результаты по алфавиту.
Вот один из способов решения нашей задачи:

$characters = [
    'Maggie Simpson',
    'Edna Krabappel',
    'Marge Simpson',
    'Lisa Simpson',
    'Moe Szyslak',
    'Waylon Smithers',
    'Homer Simpson',
    'Bart Simpson'
];

// Ищем полные имена, которые заканчиваются на Simpson
// Отфильтруем элементы, которые не совпадают

$simpsons = array_filter($characters, function ($character) {
    return preg_match('/^.+\sSimpson$/', $character);
});

// Заменяем "Simpson" пустой строкой для каждого элемента
$simpsons = array_map(function ($character) {
    return str_replace(' Simpson', '', $character);
}, $simpsons);


// Сортировка элементов с помощью PHP "strcasecmp"
usort($simpsons, function ($a, $b) {
return strcasecmp($a, $b);
});
var_dump($simpsons); // ['Bart', 'Homer', 'Lisa', 'Maggie', 'Marge']
Это дает нам правильный результат, но те, у кого острый глаз, могут заметить несколько деталей: array_filter принимает массив и обратный вызов, но array_map принимает обратный вызов и массив. Почему эти функции принимают аргументы в другом порядке? Операции фильтрации и сопоставления сохраняются в переменной $simpsons, но usort обращается к нашему массиву по ссылке. Почему такая непоследовательность?

Это не очень хорошо выглядит, если честно. Для сравнения давайте посмотрим, как эта задача может быть решена в JavaScript:

const simpsons = characters
  .filter(character => character.match(/^.+\sSimpson$/))
  .map(character => character.replace(' Simpson', ''))
  .sort((a, b) => b < a)
console.log(simpsons)
Решение JavaScript гораздо более элегантно. Массивы - это тип объекта в JavaScript, и это позволяет связывать операции с массивами. Кроме того, gриведенный выше JavaScript использует функции стрелок для краткости. PHP еще не имеет функций стрелок, но они могут появиться у нас в ближайшее время!

Цепочка методов на массивах встроена в JavaScript, но мы можем эмулировать это в PHP, написав свой собственный пользовательский класс.

Создание класса с цепочкой вызовов

Назовем это Коллекцией. Экземпляр этого класса может быть создан путем передачи массива в конструктор.

class Collection
{
    private $array;
    public function __construct($array)
    {
        $this->array = $array;
    }
}
$characters = new Collection([
    'Maggie Simpson',
    'Edna Krabappel',
    'Marge Simpson',
    'Lisa Simpson',
    'Moe Szyslak',
    'Waylon Smithers',
    'Homer Simpson',
    'Bart Simpson'
]);
Пока что класс делает не так уж много — он просто сохраняет массив, переданный как частное свойство. Давайте сосредоточимся на добавлении метода публичного фильтра.

public function filter($callback)
{
    $this->array = array_filter($this->array, $callback);
    return $this;
}
Вместо того чтобы передавать как массив, так и функцию обратного вызова в качестве аргументов, как раньше, теперь требуется только функция обратного вызова. После преобразования свойства массива экземпляра возвращается сам экземпляр. Это делает возможным цепочку методов.

Далее давайте добавим методы map и sort:

public function map($callback)
{
    $this->array = array_map($callback, $this->array);
    return $this;
}
public function sort($callback)
{
    usort($this->array, $callback);
    return $this;
}
Теперь наши методы готовы к цепочке, но нам нужен способ вернуть конечный результат в виде массива. Это похоже на то, как конструктор запросов Laravel использует get() для выполнения запроса после того, как он был создан с помощью условных выражений.

public function execute()
{
    return $this->array;
}
И мы можем, наконец, связать методы массива следующим образом!

$characters = new Collection([
    'Maggie Simpson',
    'Edna Krabappel',
    'Marge Simpson',
    'Lisa Simpson',
    'Moe Szyslak',
    'Waylon Smithers',
    'Homer Simpson',
    'Bart Simpson'
]);
$simpsons = $characters
    ->filter(function ($character) {
        return preg_match('/^.+\sSimpson$/', $character);
    })
    ->map(function ($character) {
        return str_replace(' Simpson', '', $character);
    })
    ->sort(function ($a, $b) {
        return strcasecmp($a, $b);
    })
    ->execute();
var_dump($simpsons); // ['Bart', 'Homer', 'Lisa', 'Maggie', 'Marge']
Очень похоже на JavaScript! Проблемы с порядком аргументов и доступом к ссылкам все еще существуют под капотом, но наш класс коллекции обрабатывает это за кулисами. В результате получается код, который намного чище и читабельнее!

Резюме

PHP печально известен своей неряшливостью, но Laravel и другие фреймворки используют цепочки вызовов (методы (методов). Операции с массивами в PHP не цепочечны, как в JavaScript, но базовый класс утилит (например, наш класс коллекций!) может быть использован для эмуляции этого процесса и сохранения некоторых маленьких причуд PHP за занавесом.

Примечание. Использование цепочки без различения только для некоторой субъективной выразительности следует рассматривать как явный антипаттерн. В бизнес-доменах такие шаблоны, как DDD, защищают неизменяемость, которая нарушается цепочкой, поскольку вы возвращаете измененный $this.



Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.



тегизаметки, php





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




Урок 17. Конвертация чисел в строки (Number в String) C#
Программист и системный администратор: два в одном


© upread.ru 2013-2021
При перепечатке активная ссылка на сайт обязательна.
Задать вопрос
письмо
Здравствуйте! Вы можете задать мне любой вопрос. Если не получается отправить сообщение через эту форму, то пишите на почу up777up@yandex.ru
Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.