Блог @vudaltsov о разработке на PHP
./vendor/bin/dep blackjackscope, а не $this, но параметр $newScope идёт третьим.
2. Можно было бы скипнуть второй параметр, используя именованные аргументы, но $newThis не имеет значения по умолчанию, поэтому приходится явно передавать null.
3. Метод возвращает ?Closure, из-за чего приходится проверять !== null вместо того, чтобы сразу писать Closure::bind(fn () => ...)().
4. Не поддерживается статанализ (лечится добавлением соответствуюещго стаба в проект или в анализаторы через PR).
Что будем делать? Мучиться? Нет, добавим в проект простую функцию bind, которая разом решит все проблемы!
Вариант такой функции: https://3v4l.org/K1LUG.
Кстати, Psalm падает при использовании @template of Closure, а вот PhpStan справляется: phpstan.org/r/40205…b14488e2.
И ещё. Я считаю, не стоит делать composer-пакеты из таких функций, потому что в каждом проекте они будут называться и выглядеть по-разному. Такие тривиальные штуки гораздо проще копировать и адаптировать под свои представления о прекрасном, чем тянуть сомнительные зависимости.parent можно использовать как тип в любых местах. Вроде и логично, но как-то странно... Согласитесь, вы бы поёжились, если бы увидели что-то подобное на ревью.
final class ParentOverdose extends ArrayObject
{
public function __construct(
public parent $iAmParent,
) {
parent::__construct();
}
public function iReturnParent(): parent
{
return new parent();
}
public function iAcceptParent(parent $parent): void
{
}
}
https://3v4l.org/TDXEaUtils, *Helper и т.д.
Функция — это никакой не code smell, а удобная конструкция процедурного программирования, которую не стоит избегать и недооценивать. Если ты считаешь наоборот, я попробую тебя переубедить!
Сначала разберём два традиционных замечания к функциям. Во-первых, в PHP для функций не предусмотрен автолоадинг. Да, файлы с функциями нужно явно прописывать в autoload.files, они будут загружаться на каждом хите. Однако, этот оверхед пренебрежим в случае "умирающего" PHP и отстутствует вообще, если используется preloading или PHP "не умирает". Поэтому тут в минусы запишем только возню с composer.json.
Во-вторых, функции нельзя мокать. Это традиционное мокистское заявление, на которое почти всегда можно ответить: "Так не мокай!" и послать к Фаулеру. Функция — это имплементация, а не интерфейс. "Интерфейс" функции time — это callаble в PHP и callable(): int в Psalm/PHPStan. Чтобы заабстрагировать функцию, её следует объявить как callable инъекцию или параметр (см. пост #133), и тогда в тесте можно передать всё что угодно. Если же мы говорим про "захардкоженное" использование функции, то, если она не берёт на себя больше, чем должна, это не составит проблем. Никто же не боится в коде "хардкодить" array_map или trim. Главное, чтобы функции сами были протестированы.
А вот плюсов я насчитал куда больше!
➕Функции идеально подходят для операций, не требующих организации состояния. Согласитесь, класс с приватным конструктором без состояния — это myFunction() vs MyClass::myMethod().
Критерии хорошей функции всё те же: низкая цикломатическая сложность, небольшое количество параметров, SRP. Если функцию "раздуло", то либо её надо разбить на несколько функций с разными именами, либо задачу всё-таки надо решать с использованием классов.
Вот несколько функций из нашего проекта: gist.github.com/vudalts…e2d8f1d9.
И напоследок статья Никиты Попова, которая несколько лет назад могла мне поменять отношение к функциям: www.npopov.com/2012/08…bic.html.
Пиши в комментариях, используешь ли ты функции или нет и почему.enum EnumNaming
{
case camelCase;
case PascalCase;
case snake_case;
case SCREAMING_SNAKE_CASE;
}
dump(
EnumNaming::camelCase,
EnumNaming::PascalCase,
EnumNaming::snake_case,
EnumNaming::SCREAMING_SNAKE_CASE,
);from и tryFrom — они позволяют получить перечисление по его скалярному эквиваленту ($value).
В этом посте мы попробуем добавить аналогичные методы fromName и tryFromName для получения перечисления любого типа по его имени ($name).
Для начала обратим внимание на любопытную фразу в документации: "Для вариантов перечисления применяются те же правила синтаксиса, что и для любой метки в PHP, смотрите Константы". По идее, это означает, что мы можем динамически работать с енамами как с константами. Попробуем!
enum Locale
{
case ru;
case en;
public static function tryFromName(string $name): ?self
{
$constant = self::class.'::'.$name;
if (\defined($constant)) {
return \constant($constant);
}
return null;
}
public static function fromName(string $name): self
{
return self::tryFromName($name)
?? throw new \ValueError(sprintf(
'"%s" is not a valid name for enum "%s"',
$name,
self::class,
));
}
}
Вуаля, работает! https://3v4l.org/EgBB1