Мне не нравится то, во что превращается PHP

phix4_u_nh3uzqiuisela6etvyu.png

И я уже знаю, что скажете вы, глядя на заголовок статьи:
 — Кто ты такой? Почему ты позволяешь себе так говорить?

Отвечу сразу, чтобы не было недомолвок:

  • Я профессионально программирую на PHP с 2004 года, то есть вот уже 16 лет на момент написания этой статьи, и продолжаю это делать каждый день
  • Я преподаю программирование, в том числе и на PHP, примерно 10 лет и за это время выпустил в свет несколько тысяч студентов
  • Я всегда был в восторге от каждой новой версии PHP, что выходила со времен от 5.0 до 7.4 и всегда был адептом подхода «пишем на самой свежей версии, тестируем на следующей»


И всё-таки, несмотря на всё сказанное выше, мне не нравится то, во что превращается PHP сейчас и во что он превратится уже скоро, буквально этой осенью.

Почти каждый принятый в PHP 8 RFC вызывает во мне боль и недоумение. И я готов объяснить и защищить свою позицию.

Меняются базовые концепции языка


С чего обычно начинается изучение нового языка? Значения, выражения, переменные и функции. Так и при изучении PHP — это естественный порядок.

Когда мы начинаем проходить функции, я обращаю особое внимание студентов на то, что в PHP контекст функций замкнут, «герметичен». Это очень просто и совершенно логично — нужно лишь четко запомнить, какие имена видит функция: свои аргументы и свои внутренние переменные.

Когда мы доходим до азов ООП и изучаем понятие «метод», мы опираемся на понятие функции, и добавляем еще один пункт к контексту: $this в динамических методах. Меняется ли что-то еще? Нет. Функции по-прежнему замкнуты, их контекст перестает существовать после вызова, снаружи ничто не протекает в функцию, наружу из нее тоже не утекает.

Доходим до анонимных функций — и тут правила игры тоже не меняются. Аргументы? Да. Внутренние переменные? Да. $this? ОК, давайте обойдем этот вопрос, иначе нам придется обсуждать странную вещь под названием «статическая анонимная функция» :)

Далее добавляем понятие «замыкания». С ним тоже всё логично, отдельное ключевое слово, конечный список переменных из контекста создания, базовые принципы не нарушены. А учитывая, что замыкается значение, а не имя, то вообще всё отлично — функции продолжают быть «герметичными». Ничто снаружу не просочится, ничто наружу не выливается.

А знаете, что происходит дальше?

А дальше у нас появляются стрелочные функции. И это ломает всё.

Я вынужден объяснять, что стрелочные функции нарушают усвоенные с таким трудом принципы, поскольку замыкают на себя ВЕСЬ контекст в момент своего создания.

Опс.

А как же принцип «герметичности»? А никак, на него наплевали в угоду упрощения написания, сэкономив 6 символов — теперь у нас «fn» вместо «function».

Плохо. Непоследовательно.

Вы думаете, что это единственный пример? Как бы не так.

Задайте любому нубу вопрос — с какого символа в PHP начинаются имена? Правильно, с символа »$»

  • имена переменных
  • имена свойств объектов
  • имена свойств классов
  • имена аргументов
  • даже «переменные переменные» — тоже »$» !


Логично? Да. Последовательно? Да. Нужно только помнить про константы, которым $ не нужен.

Что теперь у нас появляется? Именованные аргументы: wiki.php.net/rfc/named_params

array_fill(value: 50, num: 100, start_index: 0);


Где «доллар»? Нет.

Это проблема.

Теперь придется запоминать, что в сигнатуре функции «доллар» писать нужно, а при вызове — нет. И это очень серьезная проблема, нарушающая целостную систему языка. Это плохо и непоследовательно.

Ради чего? Да так, просто, потому что кому-то захотелось перенести сахарок из Python в PHP, не подумав. Однако в Python хотя бы используется один и тот же символ »=» для сопоставления имени и значения, что в присваивании, что в именованных аргументах, а у нас теперь их будет два — обычное »=» и »:» в новой конструкции.

Почти каждый обсуждаемый для PHP 8 RFC несет одну и ту же проблему — нарушение ранее сложившейся системы языка. И это плохо. Это ломает мозг и тем, кто пишет на PHP давно, и тем, кто только начинает его изучать.

Смотрите: (wiki.php.net/rfc/match_expression_v2)

echo match (1) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
};


это новые match-expressions. Можете объяснить, почему в них используется символ »=>», а не привычный по switch-case »:»? И я не могу.

