Comments 51
Довольно сумбурная подборка, но в целом ничего.
И надо бы освоить канонические названия для описанных практик. Приём "Вместо этого кода" в разделе про read-only классы называется Nullsafe оператор. Запись структуры данных в объект называется не ООП, а DTO. "Меньше вложенных уровней" называется "ранний возврат".
Простите, но если вы не делаете сайт-визитку, то брать с собой проблемы скорости php, полной однопоточной синхронности и плохой типизации, которая стреляет фатальным крашем приложения только в рантайме - всё это надо оставить за дверьми прямо на старте проекта. И тогда статьи не было бы.
У бекенда должны быть какие угодно другие проблемы, но не языковые.
У вас устаревшие сведения. Со скоростью у РНР всё хорошо, многопоточность обеспеспечивается менеджером процессов, строгую типизацию уже подвезли.
Я прямо сейчас поддерживаю проект на php8.1
Строгая типизация - это declare(strict_types=1)? Ну вот ровно оно и любит прифаталить приложение. Ещё достаточно часто код фаталится на стыке моего кода и кода сторонних библиотек.
Можно сказать, что линтеры или тесты решают эту проблему. А можно взять компилируемые языки и не иметь эту проблему вообще.
Про синхронность и многопоточность: как красиво и без приседаний отдать ответ юзеру, закрыть хттп соединение, и поделать какую-то работу после отдачи ответа? Например, посчитать аналитику. Без кипы процессов, стороннего хранилища и часто самописной обвязки для постановки задачи и забирания её это не сделать. Ещё и появляется дополнительная точка отказа в виде этого хранилища.
Вместо этого всего хочется какую-нибудь корутинку запустить, или даже просто сделать какой-нибудь response.close() и продолжить мелкие вычисления в том же потоке.
Ну и скорость: у меня сейчас на работе переезд проекта с php на джаву, сокращаем просто только этим действием потребление цпу на 90%. Как уж более честный бенчмарк придумать, как не написание реальных задач на двух языках.
P.s.: писал бекенды на php 9 лет с проектами на 100к rps. За всё это время ни разу не проникся любовью к языку.
Ради бога. Любовь - штука субъективная. Непонятно только, зачем было утруждаться и делиться с нами этими интимными подробностями. "Три дня я гналась за вами, чтобы сказать, как вы мне безразличны" :-D
Не фанат PHP, но есть проекты на PHP и хотелось бы решить вот такую серьёзнейшую проблему. Может подскажете? Во всех наших приложениях на .NET, если клиент отвалился, то обработка запроса моментально прекращается. Что бы там не происходило, если был запрос в БД, то он немедленно прерывается, результата не ждём. Если был запрос к другому сервису по HTTP, то он сразу закрывается. Что угодно, везде действует асинхронная кооперативная отмена всей операции, на всех уровнях.
Но в приложениях на PHP имеем такую проблему, когда клиент отваливается (сам, или прокси рубит), PHP продолжает работать и готовить ответ клиенту, который уже давно ушёл. Это забирает драгоценный рабочий процесс (воркер), и провоцирует лавинообразную деградацию, если запросов в один момент было сделано много, из-за интеграций, которые порой затягиваются, или запросов в БД, которая начала подтупливать. В итоге имеем периодическую полную неработоспособность приложения, восстанавливается когда кубер грохает контейнер из-за не отвечающей пробы php fpm. Конечно, мы масштабируем, но эта мера не устраняет проблему, а только уменьшает и откладывает последствия, которые неизбежно настигают. Слишком увлекаться репликами не можем, решение становится очень дорогостоящим по ТСО. Наши разработчики PHP разводят руками, проблема не решаема.
Есть какое-то решение? У нас Yii2. Спасибо!
Сам факт компиляции - это не особое преимущество. PHP ведь тоже парсится и компилируется в опкод, только перед исполнением.
Падения же при strict_types=1 означает, видимо что данные приходят не того типа. Помимо strict_types=1 надо везде проставлять типы переменных и типы, которые возвращают методы. Сама IDE подскажет, если что не так. Не думаю, что другой язык не упал бы если в строку записать например массив.
Я хотел донести мысль, что плохой PHP код он действительно плохой и точно может есть память и процессы. Верно, и то, что другие языки могу с конкретными задачами справляться лучше, а с другими - хуже.
Но умение правильно писать на PHP, позволяет писать хороший код и спокойно использовать и сокеты и hight load data с очередями. Горизонтально тоже прекрасно масштабируется.
Если совершенствоваться и использовать хорошие практики, то и результат будет хорошим
Что ж вы там за приложения на жаву переносите, что упоминание очередей вас приводит в ужосужос? Очередная банда олимпиадников переписывает вконтакт?
Про синхронность и многопоточность: как красиво и без приседаний отдать ответ юзеру, закрыть хттп соединение, и поделать какую-то работу после отдачи ответа?
https://www.php.net/manual/ru/function.fastcgi-finish-request.php
Во-первых, есть fastcgi_finish_request()
Во-вторых, с менеджером задач лучше, не ясно негодование. Например, нагрузка на сервер более кониролируема. Лучше контроль повторных задач. И т.п.
Конкретно про PHP ничего не скажу - отошел от него примерно в то время, когда для обработки каждого HTTP-вызова запускался интерпретатор соотвествующего скрипта (тяжело, медленно, долго).
Но вот сами принципы - универсальные для любого языка:
- если функция не помещается в 1 экран - она плохо написана
- существуют библиотеки (модули или еще как-то назовут) - всё повторяющееся вынести туда
- давать осмысленные названия функциям и переменным, и желательно понятные, чтобы не спотыкаться на переменных god и mesyats и не пытаться прочитать слово zashishennyy
- и не делать простые вещи сложным способом. Бывает, залезешь в чей-то код, а там - "кручу-верчу, запутать хочу!"
И всё будет хорошо...
Друпалом запахло 😆
Ужас, сочувствую всем кто работает с таким месивом.
Не могу не встаить свои пять копеек:
if (isset($node_ids)) { // при нормальном коде такой ситуацци в которой переменной может не быть, быть не должно
if (is_array($node_ids)) { // типизация и еще раз типизация, переменная всегда должна быть массивом ну на крайняк прийти как null если это может дать дполнительную информацию
foreach ($node_ids as $key => $node_id) {
$node = LoadNode($node_id); // по контексту должна отадвать Node или null
if ($node instanceof Node) { // в таком слачае должна быть проверка !== null
if ($node->hasField('name')) {
if ($node->isEmpty('name')) { // как и в начале не понятно зачем растим вложенность
$result[$key] = $node->get('name')->toString();
}
}
}
}
}
}
return $result;
Ну и код, мог бы выглядеть примерно так, хотя зная больше контекста и эот код можно было бы улучшить
function processNodeNames(array $nodeIds): array
{
$result = [];
foreach ($nodeIds as $key => $nodeId) {
$node = LoadNode($nodeId);
if (!$node?->hasField('name') || !$node->isEmpty('name')) {
continue;
}
$result[$key] = $node->get('name')->toString();
}
return $result;
}
Ниже будет частично псевдокод, но каждый PHP бэкендер может его узнать.
Это просто был пример черезмерных вложений :)
Говоря, про месиво я про все что указано в статье имел ввиду. А пример, просто из того, что не картинкой в статье он самый удобный.
Да, это все из личного опыта, все это и многое другое встречал в реальных коммерческих проектах. А на других языках все пишут всегда по лучшим практикам? Это что, действительно свойственно только PHP?
Насчет всех языков вряд ли сказать кто-то может адекватно, но из моего опыта к сожалени зачастую с PHP так. Но я и с PHP сталкиваюсь чаще. Благо нет друпал и вордпресс сейчас в актуальном опыте.
С Друпалом работаю давно. В нем все хорошо (на мой взгяд). В чем был ваш негативный опыт?
Я с ним до перехода на сифони работал, актульно что там сейчас не скажу
Открыл ради интереса drupal core. Куча статики, на каком нибудь roadRunner или swoole будет течь и баговать. Да и вообще практика плохая. Жонглирование бездонными массивами (привет Yii), передачи всего и вся массивами по ссылке. С хуками не разбирался, но выглядит стремно. Кодстайл свой, который сейчас выглядит как вырви глаз. Это еще если не разбираться что они вообще своими массивами пытались построить.
А, тогда понятно. Все давно не так.
Что именно не так? Я актуальный код на github смотрел
Имел ввиду, что после симфони все поменялась.
Данные передаются в объектах, на более низких уровнях появляются массивы. Друпал, это очень продуманная cms. Программист может поменять кастомным кодом любую часть приложения используя SOLID подход. Используется событийно- ориентированная модель. Все из Core и 3rd patry расширяется плагинами и модулями.
При большой нагрузке тоже справляется. Есть примеры Drupal за баллансировщиком нагрузки на нескольких нодах и базой в ~500gb. Работает.
Ещё мне нравится экосистема Друпал - огромное кол- во бесплатных модулей. И дружное, многочисленные комьюнити. С удовольствием езжу на ежегодный Drupal camp baltic.
Знаю наверняка, что большинство университетов в Финляндии, гос сектор в Эстонии и часть частного бизнеса выбрали Друпал для своих инфосистем.
Я понимаю выш скептицизм, тем более если работали только с древнеми версиями.
Man, я же пишу, что открыл и глянул текущий код и этот код отстой по текущим стандартам. Смотри здесь почему https://habr.com/ru/articles/871426/comments/#comment_27740852
if (!$node instanceof Node)
Это валидный код? Спрашиваю, потому что я вижу в этой записи попытку проверить, является ли булево значение экземпляром класса Node.
Да, поглядел, судя по приоритету операторов (https://www.php.net/manual/ru/language.operators.precedence.php) код валидный, и сперва произойдёт проверка на тип, а уже потом отрицание логического типа. Но как же это, чёрт возьми, сильно путает! Особенно отсутствие пробела между восклицательным знаком и именем переменной.
Не могу удержаться и не испортить себе карму, но самая плохая практика PHP-бекенда - это использование PHP. :)
Как и любой другой инструмент, пхп надо уметь готовить. Мне жаль людей, которым пришлось работать со старющими версиями и древними страшными проектами, но где этого нет? Кого мне не жаль так это тех, кто безвозвратно презирает активно развивающийся инструмент, который оброс тонной тулинга. И всякие ваши асинки через ампхп и реактпхп тоже доступны
Динамические свойства будут вырезаны? Но зачем?
Мне не нравится текущая стандартная практика плодить геттеры и сеттеры в Entity для каждого свойства класса. Особенно если свойства публичные и вместо того, чтобы просто написать:
public string $name;
мы пишем:
private string $name;
public function getName(): string
{
return $this->name;
}
public function setName(string $value): void
{
$this->name = $value;
}
А теперь представьте если свойств в классе десяток и вместо 15 строчек кода приходится писать или генерировать 100+ строк кода с однотипными геттерами и сеттерами. И с выходом php9 это похоже будет единственным способом уже на уровне языка. Мда...
Просто для сравнения, как эта проблема давно решена в C#:
public string Name { get; set; }
public string Desc { get; }
public string Title { get; private set; }
public bool HasName {
get {
return String.IsNullOrEmpty(Name);
};
}
Надеюсь в пхп тоже до чего-то подобного додумаются, потому что плодить однотипные геттеры и сеттеры на сотни строк кода это бред.
Такая практика в doctrine. Например, в Yii, наоборот, почти всё делается через публичные свойства. Если пишите свои классы, то можете реализовывать как душа лежит
Посмотрите на хуки свойств в версии 8.4. Очень похоже на C#, Kotlin и т.д.
Динамические свойства - однозначное зло. Это как использовать ассоциативный массив. Кто знает что можно ожидать в этих сущностях
Под динамичными свойствами имелось ввиду не то, про что вы подумали, а про обьявление свойства в экземпляре класса, которого нет в самом классе.
Я не могу присвоить значение свойству name если его нет в самом классе.
В статье у автора в классе есть только свойство title. При этом код пытаеться присвоить значение свойству name, но его нет.
Соотв вопрос почему депрекейтед? сам по себе отпадает.
При этом код пытаеться присвоить значение свойству name, но его нет.
А, точно. Не заметил этого и поэтому и написал комментарий. Да, тогда вопрос отпадает, потому что подобный функционал (динамические свойства) никогда на практике особо не использовал.
Соотв вопрос почему депрекейтед? сам по себе отпадает.
Да
Динамические свойства - это те, которые не объявлены в классе. Никто вам на запрещает объявить публичное свойство без геттеров/сеттеров
Так, погодите, они реально вырезают __get
/__set
magic-методы?
Для меня PHP кончился на версии 5.3, после которой они вырезали возможность именно в момент вызова решать, будет ли аргумент передаваться по значению (под капотом передается указатель на копию ZVAL), или по ссылке (под капотом передаётся указатель на исходную ZVAL).
Идиот, который ввел это новшество, очевидно забыл, что у классов может быть магический метод для обработки динамических методов, а у таких методов априори не может быть декларации, где можно было бы поставить @ перед аргументом. Таким образом этот идиот сделал невозможным передачу по ссылке при вызове динамических методов.
Про отмену __get
/__set
никто не говорит. Когда что-либо становится deprecated обычно полезно прочитать RFC, а не спекулировать и уж точно не паниковать. Для больших изменений всегда доступно описание:
https://wiki.php.net/rfc/deprecate_dynamic_properties
В нем изложены мотивация, последствия и анализ того, почему разрыв обратной совместимости был признан допустимым. Также есть информация о том, как можно исправить существующий код, чтобы снова сделать его рабочим, но да, в сторонних библиотеках это сделать затруднительно.
Если коротко, то new stdClass
это не касается вообще, + добавили директиву AllowDynamicProperties.#[AllowDynamicProperties]
судя по всему, апокалипсис пока отменяется ;)
class Test {}
// No deprecation warning
$obj = new Test;
$obj->bar = 1;
Ключевое здесь то, что у текущих разработчиков PHP беда с терминологией. По приведённой ссылке:
It should be noted that properties accessed through __get()/__set() are not considered as “dynamic properties”.
Блин, я не знаю, что у них на уме. Как раз именно это и заслуживает называться динамическими свойствами. А то, что они называют динамическими свойствами, стоило бы называть как угодно еще: implicit-свойствами, undeclared-свойствами и т.п.
Если более конкретных хуков не существует
Если у вас есть только одно место вызова этого хука module_name_form_after, то наверное лучше будет сделать свой EventSubscriber.
Не будет больше кода с проверкам:
if ($form['#id'] === 'some_name_1') {}
if ($form['#id'] === 'some_name_2') {}
if ($form['#id'] === 'some_name_3') {}
Все сведется к коду вида:
function module_name_form_after($form) {
$subscriber->dispatch(new ModuleNameFormEventAfter(id: $form['id']));
}
// и уже внутри сабскрайбера будет что то типа
public function dispatch(Event $event) {
foreach ($this->subscribers as $subscriber) {
if ($subscriber->supports($event)) {
$subscriber->handle($event);
}
}
}
С удоволсьтвием прочитал, но до того, чтоб написать что-то серьёзное, кроме CRUD'а, мне ещё далеко. PHP начал учить с полгода назад. В данный момент работаю над своим блогом в саблайме с нуля без фреймворков, хочу понять механизм работы. Пиху начал учить только потому что её все хаят. До сих пор не понял что плохого в этом ЯП
Плохие практики в PHP-бэкэнде: примеры и советы