tl;dr: Если Вы пишете framework agnostic пакет, не используйте illuminate/support.
Перевод статьи Метта Аллана (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)
OnYourLips
21.08.2016 10:26+16> mb_string не стандартный php модуль
> Не стоит заставлять пользователей перекомпилировать PHP
Я даже не знаю, что в ответ на это написать. Передайте пожалуйста автору статьи, чтобы он завязывал с тяжелыми наркотиками.Andrey_Volk
21.08.2016 11:10+4Тут дело не только в авторе статьи, но и в переводчике. Ляпов хватает и у того, и у другого, а всё вместе — получилась так себе статья...
ulole
21.08.2016 15:28Вы имеете в виду, что mb_string по умолчанию не включен?
nazarpc
22.08.2016 02:56+3Полагаю, имеется ввиду что PHP перекомпилировать для подключения модулей совсем не нужно, их можно подключать и отключать по необходимости.
Более того, mb_string достаточно популярный модуль в любом случае.Old_Chroft
22.08.2016 08:41mb_string достаточно популярный модуль
Только не для англоговорящих, коим и является автор оригинальной статьи.
JhaoDa
21.08.2016 11:38+6Автор и переводчик, видимо, не знают, что illuminate/support это не только 4 килограмма
диетического мясахэлперов, но ещё и базовые классы фреймворка типа сервис-провайдера и менеджер, и что из упомянутых 6000+ пакетов большая часть является именно пакетами для ларавел.
dd бесполезен в терминале
Феерично, просто феерично…Big_Shark
22.08.2016 05:44Так речь идет только о framework agnostic пакетах которые зависят от illuminate/support, если вы пишете пакет который будет работает только с laravel или lumen то ничего плохого в использовании illuminate/support нет.
JhaoDa
22.08.2016 13:26Это сначала речь идёт только о framework agnostic пакетах, а потом упоминаются некие «более 6000 пакетов».
Evsign
21.08.2016 15:28+2> а dd бесполезен в терминале
Ахаха, делает http запрос к сайту и удивляется, что ему в консоль падает html от dd… К слову, в cli — dd не генерирует html.
sasha1024
21.08.2016 18:13-6isset($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» — это, хоть и длинно, но передаёт логический смысл.enniel
21.08.2016 18:17-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
И коротко, и логический смысл вполне понятен, как мне кажется.sasha1024
21.08.2016 19:47+2А 0, '' и array() у Вас никогда не бывают значениями массива?
antoo
21.08.2016 20:40-3Чаще всего в таких случаях это используется в качестве значения по умолчанию.
Ничего же не мешает написать:$a = @$arr['key'] ?: [];
sasha1024
21.08.2016 21:02-2Ну, это уже более осмысленно (ИМХО).
Но всё же в случае со строками (как в исходном Вашем примере) Вы потеряете значение '0'.
Т.е. «@$arr['key'] ?: ''» вернёт '' и для значения '', и для значения '0'.serg_deep
22.08.2016 08:08+4$a = @$arr['key'] ?: [];
Я очень сильно надеюсь что вы это не в циклах используете? Если в циклах, то дорога в АД вам обеспечена. Почему? Зпустите бенч @ vs isset и поймете. В PHP символ подавления ошибок луше вообще не использовать, это пережиток прошлого.
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";
xotey83
22.08.2016 13:52У нас используется error handler. Поэтому оператор подавления ошибок не используем.
Добавьте error handler и тогда время, затраченное на "@" улетит в космос.quantum
22.08.2016 17:37Да, с пустым хэндлером как раз на порядок:
1.4448421001434
1.7445378303528
8.413987159729
10.663077116013
Fesor
22.08.2016 00:02+9Часто пишу вот так:
обновляйтесь на php7 и прекращайте
$a = $arr['key'] ?? null; $b = $arr['nonexistent'] ?? null;
Оператор подавления ошибок — лучше бы его в PHP вообще небыло.
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»), без смешения сущностей.Fesor
22.08.2016 16:52и так логически (зачем-то) включают в себя подавление ошибок
нет, они не "подавляю ошибки", они просто их не вызывают. Вы просто проверяете есть там значение или нет.
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-й версии.
L0NGMAN
22.08.2016 10:13На счёт проблемы версии illuminate/support, можно свободно писать в composer-е вашего пекиджа: 5.1.*|5.2.*|5.3.* если конечно уверены что все они подходят для вашего package.
nazarpc
Как раз сегодня видел шутку в тему