Есть бэкенд на 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
Ответ
Вы забыли о еще одном очень-очень важном моменте. В первом вашем варианте конструкция:
$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.
Комментариев нет:
Отправить комментарий