Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Фокус внимания давно переместился с PHP на JavaScript и Python. Тем не менее у него выходят новые версии, а тесты производительности говорят о неплохом прогрессе. Насколько актуален PHP сегодня? Под катом — размышления разработчика, который продолжает отдавать ему предпочтение.
Краткая история PHP
PHP разработал Расмус Лердорф в 1994 году. Лердорф создал набор скриптов, которые отслеживали посещения его онлайн-резюме, и назвал их Personal Home Page Tools («инструменты личной домашней страницы»). Со временем название превратилось в PHP Tools. Он пополнял этот набор новыми инструментами, а потом решил переписать их: добавил взаимодействие с базами данных и многое другое. Так набор превратился во фреймворк.
Дальше эти инструменты продолжали эволюционировать в еще более сложные примитивы, а после публикации кода в open source в 1995 году число пользователей стало расти заметно быстрее. Если вас интересуют подробности этой истории, вы можете найти их на официальном сайте PHP.
Последняя версия языка сейчас — PHP 8.0.
А что не так с PHP?
Уже долгие годы этот язык — мишень для критики. Люди справедливо упрекают его за проблемы в работе, особенно старые версии. Лердорф разрабатывал PHP как язык шаблонизации, а не как полнофункциональный язык программирования. Поэтому у него есть ряд недостатков, которые сильно усложняют поддержку и разработку крупных приложений.
Слабая типизация
Лично мне в этом языке не нравится слабая типизация, позволяющая сочетать разные типы и выполнять их неявные преобразования. Рассмотрим такой пример:
echo "1" + 3;
echo 1 + "3";
echo "1" + "3";
Результатом этих операций будет число 4, то есть в контексте оператора сложения язык преобразует числа в строке в целочисленные значения. В некоторых случаях это может оказаться желательным или позволит сэкономить пару строк кода, но чем больше становится проект, тем сложнее будет поддержка.
В поздних версиях языка начали появляться предупреждения о подобных странных и недопустимых операциях, то есть они признаны устаревшими или скоро ими станут.
Отсутствие пространств имен
Поддержка пространств имен была добавлена в PHP в версии 5.3. В более старых проектах приходилось создавать собственные типы пространств имен, в основном при помощи добавления пространств имен к названиям классов и методов. Из-за этого приходилось использовать повсюду абсурдно длинные имена.
В проектах, разработанных в предыдущих версиях, часто можно встретить классы наподобие Payments_Provider_Processor_Provider_SomeExternalServiceProvider
, хотя его можно было бы просто назвать SomeExternalServiceProvider
. В большинстве случаев это приводит к созданию раздутого кода и усложняет его чтение и разбор.
В более поздних версиях языка таких проблем нет.
Противоречивые функции стандартной библиотеки
Не буду говорить, что стандартная библиотека языка плоха, но она могла бы быть и получше. Язык довольно сильно совершенствуется, однако первым версиям стандартной библиотеки, на которые до сих пор ссылаются и поддерживают для обратной совместимости, не хватает целостности. Хоть это и небольшая проблема, она означает, что многие функции стандартной библиотеки имеют разные правила наименования, имена и порядок аргументов. Все это усложняет выявление их значений по умолчанию и поведение.
Вот некоторые примеры противоречивости наименований в строковых методах из документации:
strpos(string $haystack, string $needle, int $offset = 0): int|false
: находит позицию первого вхождения подстроки в строке;strsplit(string $string, int $length = 1): array
: преобразует строку в массив;explode(string $separator, string $string, int $limit = PHP_INT_MAX): array
: разделяет строку по граничной строке.
Три разные функции: одна с префиксом str, вторая с префиксом str
, а третья без префикса. Аргумент $string
является первым для str_split
, но вторым для explode
. Вы можете изучить все строковые методы в документации — этому паттерну следует множество функций, то есть среди них нет особого единообразия.
Суперглобальные переменные
Это больше относится к личным предпочтениям — я ненавижу использовать глобальные переменные, а следовательно, и суперглобальные. Когда находишь какие-то старые самодельные проекты, особенно высока вероятность встретиться с печально известными переменными типа $SERVER
или $REQUEST
. Не поймите меня неправильно: иногда они очень полезны и время от времени их нужно использовать. Однако для безопасного использования этих значений первым делом нужно инкапсулировать их в многократно используемые классы. Если этого не сделать, то взаимодействие со значениями или внесение изменений в крупный проект будет очень сложной задачей, ведь с этими значениями связано множество скрытых зависимостей.
И что хорошего в PHP?
Язык на многих произвел плохое впечатление, но за последние годы он сильно улучшился. Благодаря релизу PHP 7 язык модернизировался, в его основе появилось множество приятных особенностей, повысились скорость работы и удобство пользования.
Type hints
Это один из моих любимых способов модернизации старого кода на PHP: использование необязательных type hints, выполняющих преобразование типов, а также обеспечивающих документацию кода. Рассмотрим простую функцию:
function isValueSomething($value) {}
Если добавить type hints, код станет таким:
function isValueSomething(string $value): bool {}
Просто увидев сигнатуру функции, мы понимаем, что она ожидает строковое значение и вернет булев-результат. Можно добавить, что здесь полезно было бы применить правильное наименование, однако эти type hints гарантируют, что значения будут именно указанных типов и предоставляют IDE возможность автодополнения и статического анализа с предупреждениями и другими полезными вещами.
С версии 7.4 PHP позволяет задавать и типизированные свойства для классов:
class Person {
public string $firstName;
public string $lastName;
public int $age;
public ?string $job;
}
Это означает, что у объектов Person
будут строковые имя и фамилия, возраст в integer и допускающая пустое значение строка для должности. Чем больше классов — тем полезнее эта возможность.
Улучшения синтаксиса
В поздних версиях PHP появилось много синтаксических улучшений:
стрелочные функции:
fn ($x, $y) => $x + $y;
оператор объединения с неопределенным значением:
$value = $array['key'] ?? 'default value';
присваивание с неопределенным значением:
return $cache['key'] ??= computeSomeValue('key');
расширение массивов:
$first = ['a', 'b']; $second = ['c', 'd']; $final= […$first, …$second];
именованные аргументы:
array_fill(start_index: 0, num: 100, value: 50);
разделитель числовых литералов:
299_792_458
Помимо синтаксических он содержит возможности для комплексных улучшений.
Constructor promotion
Взгляните на класс Person
:
class Person {
private string $firstName;
private string $lastName;
protected int $age;
public ?string $job;
public function __construct(
string $firstName,
string $lastName,
int $age,
?string $job
){
$this->firstName = $firstName;
$this->lastName = $lastName;
$this->age = $age;
$this->job = $job;
}
}
Вместо этого избыточно многословного кода PHP 8 поддерживает возможность написания такого кода:
class Person {
public function __construct(
private string $firstName,
private string $lastName,
protected int $age,
public ?string $job
){}
}
Удобно, не так ли?
Nullsafe-оператор
Этот оператор существовал в некоторых других языках наподобие JavaScript, но PHP его не поддерживал. Взгляните на код, который я взял из документации PHP:
<?
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
Именно так писали логику в старых версиях PHP для учета проверок на null. Новый nullsafe-оператор позволяет свести это к такому коду:
$result = $repository?->getUser(5)?->name;
Разве не великолепно?
Объединенные типы
Хоть для меня это и не самая любимая штука, она все равно ценна в случаях, когда уже есть несколько возможных типов без type hints. Объединения позволяют определять в качестве вариантов несколько типов значений. Благодаря объединениям становится работающим вот такой код:
function doSomething(int|string $value): bool|array {}
Обычно наличие нескольких возвращаемых типов сигнализирует о возможности улучшения кода, однако предыдущие версии PHP вообще не позволяли задавать в таких случаях типы, поэтому это все равно усовершенствование.
Производительность
У меня нет четких данных для сопоставления производительности с другими языками, но по сравнению с предыдущими версиями скорость работы кода PHP заметно выросла. В дополнение к рывку, сделанному PHP 7 по сравнению с PHP 5.6, все последующие релизы вносили улучшения, и эта тенденция продолжается. Проведенные Phoronix бенчмарки демонстрируют, что последняя версия PHP 8 в три с лишним раза быстрее PHP 5.6. В посте по ссылке выше есть подробные тесты, так что их стоит изучить.
В дополнение к этим бенчмаркам Kinsta также провела реальные испытания с WordPress. Вот результат для WordPress 5.3.
Точные цифры замеров таковы:
Результаты бенчмарка WordPress 5.3 с PHP 5.6: 97,71 запросов/с
Результаты бенчмарка WordPress 5.3 с PHP 7.0: 256,81 запросов/с
Результаты бенчмарка WordPress 5.3 с PHP 7.1: 256,99 запросов/с
Результаты бенчмарка WordPress 5.3 с PHP 7.2: 273,07 запросов/с
Результаты бенчмарка WordPress 5.3 с PHP 7.3: 305,59 запросов/с
Результаты бенчмарка WordPress 5.3 с PHP 7.4: 313,42 запросов/с
Пока в эти бенчмарки не включен PHP 8, однако видно, что даже версия 7.4 способна обрабатывать в три раза больше запросов по сравнению с 5.6, и это серьезное улучшение.
Вывод
В целом PHP за последние годы значительно улучшили, и с ним стало удобно работать. Я профессионально пишу на Golang, PHP и Python, но больше всего опыта у меня в PHP, поэтому я могу быть пристрастен. Однако, по моему мнению, PHP нашел идеальный баланс между гибкостью и поддерживаемостью.
В нем есть необходимые инструменты для реализации безумных вещей, гибкая система типов, позволяющая постепенно улучшать легаси-код, он достаточно быстр для большинства применений, его продолжают совершенствовать, а еще у него потрясающее сообщество open source.
Тем, кто разочаровался в PHP при работе с более старыми версиями, я рекомендую дать этому языку еще один шанс. Возможно, он вам все равно не понравится, но я считаю, что многие изменят свое мнение, беспристрастно взглянув на новую версию.
P. S. А еще хотел напомнить, что на нашей платформе каждую неделю проходят полтора-два десятка бесплатных мероприятий, связанных с IT и программированием. Не все они так же хардкорны, как доклады на конференциях Олега Бунина и JUG.ru, хотя попадаются темы и для продвинутых. Например, летом мы делали расшифровку доклада сообщества JUGNsk из новосибирской Точки кипения «Project Panama: как сделать Java “ближе к железу”». И это был классный рассказ.