tl;dr: Если Вы пишете framework agnostic пакет, не используйте illuminate/support.


laravel



Перевод статьи Метта Аллана (Matt Allan) "Don't Use Illuminate Support".


Множество framework agnostic Composer пакетов (PHP) зависят от illuminate/support, который включает в себя хелперы и код общего назначения, используемый в Laravel framework. А всё потому, что данный пакет содержит в себе множество замечательных функций типа array_get, а также великолепные коллекции.


Хелперы — отличная штука, но я не думаю, что разработчики понимают все последствия включения данного пакета в свой проект. Все боятся критики за изобретение велосипеда, поэтому тянут 6000+ строк кода чтобы самим не писать такой код


isset($arr[$k]) ? $arr[$k] : null

Ад зависимостей


Используя illuminate/support (5.2) Вы подтягиваете в свой проект illuminate/contracts, doctrine/inflector, полифилл для random_bytes, и mb_string. К счастью дерево зависимостей на этом заканчивается.


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

mb_string не стандартный php модуль, поэтому он не может быть установлен на машине пользователя. Если Вы не работаете со строками, не стоит заставлять пользователей перекомпилировать PHP только для того, чтобы использовать свой пакет. Можете использовать stringy как хорошую альтернативу, он использует полифилл.


Конфликт версий


Более 6000 пакетов зависят от illuminate/support. Если кто-нибудь установит Ваш пакет и другой, включающий в себя illuminate/support, он должен быть зависим от той же версии, иначе получится конфликт. Беглый взгляд показывает, что множество проектов всё ещё используют версию 4.1.x.


Положение ухудшается, если Ваш пакет используется вместе с Laravel или Lumen. Пользователь не сможет обновиться раньше Вас (и все пакеты, использующие illuminate/support). Теперь Вы мешаете пользователю обновить свой фреймворк. Единственная альтернатива, использовать неограниченные диапазоны вроде >5.2, но это довольно плохая идея.


Глобальная область видимости


illuminate/support подтягивает 52 функции в глобальную область видимости. Хорошо если используемый фреймворк не использует пространство имён. Зависимости не должны загрязнять глобальную область.


Но ведь хелперы прекрасны, почему бы не использовать их? Некоторые трансформеры работают не так, как ожидается, а dd бесполезен в терминале. Но все хотят, чтобы эти 52 функции вели себя так как хочется, поэтому суют их в глобальную область.


Фасады


Это конечно небольшая проблема, но сильно раздражает когда пишешь код по 40+ часов в неделю. illuminate/support имеет множество часто используемых классов типа Collection, Request, Response, и App. Каждый раз, когда я начинаю набирать пространство имён, IDE пытается импортировать неправильную коллекцию или бесполезный фасад вместо фактического класса, который мне нужен. illuminate/support включает в себя целый каталог классов которые даже не работают за пределами Laravel! Я гарантирую, что Вы не используете фасады в рамках framework agnostic, поэтому, пожалуйста, прекратите тянуть их в мой проект.


Критические ошибки


Сейчас ваш пакет зависит от 3-х различных пакетов, не нарушающих SemVer. Стоит ли рисковать получить такие же проблемы как с left-pad вместо написания нескольких строк кода?


Нацеливание на меньшее количество зависимостей


Попробуйте использовать как можно меньше зависимостей. Если посмотреть на проекты от phpleague, то можно заметить, что все они имеют малое количество зависимостей или вообще их не имеют. Хорошая практика — написать несколько небольших вспомогательных классов для 2-х или 3-х функций. Если не хочется писать самостоятельно, скопируйте функции которые Вам нужны в соответствии с лицензией.


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


Альтернативы


Если Вам нужна тонна функционала, следует использовать пакеты вместо того, чтобы всё писать самому. Поскольку illuminate/support охватывает огромное количество функционала, ниже список тех, что можно использовать для каждого конкретного случая.


doctrine/inflector


