Достоинства и фатальные недостатки типизации в php +21

Язык php часто ругают, обычно необоснованно. Особенно удивляет, что javascript ругают меньше. Зачастую это делают люди, которые писали на нем 10+ лет назад, когда язык был действительно чертовски плох, да и разработчики в те времена не задумывались над качеством кода. Посмотрите хотя бы на код wordpress, который до сих пор вызывает шок.


Ругают необоснованно, но проблемы у языка, конечно же, есть, и они серьёзные. Разуметеся, если сравнить последние релизы php7 (с нормальным ООП и строгим тайпхинтингом) и php4, то разница будет колоссальная. Однако и в последних версиях языка не всё гладко, и до java/c# пока что очень далеко. Более того, берусь утверждать, что будущее php тоже довольно сомнительно (с точки зрения типов).


Другими словами, давайте рассмотрим предметно, что хорошо и что плохо в php с точки зрения типизации.


Тайп хинтинги


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


Немного отвлечемся и посмотрим кусок кода на javascript:


function filterUsersByAge(users, age) {
    // тут какой-то код
}

Что мы можем сказать об этой функции? Она берет каких-то пользователей и фильтрует их по возрасту. Но этого мало, потому что сразу возникают вопросы:


Что такое users? Массив? Или какой-то хитрый объект-коллекция?
Возраст задан как целое число или может быть дробным?
Может ли возраст быть null?
Возвращает ли эта фунция значение или же меняет переданный массив users?


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


Как известно, программист тратит больше времени на чтение и понимание кода, чем на написание нового, поэтому отстутствие подсказок в коде является затармаживающим фактором.


Для сравнения код на последних версиях php:


function filterUsersByAge(array $users, ?int $age) : array {
   // ...
} 

Тут мы видим, что на входе массив пользователей, возраст может быть null, возвращается также массив. Гораздо яснее, не так ли? Если же в нужных местах указать declare(strict_types=1), то при попытке пихнуть дробное число в качестве возраста, мы получим ошибку.


Вроде всё супер, но есть нюансы.


Нет дженериков


Мы смотрим на эту php-функцию filterUsersByAge и сходу не понимаем, массив чего нам пришел. Что именно за array? В java можно было бы написать List<User>, и мы бы понимали, что к нам пришел список объектов User. Или Set<User>, и мы бы сразу видели, что это список без повторов, т.е. только разные объекты. (Вообще, array в php — это странноватая смесь массива и HashMap, но это тема для отдельной статьи)


Нет уточнений для типа callable.


Вот пример функции:


function reduce ( array $array, callable $callback )

Что за функция идет вторым аргументом? Что в ней должно быть?


Только в комментариях к коду мы можем понять, что там должно быть, к примеру, два аргумента. Кстати, есть четыре вида лжи: ложь, наглая ложь, статистика и комментарии к коду.


В некоторых языках, например в TypeScript, можно прописать прямо в объявлении функции:


function fn(action: (a: string, b: number) => void)

Т.е. здесь в качестве аргумента action должна быть функция с двумя аргументами (строка и число), которая ничего не возрващает. Всё максимально явно, IDE и компилятор сразу скажут, если аргумент был какой-то не такой


Странности тайпхинтинга и типа возврата в связке с наследованием


<?php

interface Entity {}

class User implements Entity {}

abstract class Repository {
    abstract public function findById(): Entity;
}

class UserRepository extends Repository {
    function findById(): User {
        return new User();
    }
}

Здесь получаем ошибку, что findById не совместим с findById из абстрактного класса.


Такой же пример в java нормально компилируется:


interface Entity {}

class User implements Entity {};

abstract class Repository {
    abstract public Entity findById();
}

class UserRepository extends Repository {
    public User findById(){
        return new User();
    }
}

в TypeScript тоже можно:


interface Entity {}

class User implements Entity {}

abstract class Repository {
    public abstract findById(): Entity;
}

class UserRepository extends Repository {
    public findById(): User{
        return new User();
    }
}

На это дело время от времени появляются баг репорты, возможно будет исправлено когда-нибудь:



Фатальная проблема


Самая большая проблема в том, что php проверяет типы во время выполнения, а не во время компиляции. Потому что, не смотря на strict_types и type hintings, это ВНЕЗАПНО не строго типизированный язык


Отсюда следует два вывода:


1) Чем больше проверок в рантайме, тем больше тормозов. Поэтому слишком сложные проверки навряд ли вообще когда-нибудь появятся. Многослойные дженерики и callable с callable аргументами просто положат рантайм. Также будут тормозить рантайм введение типов для членов класса и в других местах.


2) Ошибки выявляются только во время запуска. Т.е. всегда будут ситуации, когда в какой-то хитрой ситуации пользователь сделает что-то не предусмотренное тестами, и всё повалится