Это снова нарушение уже сложившейся системы. Символ »=>» всегда (до чертовых стрелочных функций, снова они!) обозначал разделитель пары «ключ-значение». Теперь он обозначает еще и разделитель списка аргументов и возвращаемого значения в «стрелке», и символ выбора значения в операторе match.

Это плохо. Это очень плохо. Это крайне непоследовательно. Это даже хуже, чем с ключевым словом static, которое используется как минимум в трех принципиально разных значениях.

Нечитаемость на естественном языке


Покажите носителю английского текст

SELECT * 
FROM users
WHERE age>=18 AND name LIKE 'J%'


и он, если его IQ превышает 60, с легкостью объяснит — о чем этот текст и что будет сделано при реализации этого текста, как программы.

Покажите гению, не знакомому с JS, текст

const f = () => 42;


и он не поймет ничего. Константа? f это скобки? Скобки стремятся к числу? Что это?

Я всегда был рад, что PHP далек от того, чтобы принести в жертву читаемость кода в угоду краткости. Я был рад, что он далек от JS, где принцип читаемости кода был отринут в пользу «пиши меньше символов, всё равно этот код читать никто не будет».

Теперь я понял, что в PHP 8 принцип естественной читаемости кода будет нарушен. И, судя по всему, бесповоротно.

Просто посмотрите на эти примеры:

wiki.php.net/rfc/constructor_promotion

class Point {
    public function __construct(
        public float $x = 0.0,
        public float $y = 0.0,
        public float $z = 0.0,
    ) {}
}


Теперь у нас вместо аргументов конструктора — сразу и объявление свойств и задание им значений из аргументов.

Догадайтесь, что после знаков »=»? Начальные значения свойств? Или дефолтные значения аргументов конструктора? Догадаться невозможно. Узнать, прочтя код — тоже. Это плохо. Еще одно место, которое нужно заучивать наизусть.

wiki.php.net/rfc/property_write_visibility

class User {
    public:private int $id;
    public:protected string $name;
}


Публично-приватное свойство? Серьезно? Как из этого кода понять, что речь идет о свойстве доступном на чтение и недоступном на запись?

wiki.php.net/rfc/conditional_break_continue_return

    function divide($dividend, $divisor = null) {
        return if ($divisor === null || $divisor === 0): 0; 
        return $dividend / $divisor;
    } 


Серьезно, это кому-то нужно? Кто-то писал годами на PHP и страдал из-за отсутствия возможности написать «return… if» вместо «if… return»? Нам действительно нужен новый йода-синтаксис для банального if и return?

Слишком много способов сделать одно и тоже


PHP всегда нарушал знаменитый принцип «должен быть один и только один способ…» Но он делал это разумно, понемногу и обоснованно.

Теперь же этот принцип растоптан и уничтожен. Каждый принимаемый RFC словно говорит «а давайте добавим еще один способ написать if, ведь я видел такое в Perl/Python/Ruby/Brainfuck!» — причем других обоснований, кроме как «я видел» в общем-то не приводится.

Что было:

if ($y == 0)
  return 0;

if ($y == 0) {
  return 0;
}

if ($y == 0):
  return 0;
endif;


— целых три способа записи одного и того же. Не очень хорошо, приходится объяснять: зачем их три, почему не стоит писать код без операторных скобок и для чего нужен альтернативный синтаксис.

Но этого мало! Подождите еще немного, и вы увидите:

// революционный Йода-If
return if ($y == 0): 0;


Вот так вызывались функции:

foo(bar($baz));


Скоро вы увидите вот такое:

$baz |> 'bar' |> 'bar'


— гениально, не правда ли? Сразу же понятно, что это вызов функций!

И это я еще ничего не написал про стрелочные функции :)

Больше, больше способов сделать то, что делалось и раньше без всяких проблем:

  • match-expressions
  • оператор »?→»
  • два разных синтаксиса для замыкания
  • цикл + else
  • статические конструкторы
  • объявление свойств в аргументах конструктора (!)


и многое другое, что приведет лишь к тому, что код станет сложнее читать, а язык — сложнее изучать.

Итог


PHP развивается. Это важный процесс, который затрагивает огромное количество людей и влияет на всё сообщество программистов.

Однако начинает складываться ощущение, что развитие заходит куда-то не туда. Я опасаюсь, что принимаемые изменения приведут язык к тому, что программы на нем будут становиться всё более короткими и всё более малочитаемыми, к тому, что он будет предоставлять всё больше и больше возможностей сделать одно и тоже разными способами и для изучения всех этих способов будет требоваться всё больше и больше времени.

Мне не хочется увидеть через год на месте PHP новый Perl. А вам?

© Habrahabr.ru