There is a PHP backend serving various requests and returning json.

Entry point (simplified):

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); 

Already saw the error? ;)

Problem: $call::defaultResult() - in the catch the $call object is not defined.

All classes bullet Error extends Exception and implement the interface

 interface Call { public function __construct(array $data); public function __invoke(); public static function defaultResult(); } 

defaultResult needed because _invoke can return a scalar, a regular array, or an associative array, and if you use a value of one type for an error, the front will stumble upon deserialization. That is, I need to be guaranteed to get in the catch the same structure of the 'r' field as in the try block

I see two solutions:

  1. Remove from the constructor everything that can throw an exception. I do not like this approach because in my constructors, I only check the input data for validity and bring it in if necessary. IMHO this functionality is the place in the constructor.

  2. Go to the dark side and do something like this:

    (I don’t like this option at all. It causes discomfort and loss of appetite.)

     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')), ... } 

Attention, question!

Maybe I'm wrong about my phobias and the first (or second) option is quite good for yourself?

Maybe I earned and do not see a simple and obvious solution? So that it works fine and does not upset your soul?

UPD PHP 5.6

    1 answer 1

    You forgot about another very, very important point. In your first design version:

     $call::defaultResult() 

    invalid (at least ideologically). In fact, you are trying to call a class method on an instance, which is meaningless.

    With this in mind, your second option is much better. Although I would brush it a bit (in the example below I use 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()]; } 

    Comment:

    For input validation errors, PHP traditionally uses \InvalidArgumentException and its subclasses. A \Error is a special class of errors introduced in PHP7. It may be worthwhile to slightly change the name of your exception class in order to increase the readability of the code and avoid ambiguity after switching to PHP7.

    • Well, why are there any tener operators everywhere? And the question is not how to call, but how to validate the data - Naumov
    • Is @naumov "everywhere" in one place? Use if if you like it more. Well, the question is not about validation, but about code-review. - Dmitriy Simushev
    • ok guess the coding standard where is recommended to use the tenancy operators? - Naumov
    • 2
      @Naumov, ok, give the generally accepted coding style where it is not recommended . Moreover, the TS seems to have PHP7 in general, and if so, then instead of isset() ?: Can you use ?? which is much clearer if . - Dmitriy Simushev
    • one
      @DmitriySimushev not-not-not, very embarrassed. Someday you will have to crawl on PHP7, so it's better now, from sin, I will rename it. - rjhdby