Приведение слов к единственному и множественному числу. Это на самом деле зависимость illuminate/support.


danielstjules/Stringy


Охватывает все функции преобразования строк. Использовался в illuminate/support до версии 5.2.


dusank/knapsack


Работа с коллекциями. Единственные пакет, обнаруженный мной сравнимый с Laravel collections.


anahkiasen/underscore-php


Замена функциям для работы с массивами, использует точечную нотацию. Я не знаю, хорошей альтернативы, поддерживающей точечную без использования зависимостей. Я просто пишу ванильный php для этого. В php7+ можно использовать null coalesce operator вместо array_get.

Поделиться с друзьями
-->

Комментарии (35)


  1. nazarpc
    21.08.2016 09:35
    +18

    Как раз сегодня видел шутку в тему



  1. OnYourLips
    21.08.2016 10:26
    +16

    > mb_string не стандартный php модуль
    > Не стоит заставлять пользователей перекомпилировать PHP

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


    1. Andrey_Volk
      21.08.2016 11:10
      +4

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


    1. ulole
      21.08.2016 15:28

      Вы имеете в виду, что mb_string по умолчанию не включен?


      1. nazarpc
        22.08.2016 02:56
        +3

        Полагаю, имеется ввиду что PHP перекомпилировать для подключения модулей совсем не нужно, их можно подключать и отключать по необходимости.
        Более того, mb_string достаточно популярный модуль в любом случае.


        1. Old_Chroft
          22.08.2016 08:41

          mb_string достаточно популярный модуль
          Только не для англоговорящих, коим и является автор оригинальной статьи.


          1. nazarpc
            22.08.2016 08:45
            +2

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


            1. sasha1024
              22.08.2016 10:17
              +1

              Хоть одна польза от emoji!


          1. enniel
            22.08.2016 11:02

            Это точно. Англоязычные разработчики не особо парятся по этому поводу. Совсем недавно столкнулся с такой проблемой в climate.


  1. JhaoDa
    21.08.2016 11:38
    +6

    Автор и переводчик, видимо, не знают, что illuminate/support это не только 4 килограмма диетического мяса хэлперов, но ещё и базовые классы фреймворка типа сервис-провайдера и менеджер, и что из упомянутых 6000+ пакетов большая часть является именно пакетами для ларавел.

    dd бесполезен в терминале
    Феерично, просто феерично…


    1. Big_Shark
      22.08.2016 05:44

      Так речь идет только о framework agnostic пакетах которые зависят от illuminate/support, если вы пишете пакет который будет работает только с laravel или lumen то ничего плохого в использовании illuminate/support нет.


      1. JhaoDa
        22.08.2016 13:26

        Это сначала речь идёт только о framework agnostic пакетах, а потом упоминаются некие «более 6000 пакетов».


  1. G-M-A-X
    21.08.2016 13:25
    -7

    То есть на самом деле повторное использование кода — зло? :)

    Тогда не используйте вообще фреймворки.


    1. aktuba
      21.08.2016 18:14
      +5

      О! Он снова здесь))))


  1. Evsign
    21.08.2016 15:28
    +2

    > а dd бесполезен в терминале
    Ахаха, делает http запрос к сайту и удивляется, что ему в консоль падает html от dd… К слову, в cli — dd не генерирует html.


  1. sasha1024
    21.08.2016 18:13
    -6

    isset($arr[$k])? $arr[$k]: null

    Я немного отстал от жизни, но зачем писать так?

    Ведь «isset(expr)» по сути означает «expr !== null» (точнее, isset ещё не показывает ошибки в случае несуществования переменной/индекса, поэтому на самом деле «@expr !== null»). Т.е. приведенный выше кусок кода по сути означает «@$arr[$k] !== null ? $arr[$k] : null», только больше запутан. Почему не написать просто «@$arr[$k]»?

    Я понимаю, если бы было «array_key_exists($k, $arr) ? $arr[$k] : null» — это, хоть и длинно, но передаёт логический смысл.


    1. enniel
      21.08.2016 18:17
      -1

      Я собственно тоже не понимаю зачем так пишут, но многие пишут просто «потому что так короче».


      1. sasha1024
        21.08.2016 18:19
        +3

        Чем что? Чем «@$arr[$k]»?


        1. sasha1024
          21.08.2016 18:23

          А, собственно, я понял. Вы, наверное, имели в виду не «@$arr[$k] !== null ? $arr[$k] : null», а «@$arr[$k] !== null ? $arr[$k] : $default»?


        1. enniel
          21.08.2016 18:24

          Чем array_key_exists($k, $arr)? $arr[$k]: null


        1. enniel
          21.08.2016 18:36
          +1

          А символ @ я вообще не использую.


    1. antoo
      21.08.2016 19:36
      -6

      Часто пишу вот так:

      $arr = ['key' => 'value'];
      
      $a = @$arr['key'] ?: NULL;
      $b = @$arr['nonexistent'] ?: NULL;
      
      var_dump($a); // string(5) "value"
      var_dump($b); // NULL
      

      И коротко, и логический смысл вполне понятен, как мне кажется.


      1. sasha1024
        21.08.2016 19:47
        +2

        А 0, '' и array() у Вас никогда не бывают значениями массива?


        1. antoo
          21.08.2016 20:40
          -3

          Чаще всего в таких случаях это используется в качестве значения по умолчанию.
          Ничего же не мешает написать:

          $a = @$arr['key'] ?: [];


          1. sasha1024
            21.08.2016 21:02
            -2

            Ну, это уже более осмысленно (ИМХО).
            Но всё же в случае со строками (как в исходном Вашем примере) Вы потеряете значение '0'.
            Т.е. «@$arr['key'] ?: ''» вернёт '' и для значения '', и для значения '0'.


            1. serg_deep
              22.08.2016 08:08
              +4

              $a = @$arr['key'] ?: [];

              Я очень сильно надеюсь что вы это не в циклах используете? Если в циклах, то дорога в АД вам обеспечена. Почему? Зпустите бенч @ vs isset и поймете. В PHP символ подавления ошибок луше вообще не использовать, это пережиток прошлого.


              1. quantum
                22.08.2016 10:04

                Ну не на порядок медленнее:

                Заголовок спойлера
                <?php
                
                $iters = 1000000;
                
                $arr = [];
                foreach (range(1, $iters) as $i) {
                    $arr[$i*2] = $i;
                }
                
                
                
                $t = microtime(true);
                foreach (range(1, $iters) as $i) {
                    $a = $arr[$i] ?? null;
                }
                print microtime(true)-$t;
                print "\n";
                
                
                $t = microtime(true);
                foreach (range(1, $iters) as $i) {
                    $a = isset($arr[$i]) ? $arr[$i] : null;
                }
                print microtime(true)-$t;
                print "\n";
                
                
                $t = microtime(true);
                foreach (range(1, $iters) as $i) {
                    $a = array_key_exists($i, $arr) ? $arr[$i] : null;
                }
                print microtime(true)-$t;
                print "\n";
                
                
                $t = microtime(true);
                foreach (range(1, $iters) as $i) {
                    $a = @$arr[$i];
                }
                print microtime(true)-$t;
                print "\n";
                


                1. xotey83
                  22.08.2016 13:52

                  У нас используется error handler. Поэтому оператор подавления ошибок не используем.
                  Добавьте error handler и тогда время, затраченное на "@" улетит в космос.


                  1. quantum
                    22.08.2016 17:37

                    Да, с пустым хэндлером как раз на порядок:

                    1.4448421001434
                    1.7445378303528
                    8.413987159729
                    10.663077116013


      1. symbix
        21.08.2016 22:31
        +8

        Все, кто пользуется в таком контексте @, будут гореть в аду.


      1. Fesor
        22.08.2016 00:02
        +9

        Часто пишу вот так:

        обновляйтесь на php7 и прекращайте


        $a = $arr['key'] ?? null;
        $b = $arr['nonexistent'] ?? null;

        Оператор подавления ошибок — лучше бы его в PHP вообще небыло.


        1. sasha1024
          22.08.2016 10:06

          Про "??" безусловно согласен (мне непонятно зачем вообще вводили "?:" без среднего операнда вместо того, чтобы сразу ввести нормальный coalesсe; учитывая, что в PHP ошибка/отсутствие индицируется либо возвратом null, либо возвратом false, PHP нужны два а-ля-coalesce оператора: «coalesceNull(a, b) = a!==null ? a : b» и «coalesceStrictFalse(a, b) = a!==false? a : b»; первый уже НАКОНЕЦ-ТО ввели в виде "??", теперь ждём второго).

          Но про подавление ошибок не понимаю. Ведь isset и "??" (а также empty) и так логически (зачем-то) включают в себя подавление ошибок. Что не так-то? По-моему, было бы лучше, если бы они как раз НЕ включали в себя подавление ошибок и одним оператором делалась ровно одна вещь (писали бы «@$arr[$key] ?? $default» или «array_key_exists($key, $arr) ? $arr[$key] : $default»), без смешения сущностей.


          1. Fesor
            22.08.2016 16:52

            и так логически (зачем-то) включают в себя подавление ошибок

            нет, они не "подавляю ошибки", они просто их не вызывают. Вы просто проверяете есть там значение или нет.


            1. sasha1024
              22.08.2016 17:46

              Я-то это понимаю. Но считаю это неправильным. В смысле:
              1. Если выражение «expr» вызывает ошибку, то логично, чтобы любое выражение, содержащее «expr» (в том числе, например, «isset(expr)» и т.п.) в качестве операнда вызывало ту же ошибку. Не вызывать в таком случае ошибку глупо — для проверки без вызова ошибки должны использоваться отдельные конструкции (например, «array_key_exists($key, $array)» вместо «isset($array[$key])» и какое-то пока несуществующее «variable_exists('varname')» вместо «isset($varname)» и т.д.).
              2. Даже если допустить, что существование какого-то оператора «op(…)», не вызывающего ошибку для вызывающего ошибку выражения «expr», логично — то к isset всё равно это не относится. Так как isset дважды нелогично — кроме описанного в предыдущем пункте, оно ещё пытается выполнить два действия сразу: оно проверяет наличие (переменной, индекса, поля) и проверяет на неравенство null. Т.е. я бы ещё согласился использовать какой-то оператор «exists(…)», не вызывающий ошибку для вложенного выражения — если бы он просто проверял наличие.

              Короче говоря. Я понимаю, что isset быстрее @/array_key_exists. Я понимаю, что isset не является полным эквивалентом "@expr !== null" (кроме разного поведения в случае наличия обработчиков ошибок, оно ещё по-разному реагирует на ошибки в подвыражениях, например, «isset($arr[f()])» не подавит ошибку внутри фукнции f, а «@$arr[f()]» — подавит). Но лучше я потеряю несколько микросекунд, чем буду писать уродливый код с isset.

              Что же касается "??", то как я уже говорил выше, хорошо, что его наконец-то ввели (не понимаю, почему так поздно, в частности позднее уродливого бинарного "?:"), плохо, что на него распространяется «магия» в стиле isset/empty (лучше бы сделали array_key_exists встроенным оператором для быстродействия) — но использовать его мне, думаю, не придётся, потому что я завязал с PHP задолго до появления даже 5.6-й версии.


  1. L0NGMAN
    22.08.2016 10:13

    На счёт проблемы версии illuminate/support, можно свободно писать в composer-е вашего пекиджа: 5.1.*|5.2.*|5.3.* если конечно уверены что все они подходят для вашего package.