php, perfectionism

Question:

There is a PHP backend serving different 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);

Have you already seen the error? 😉

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

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 on error, the front will stumble on deserialization. That is, I need to be guaranteed to get the same structure of the 'r' field in the catch block as in the try block

I see two solutions:

  1. Remove anything from the constructor that might throw an exception. I don't like this approach because my constructors only check input data for validity and cast them if necessary. IMHO this functionality has its place in the constructor.

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

    (I don't like this option at all. It directly 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 myself?

Maybe I'm working and I don't see a simple and obvious solution? So that it worked fine and did not turn up from the soul?

UPD PHP 5.6

Answer:

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

$call::defaultResult()

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

With this in mind, your second option is much better. Although I would brush it up a bit (I'm using PHP 5.5+ in the example below):

$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:

PHP traditionally uses \InvalidArgumentException and its subclasses for input validation errors. A \Error is a special class of errors introduced in PHP7. It may be worth changing the name of your exception class slightly to improve readability and avoid ambiguity after migrating to PHP7.

Scroll to Top