#php #алгоритм #инспекция_кода
Есть бэкенд на PHP, обслуживающий разные запросы и возвращающие json. Точка входа(упрощенно): try { switch ($method) { case 'login': $call = new Login($data); break; case 'list': $call = new List($data); break; default: $call = new WrongMethod($data); } $out = ['r' => $call(),'n' => (string)Error::OK,'e' => '']; } catch (Error $e) { $out = [ 'r' => $call::defaultResult())), 'n' => (string)$e->getCode(), 'e' => $e->getMessage() ]; } print_r(json_encode($out, JSON_UNESCAPED_UNICODE), false); Уже увидели ошибку? ;) Проблема: $call::defaultResult() - в блоке catch объект $call не определен. Все классы пуляют Error extends Exception и реализуют интерфейс interface Call { public function __construct(array $data); public function __invoke(); public static function defaultResult(); } defaultResult нужен потому, что _invoke может вернуть скаляр, обычный массив либо ассоциативный массив и если при ошибке использовать значение какого-то одного типа, то фронт споткнется на десериализации. То есть мне нужно гарантированно получить в блоке catch такую же структуру поля 'r', как и в блоке try Я вижу два варианта решения: Вынести из конструктора все, что может бросить исключение. Такой подход не нравится тем, что у меня в конструкторах исключительно проверка входных данных на валидность и их приведение в случае необходимости. ИМХО этому функционалу самое место в конструкторе. Перейти на тёмную сторону и сделать как-то так: (Этот вариант мне вообще не нравится. Прям вызывает дискомфорт и потерю аппетита.) function getMethodClass($method) { switch ($method) { case 'login': return "Login"; case 'list': return "GetList"; default: return "WrongMethod"; } } $className = $callsNameSpace."\\" . getMethodClass($method); try { $call = new $className($data); ... } catch (Error $e) { $out = ['r' => call_user_func(array($className, 'defaultResult')), ... } Внимание, вопрос! Может я не прав относительно своих фобий и первый(или второй) вариант вполне себе хорош? Может я заработался и не вижу простого и очевидного решения? Такого чтоб и работало нормально и с души не воротило? UPD PHP 5.6
Ответы
Ответ 1
Вы забыли о еще одном очень-очень важном моменте. В первом вашем варианте конструкция: $call::defaultResult() невалидна (как минимум, идеологически). Фактически вы пытаетесь вызвать метод класса у экземпляра, что лишено смысла. С учетом этого, второй ваш вариант намного лучше. Хотя я немного причесал бы его (в примере ниже использую PHP 5.5+): $map = [ 'login' => Login::class, 'list' => List::class, ]; $class_name = isset($map[$method]) ? $map[$method] : WrongMethod::class; try { $call = new $class_name($data); // ... } catch (Error $e) { $out = ['r' => $class_name::defaultResult()]; } Замечание: Для ошибок валидации входных данных в PHP традиционно используется \InvalidArgumentException и его подклассы. А \Error - это специальный класс ошибок введенный в PHP7. Возможно стоит немного изменить имя вашего класса исключений, чтобы повысить читаемость кода и избежать двусмысленности после перехода на PHP7.
Комментариев нет:
Отправить комментарий