Вместо выводов


Хотя (с точки зрения типов и ООП) на мой взгляд php на голову выше, чем javascript, и подходит для написания сложных программ, но при этом, конечно, не дотягивает до java/c#/typescript, и навряд ли когда-нибудь дотянется (см "Фатальная проблема"). Повторюсь, не дотянется именно с точки зрения системы типов, в остальных вещах возможны предпочтения в ту или иную сторону.


Поэтому в по-настоящему сложных приложениях надо обязательно всё обкладывать тестами. Также, возможно, что phpdoc добавит поддержку сложных callable с параметрами, и IDE научатся их понимать.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (90):

  1. AlexLeonov
    /#10622820 / -1

    проблемы у языка, конечно же, есть, и они серьёзные

    до java/c# пока что очень далеко

    ВНЕЗАПНО не строго типизированный язык

    конечно, не дотягивает до java/c#/typescript


    Антон, так пишите на Java или C#, кто же вам запрещает-то?

    Статья — прекрасный образец самой настоящей демагогии.

    Берем язык А, в котором нет фичи b, поскольку она не нужна или реализована иначе.
    Берем язык B, в котором фича b в наличии
    Намеренно опускаем момент, что это принципиально разные языки и сравниваем килограмм с километром, делая вывод, что «А не дотягивает до B»

    Я никогда не понимал — как можно быть «руководителем отдела разработки» на языке, который тебе так не нравится? Расскажите, Антон, пожалуйста, как вы планерки проводите?

    Примерно так: «Парни, начинается новая неделя и нам всем снова предстоит писать код на этом ненавистном PHP, будь бы он неладен… Когда же это кончится уже?»

    • varanio
      /#10622822 / -1

      Я же написал: «Повторюсь, не дотянется именно с точки зрения системы типов, в остальных вещах возможны предпочтения в ту или иную сторону.»

      • AlexLeonov
        /#10622828

        А нужно дотягиваться?
        Зачем вообще принципиально слаботипизированному языку с динамической «рантайм» системой типов дотягиваться до языков со статической типизацией?
        Кто такую цель поставил для PHP? Вы?

        • varanio
          /#10622836 / -1

          Ну php же явно идет в эту сторону. Type hinting-и усложняются с каждой версией. strict_types и т.д. Но в то же время у этого процесса есть ограничения, потолок возможностей, что я и показал в этой статье. Просто это не всем очевидно.

          • AlexLeonov
            /#10622844

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

            • sam002
              /#10622988

              Цель-то достаточно очевидная у нынешних владельцев zend — отжимать рынок у java/c#/typescript. Это предопределяет кучу стратегических решений по развитию языка. Так что не зарекайтесь от «строгой типизации».
              Вполне допустимо повторение ошибок, подобных php6. Тогда всё исходило из идеи что в java же utf-16, значит и в php надо.
              К большому сожалению само ядро php имеет относительно слабое комьюнити и ограниченные ресурсы. Плохая сторона — зависимость от zend с плохо продуманной стратегией, хорошая — очень тщательно выбираемые изменения не ломающие BC каждые пять лет и дающие максимальный профит тактически.

        • varanio
          /#10622838

          Не надо так агриться на статью, я не хотел никого обижать )

          • AlexLeonov
            /#10622846

            Я бы не «агрился» так, если бы не очевидный вред от таких статей.

            Вы своё собственное несовпадение «идеального языка» и реального PHP почему-то считаете проблемой последнего. Да еще и называете «фатальным недостатком»

            Если бы не подобная тональность статьи — никаких отрицательных эмоций в вашу сторону не было бы.

            • varanio
              /#10622874 / +1

              Какая тональность? В какой фразе? Система типов хуже, чем у java — ну извините, это медицинский факт, а не тональность.
              Кроме этого факта я ничего плохого про php не сказал. Более того, я на нем сам пишу.
              О проблемах надо говорить, а не замалчивать. Тогда возможно появятся какие-то решения. К примеру для javascript появился typescript. Чем не решение? И строгие типы, и возможность использоваь либы на ванильном Js

              • AlexLeonov
                /#10622898

                Можно я чуть-чуть «сагрюсь» еще? Ну совсем немножко?

                Система типов хуже, чем у java — ну извините, это медицинский факт, а не тональность.

                Вот здесь у вас фатальная ошибка и недостаток. У вас, а не в PHP. Ошибка в том, что вы пытаетесь личное субъективное выдать за объективное сравнение несравнимых величин. Пресловутые «килограмм» с «километром»

                Система типов в PHP не «хуже» или «лучше» — она другая. Она построена на иных принципах и решает иные задачи, нежели в Java.

                Не понимаете? А как вы тогда руководите разработкой?
                Впрочем, этот вопрос я вам уже задавал, ответа не последовало.

                • varanio
                  /#10622914

                  Не могли бы вы мне объяснить, какие именно задачи решает система типов в php? Чтобы я мог лучше руководить разработкой? А может и вообще стёр вредную статью?

                  • AlexLeonov
                    /#10622936

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

                    Но если совсем коротко, то успешно решаемые задачи в PHP стоит свести к нескольким важнейшим пунктам:
                    — автоматический динамический кастинг скалярных типов
                    — реализация понятия «практической эквивалентности»
                    — стандартизация всех i/o хэндлеров в едином типе resource
                    — решение всех проблем, которые возникают в других языках с динамическими массивами
                    — реализация класс-ориентированного подхода
                    — контроль типов времени исполнения
                    — контроль типов времени компиляции

                    Постарался расположить по степени важности

                    • varanio
                      /#10623096

                      — автоматический динамический кастинг скалярных типов

                      Да, когда-то целью php это было. Но ввели declare(strict_types=1). Это для чего?
                      По-моему чтобы избавиться от неявного, разве нет?

                      — реализация понятия «практической эквивалентности»

                      Не могли бы вы пояснить?

                      — решение всех проблем, которые возникают в других языках с динамическими массивами

                      Каких проблем?

                      — контроль типов времени компиляции

                      в php почти нет этого

                      • AlexLeonov
                        /#10623102

                        Да, когда-то целью php это было. Но ввели declare(strict_types=1). Это для чего?
                        По-моему чтобы избавиться от неявного, разве нет?

                        Нет.
                        Эта директива компилятора просто запрещает кастинг скаляров в рантайме в строго определенных случаях (ровно в двух), заменяя кастинг на строгий контроль типа с выбросом исключения. Всё.

                        Причем тут ваши фантазии о «явном» и «неявном» — я не знаю.

                        И не надо, пожалуйста, рассказывать о «целях PHP». В вашем исполнении это звучит как минимум странно.

                        • varanio
                          /#10623186

                          Причем тут ваши фантазии о «явном» и «неявном» — я не знаю.

                          Я про неявное приведение типов.

                          function myFunc(int $val) {
                             print $val;
                          }
                          
                          myFunc("1.99999999");


                          выдаст единицу. Молча, неявно преобразовав тип.

                          с decrlare(strict_types=1) придется явно написать

                          myFunc((int) "1.99999999");
                          


                          иначе будет выдана ошибка. Вот про это явное указание (int) я и говорил.

                          И не надо, пожалуйста, рассказывать о «целях PHP». В вашем исполнении это звучит как минимум странно.


                          Почему в моем исполнении странно? Может именно в вашем странно?
                          Я просто высказываю свое мнение, наблюдая за changelog ом языка.

                          • AlexLeonov
                            /#10623200

                            выдаст единицу. Молча, неявно преобразовав тип.


                            Как же «неявно», если вы сами написали «int»? Что тут неявного?

                            • poxvuibr
                              /#10624496 / +1

                              Как же «неявно», если вы сами написали «int»? Что тут неявного?

                              Ну вот так, неявно. Если функция принимает int, ей передают строку и эта строка автоматом преобразуется в int — это назвается неявным преобразованием.


                              Явное преобразование — это когда программст, когда передаёт параметр сам пишет, что его надо преобразовать в int.

                              • varanio
                                /#10624500

                                Вот да, достаточно просто погуглить по фразе «неявное преобразование типов»

                      • AlexLeonov
                        /#10623106

                        Практическая эквивалентность это
                        1 == '1' == true
                        0 == '0' == '' == false == []

                        что с трудом достигается в строго типизированных языках с контролем типов времени компиляции

                        На практике же такая эквивалентность необходима просто в силу того, что так устроен HTTP и CLI.

                        • varanio
                          /#10623194

                          Да, это было одной из целей языка изначально. Однако это сомнительный плюс, если честно. Столько нюансов с этими магическими штуками, вы и сами знаете наверняка

                          • AlexLeonov
                            /#10623202

                            Однако это сомнительный плюс, если честно.

                            Не пишите на PHP. Он вам не нравится, и, как результат — вы его не понимаете.

                            • asm0dey
                              /#10623422 / +4

                              Последнее утверждение крайне спорно. Я 9 лет писал на джаве, я думаю что понимаю её глубже чем многие. Но я прям далеко не фанат. И как только появилась внятная альтернатива в лице котлина — с радостью перешёл на него. Не нравится ? не понимаю.

                              • AlexLeonov
                                /#10623650 / -2

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

                                Изучение же, приводя к пониманию, затем убирает это странное «не нравится». Ну или человек уходит на другую платформу.

                                • OlegOleg1980
                                  /#10624026

                                  Позвольте вас дополнить.
                                  В целом обсуждение сводится к той ситуации, когда есть два напильника — плоский и круглый, и, автор, взяв в руки круглый напильник, рассказывает нам, как же плохо им выравнивать плоскую поверхность — то он соскальзывает, то канавки в поверхности делает, то вообще очень медленно точит! И, говорит, неплохо бы этому круглому напильнику грани сделать — тогда и с плоскими поверхностями намного удобнее будет работать! У автора то уже есть один удобный плоский напильник, тот то хорошо плоскости обрабатывает, значит надо к этому стремиться, сделать все напильники плоскими. Автор просто редко занимается округлыми поверхностями, но в те редкие моменты, когда это все же происходит, он тихим шепотом сыплет проклятия тому, кто ему подсунул эти неплоские поверхности, возможно даже изредка подумывая о круглом напильнике…

                                  • varanio
                                    /#10624040 / +1

                                    Если уж на то пошло, то php (personal home page) изначально предназначался для простых сайтиков

                                    Но время шло, сайтики превращались в фейсбуки, и старый напильник перестал подходить

                                    Множество гигантских систем написны на php и страдают от недостатчной строгости язык

                                    Посмотрите современные фреймворки.

                                    • OlegOleg1980
                                      /#10624068

                                      Вот вы сами и ответили на свои же, скажем так, «вопросы».
                                      Время шло, «фейсбуки превращались», «напильники перестали подходить», но вместо того, чтобы брать для работы другие, более подходящие напильники (треугольные, квадратные, и другие разные инструменты), мы продолжаем мужественно стачивать грани у круглого напильника, превращая его во всё более плоский…

                                      • varanio
                                        /#10624640

                                        Блин.
                                        Расскажите мне, какие аргументы приводить начальству, чтобы уговорить их потратить пару человеколет на то, чтобы переписать всё на другой язык?

                                        Напильники, фигильники. Многие компании в заложниках у тех технологий, на которых они построены. И зачастую это навсегда.

                                        Т.е. выбора-то нет на самом деле.

                                        • VolCh
                                          /#10625358

                                          Потратить на портирование пару человеко-лет ещё можно как-то убедить, но вот убедить, что на несколько месяцев развитие текущего проекта остановится точно нельзя.

                                      • VolCh
                                        /#10625354

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

                            • varanio
                              /#10623490 / +3

                              Чего я там не понимаю? На php пишу 100500 лет.

                              Вообще, нравится/не нравится — дело десятое. Мне важно, где он применим, где нет, какие нюансы есть, куда развивается язык.

                              • AlexLeonov
                                /#10623752 / -3

                                Не понимаете главного — зачем этот язык. От непонимания идет нежелание его изучать. От нежелания изучать — недостаток знания и понимания.

                                Замкнутый круг.

                                Попробуйте ответить на самый простой вопрос для самых-самых начинающих — а зачем вообще в PHP
                                empty('0') === true;
                                ?
                                Какой практический смысл в таком?

                                С точки зрения любого не-PHP программера ересь же. Непустая строка пустая?

                                • varanio
                                  /#10623976

                                  empty($x) — это фактически сокращенная запись ( isset($x) && $x == false)
                                  А '0' в php — это false

                                  Это не то, чтобы ересь, просто нейминг немного путает людей

                                  • WebSpider
                                    /#10624096

                                    empty($x) — это фактически сокращенная запись ( isset($x) && $x == false)

                                    Да ну? то есть для несуществующих переменных (isset($x) = false) empty должна вернуть false?

                                    • varanio
                                      /#10624104

                                      Да, ошибся. Я имел в виду !isset($x) || $x == false

                                      • AlexLeonov
                                        /#10624746

                                        Вы не ответили на вопрос «зачем оно так»

                                        • varanio
                                          /#10625620

                                          ну, чтобы меньше кода писать. Вместо двух условий одно

                        • 0xd34df00d
                          /#10623922 / +2

                          1 == '1' == true

                          Idris> the Int $ cast "1"
                          1 : Int
                          


                          0 == '0' == '' == false == []

                          Но… Зачем? Как HTTP и CLI требуют этого?

                          • varanio
                            /#10623996 / +1

                            AlexLeonov намекает, что в http-протоколе есть только строки, других типов нет. И удобно брать строки из $_GET и тд и сразу начинать сравнивать и складывать с чем нибудь, не приводя вручную к типу

                            На самом дделе этот аргумент не совсем верен.

                            • tehSLy
                              /#10624004

                              Учитывая, что PHP работает далеко не только с $_GET, $_POST и иже с ними, не уверен в корректности этого аргумента как такового. Но, надо же привести что-то в защиту ?\_(?)_/?

                              • varanio
                                /#10624020

                                В сложных приложениях все равно приходится тщательно валидировать и санитизировать входные данные.
                                Одна ручная операция по преобразованию типа роли бы не сыграла.

                                В сложных приложениях получение данных из GET и POST — вершина айсберга, причем самая простая. Дальше идет бизнес логика и т.д., где цена ошибки слишком высока, и хочется не надеяться слепо, что php как-то сам закастит данные по своим магическим правилам. Делать все явно — вот, что было бы хорошо. Понятный с одного взгляда неизменный тип переменной -это было бы супер. Но увы

                                • tehSLy
                                  /#10624056

                                  И я о том, но ведь «мы просто используем инструмент не по назначению», как тут многие выражаются(С удовольствие использовал бы подходящий инструмент, да вот поди объясни бизнесу, что у них кодовая база не на том написана). Я с Вами во всем солидарен. Времена, когда PHP заходил с его слабой динамической типизацией канули в лету, теперь скорее источник ошибок, нежели полезная фича.

                              • VolCh
                                /#10624138

                                mysql тоже строки возвращает как результаты выборки, независимо от типа в базе. Так что основная причина подобных приведения — именно упрощение разработки в среде, где почтм всё представлено в строках технически, но имеет семантику не только строк, но и чисел, булевых и т. д.

                              • dekameron
                                /#10625326

                                В защиту: для проверки с учётом типа есть ===

                            • 0xd34df00d
                              /#10624194

                              А вручную ничего не надо приводить. Ну вот как тут, в quick start-примере. quiet имеет тип Bool, enthusiasm — вообще Int, и вместе с тем никаких ручных приведений.

                      • AlexLeonov
                        /#10623122

                        Каких проблем?

                        — создание в рантайме массива
                        — динамическое добавление и удаление элементов массива
                        — возможность иметь неплотное и немонотонное множество ключей
                        — упорядоченность массива, отдельная от упорядоченности ключей
                        — нетипизированность массива

                        Поверьте, не каждый язык это всё умеет.

                        • varanio
                          /#10623198

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

                        • JTG
                          /#10625544

                          Это всё классно, но

                          $requiredParams = array_slice($params, 0, 9);
                          $baseUrl = Router::createLink('analys', 'index', $requiredParams);

                          Видите ошибку? А она есть.

                    • 0xd34df00d
                      /#10623910

                      Должен сразу сказать, что последний раз я трогал PHP, когда он был четвёртой версии, и с тех пор моя жизнь круто поменялась, но у меня к вам есть пара вопросов.

                      автоматический динамический кастинг скалярных типов

                      А это хорошо?

                      решение всех проблем, которые возникают в других языках с динамическими массивами

                      Включая статические гарантии не выхода за границы? Или как проблемы-то решаются?

                      • AlexLeonov
                        /#10624946

                        Включая статические гарантии не выхода за границы? Или как проблемы-то решаются?


                        Покажите мне практический пример «выхода за границы» для массивов в PHP — и тогда обсудим вопрос про гарантии.

                        На самом же деле постановка вопроса не имеет смысла, поскольку «массивы» в PHP это не массивы с фиксированным размером элемента и смещением, а что-то вроде hashmap (тоже неверно, но ближе к истине)

                        • 0xd34df00d
                          /#10625830

                          Я сразу написал, что в PHP не особо разбираюсь. Собственно, именно поэтому мне и интересно, как он решает эти проблемы. Если через возврат нулевых-пустых-неопределенных значений, то это, в принципе, решение. Если через утверждение, что «на практике таких проблем не возникает», то это, в принципе, тоже решение, да.

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

                          • AlexLeonov
                            /#10625836

                            Запрос элемента с несуществующим ключом приведет к выбросу предупреждения (можно проигнорировать) и возврату null значения.
                            Но поскольку в языке есть явная конструкция проверки существования элемента — проблема не имеет смысла.

                            • 0xd34df00d
                              /#10625840

                              Хорошее решение, спасибо!


                              А ради интереса, есть языки, где нет проверки существования элемента в массиве или индекса в массиве?

                              • VolCh
                                /#10627058

                                C?

                                • 0xd34df00d
                                  /#10627768

                                  Ну, если не сохранять размер выделенного блока памяти, то да.

                • hack3p
                  /#10622956

                  Если PHP претендует на OOP с SOLID, тогда почему они немогут реализовать нормальное ковариантное наследование как в других языках. Вместо этого зачем-то делают костыли в виде нарушения принципа Лисков. Можно еще ознакомится с этим PR и отношением PHP сообщеситва к таким изменениям.

                  • AlexLeonov
                    /#10622968

                    Можно ссылочку на документацию, где указано, что цель развития PHP — соблюдение принципов SOLID?

                    И не кажется ли вам, что соблюдать эти принципы должен программист, как разумное существо, а не язык, как набор инструментов?

                    Это всё равно что сказать, что нож нарушает уголовный кодекс.

                  • greatkir
                    /#10622998 / +2

                    Прошу прощения, но искренне не понимаю, где в вашем примере нарушение принцпа Лисков.

                    Если бы речь шла о сужении типа, то да, типы в наследуемых классах не соответствовали бы базовому типу, и это было бы нарушением принципа Лисков (кстати, именно запретом такого поведения почему-то не доволен автор статьи в подзаголовки «Странности тайпхинтинга...»).

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

                    • 0xd34df00d
                      /#10623944 / +1

                      Если бы речь шла о сужении типа, то да, типы в наследуемых классах не соответствовали бы базовому типу, и это было бы нарушением принципа Лисков

                      Нет. Наследование ковариантно по возвращаемым типам и контравариантно по принимаемым.


                      Ковариантность: если у вас в базовом классе Base есть метод, возвращающий некоторый BaseRet, то вы по факту говорите, что объект каждого класса Derived, унаследованного от Base, возвращает из этого метода BaseRet. Если вы в конкретном Derived возвращаете DerivedRet, унаследованный от BaseRet, то вы всё равно возвращаете BaseRet (потому что DerivedRet is-a BaseRet), и все действия, которые клиент вашего метода может сделать с возвращённым ему (по контракту базового класса) BaseRet, он может сделать и с возвращённым из конкретного метода DerivedRet.


                      Если вкратце: возвращаемые типы сужать можно, расширять нельзя.


                      Контравариантность, как подсказывает название, работает в обратную сторону: когда вы объявляете функцию, принимающую DerivedIn, вы говорите, что ваша функция умеет работать со всеми объектами, реализующими так или иначе интерфейс DerivedIn. Если вы теперь в наследнике объявите функцию, принимающую BaseIn, то унаследованный от него DerivedIn она уж точно принять сможет!


                      Если вкратце: принимаемые типы расширять можно, сужать нельзя.

                      • sergyx
                        /#10625458 / +1

                        Ковариантность: если у вас в базовом классе Base есть метод, возвращающий некоторый BaseRet, то вы по факту говорите, что объект каждого класса Derived, унаследованного от Base, возвращает из этого метода BaseRet. Если вы в конкретном Derived возвращаете DerivedRet, унаследованный от BaseRet, то вы всё равно возвращаете BaseRet (потому что DerivedRet is-a BaseRet), и все действия, которые клиент вашего метода может сделать с возвращённым ему (по контракту базового класса) BaseRet, он может сделать и с возвращённым из конкретного метода DerivedRet.

                        Если вкратце: возвращаемые типы сужать можно, расширять нельзя.

                        Если под расширением типов вы подразумеваете
                        B extends A {...}
                        то разве из первого абзаца не следует прямо противоположное — что возвращаемые типы расширять можно?

                        • 0xd34df00d
                          /#10625838

                          Я имею ввиду теоретико-множественное расширение (где тип, упрощая, это множество принадлежащих ему значений). Множество объектов класса B, очевидно, является подмножеством (и чаще всего собственным) множества объектов класса A.

            • varanio
              /#10622884 / +1

              Вы не представляете, как я радовался declare(strict_types=1). Как ребенок.
              Но потом я внезапно осознал, что в эту сторону большого развития не будет из-за того, что всё проверяется в рантайме. И опечалился.
              Собственно, об этом и статья.

              • Gemorroj
                /#10622924

                Когда будет JIT, вероятно, проблемы рантайма должны будут уменьшится.
                Даже opcache, по словам Дмитрия Стогова уже использует тайпхинтинг для оптимизаций.

              • Adelf
                /#10622960 / +1

                У меня такие же чувства. Но если честно, то первый комментарий абсолютно верен. Нам с вами надо писать на Яве и сишарпе :) там все в разы приятнее. Особенно с типами :)
                А так… я пока тоже пишу на PHP. Мне это приносит больше денег :)

      • YaRobot
        /#10622934

        Меня вполне устраивают типы.
        Есть конечно моменты которые стоит доработать, но их уже предложили и рассматривают.
        К примеру хотят добавить скалярный тип.
        Еще бы хотелось иметь возможность создавать собственный тип.
        (type Test = ...)

        • AlexLeonov
          /#10622940

          Ну да, соглашусь.
          Пожалуй не хватает только конструктора кастомных типов.
          Что-то вроде
          type iterable = array | \Iterator, но в своем коде

    • VolCh
      /#10624154

      Можно любить, признавая недостатки в тех или иных областях. Движения в сторону строгости типизации подтверждает, что слабая типизация создаёт проблемы в некоторых случаях типового по нынешним временам использования PHP, не только по мнению части сообщества, но и по мнению разработчиков самого языка. Пойдёт ли движение дальше в сторону ещё большей строгости, вряд ли кто сейчас скажет наверняка, но исключать, что PHP станет ещё ближе к Java/C# в плане строгости типизации, нельзя.

  2. KirEv
    /#10623050

    странная статья, странные предъявы,

    сравнивать типы и ооп в рнр с java, etc… вы серьезно?

    … в рнр много вредных возможностей, например:

    $Obj = new \stdClass;
    $Obj->first = "First";
    $Obj->second = "Second";
    
    $a = "first";
    
    print($Obj->$a);
    
    


    эти вещи добавляют неочевидности, и несколько скриптов в сотни строк каждая из таких конструкций способны голову сломать…

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

    и всякое такое…

    это как автомобили сравнивать… что говно что не говно… говорю, любое авто хорошее, если им по назначению пользоваться и вовремя обслуживать, а не лезть паркетником в гавно\глину и жаловаться «джип с недостатками».

  3. CyberSoft
    /#10623290

    1. Особенно удивляет, что javascript ругают меньше

      Откуда вы взяли про больше/меньше? Получается java/c# совсем не ругают?


      По моим ощущениям, ругают одну только джаву (чего только это стоит — как же её не ругать?), и ощущение складывается от того, что эта платформа моя основная сфера деятельности.


    2. релизы php7 (с нормальным ООП и строгим тайпхинтингом)

      Тут надо начать с "какое оно, нормальное ООП?", а вообще, чтобы понять, откуда взялось ООП в PHP, нужно ответить на вопрос для чего он создавался и как потом росла сложность проектов. Отсюда такое развитие


  4. tehSLy
    /#10623904 / +3

    Не понимаю, почему все накинулись на автора, еще и с аргументами, аля «пишите на своих джаве, сях и прочем, если не нравится», это сродни «я художник, я так вижу». Но тут-то все-таки комьюнити не гуманитарное, думается мне.

    Система типов в пхп очень неплоха для скриптового языка(и то, в том же питоне она объективно лучше), для того, чтобы писать скрипты, только вот сфера применения языка из написания скриптов для домашних страничек явно переросла начальные цели, а инструмент для достижения этих целей двигается существенно медленней. Да, во многом из-за необходимости поддерживать ВС, но это не оправдывает язык как таковой. Это оправдывает решения по выбору фич, реализациям фич в данном контексте, но не самих фич.

    2 моих последних проекта на PHP, это мой основной рабочий язык, он неплохой, он очень простой, в этом его плюс и минус. И если это плюс на старте, то чем дальше в лес, тем больше понимаешь, какой же он в некоторых моментах уродливый и как же не хватает тех фич `b` из тех самых других языков. Я не говорю, что PHP плохой язык, упаси боже, у него действительно свои цели и своя ниша, в которой он чертовски неплох. Но давайте будем честны, как только он делает хоть шаг за границы возможного, он мгновенно теряет весь свой шик перед упомянутыми «тру-языками». #nohate

  5. 0xd34df00d
    /#10623948

    Самая большая проблема в том, что php проверяет типы во время выполнения, а не во время компиляции. Потому что, не смотря на strict_types и type hintings, это ВНЕЗАПНО не строго типизированный язык

    Строгость типизации и статичность/динамичность типизации — это два разных измерения.

    • Corpsee
      /#10624362

      Он имел в виду статическую типизацию, по контексту. Их почему-то все путают, Python-у вон никто не мешает иметь динамическую строгую типизацию, а C статическую слабую)

      • varanio
        /#10624504

        в php и не статическая, и не строгая

        • Corpsee
          /#10625560

          Я обратного и не утверждал. Только строгость типизации никак не связана с проверкой во время компиляции/выполнении. Си, внезапно, тоже является нестрого типизированным.

          • 0xd34df00d
            /#10625844

            О чём я изначально и написал, да.

  6. cliff_5
    /#10624706

    Что значит «js не подходит для сложных программ»? Масштабные 3D игры явно посложнее чем какая-нибудь cms. И нейросети на нём давно реализованны, есть библиотека. Ооп и типы этому никак мешать не могут, всё зависит лишь от программиста

    • varanio
      /#10624716

      Ну не то, чтобы совсем не подходит, но на typescript гораздо легче писать сложные программы. Быстрее понимаешь код и допускаешь меньше тупых ошибок. Angular не зря его выбрал как дефолт

  7. Mabusius
    /#10624856

    Поначалу тоже плевался от нестрогой типизации. А теперь вообще не представляю как без нее можно жить. Откуда я знаю что там в ответе от АПИ приходит? 0 как число? 0 строка? null? А что если один единственный метод этого апи писал другой автор и написал «пустой ответ» вообще как то по другому? В ПХП очень низкий порог вхождения, надо учитывать что тут говнокод прет просто со всех щелей и со строгой типизацией будет только хуже.

    • tehSLy
      /#10625010 / +1

      По началу не представлял как можно жить без нестрогой типизацией. А теперь вообще не представляю, как с ней можно жить.
      В этом и суть. Со строгой типизацией таких вопросов, о том, что же придет в начале, не возникло бы, все было бы явно и понятно

      • Mabusius
        /#10625394

        С чего вдруг? Как строгая типизация помешает говнокодерам с другой стороны в одном месте вернуть 0 числом, а в другом строкой? Никак. Или вы рассчитываете, что они будут ограничены интерфейсами или чем то вроде того? Скорее всего они даже не знают, что это такое.

        • tehSLy
          /#10625482

          С того, например, что если бы типизация была статической, или хотя бы, строгой динамической, компилятор еще на этапе компиляции сказал бы,(ну или хотя бы понимал в рантайме) что тут приходит не то, что нужно, а не тихонечко кастовал величины к тайпхинтам. Эта вседозволенность как раз таки и приводит к говнокоду, просто потому, что так проще, а никто не запрещает. Вы точно работали на хоть сколько-нибудь крупных проектах в PHP? Без этого поддержка и разработка оных становится болью ниже спины, я не знаю, как тут можно спорить. Если в плюсах, джавах, шарпах и т.д. можно выстрелить себе в ногу в некоторых случаях, то это точно не относится к таким вещам, как типизация, которая как бы является фундаментальной вещью, ибо есть суть представления информации. У PHP в этом вопросе как будто читы на оружие, можешь отстрелить все что угодно, просто из-за того, что тебе вместо 0 — «0» вернулся, а вкупе с дизайном его API так это вообще стрелковая фиеста.

          • Mabusius
            /#10625594

            Я не говорил, что участвовал в чем то крупном :). ПХП это в первую очередь не для крупных проектов. Если у вас там потребность лезть аж в компилятор, будьте добры возмите чтото другое. А если не можете, то это не проблема ПХП уже. Как выше писали аналогию с напильником — вы взяли не тот инструмент, а теперь ругаете инструмент, что он плохо работает.

            • tehSLy
              /#10625604 / +1

              Ну вот как появится опыт разработки проектов покрупнее, я уверен, мнение о крутости слабой динамической типизации очень быстро рассеется. Я думаю, тут никто не говорит об кейсах аля «бложик на 200 строк».
              Выше также писали, что приходится работать с тем что есть, и что никто под нас, разработчиков подстраиваться не будет, просто потому что, нам видите ли неудобно так.

    • michael_vostrikov
      /#10625244

      Суть не в том, чтобы везде нестрогую заменить на строгую, а чтобы была возможность использовать то или другое.

      • AlexLeonov
        /#10625374

        Нет никакой «строгой типизации» в PHP. Есть контроль типов в рантайме.
        И да, у вас есть возможность его использовать либо не использовать. Что прекрасно, имхо.

        • VolCh
          /#10625388

          Контроль типов — это и есть типизация. А в рантайме она или в каком-то компайлтайме — разница между динамической и статической типизацией. Вот в python типизация строгая, но динамическая. Хотя в каких-то моментах она теперь менее строгая, чем в PHP.

    • VolCh
      /#10625376

      А откуда вы знаете, что в API 0 (число) — это пустое значение, а не валидное полноценное? Банальный индекс элемента в коллекции, например. Собственно, даже в документации функций типа strpos постоянно выделяют предупреждения, что результат false надо проверять строго, что if (strpos($s1, $s2)) не означает "если подстрока найдена".

  8. JTG
    /#10625660

    У самого основной хлеб — PHP, но частично согласен с автором.
    Как писали в PHP: фрактал плохого дизайна:

    PHP обширно черпает вдохновение из других языков, при этом ему удаётся быть непонятным для тех, кто эти языки знает.

  9. Sannis
    /#10629138

    Чем больше проверок в рантайме, тем больше тормозов. Поэтому слишком сложные проверки навряд ли вообще когда-нибудь появятся. Многослойные дженерики и callable с callable аргументами просто положат рантайм. Также будут тормозить рантайм введение типов для членов класса и в других местах.

    Не нагнетайте, в PHP5 это работает даже быстрее, чем strlen($str), а в PHP7 вообще без разницы.