phpcallbackoutputnegate

PHP - nagate callback result


Is there any way to negate result of callback?

$this->myInjectedService->doSomething([$this, 'myCallback']);

I need to nagate result of myCallback method. I know I can do it by nested function, but is there any more clean way (exclamation mark)?

$this->myInjectedService->doSomething(function() {
    return !$this->myCallback();
});

Solution

  • No, there is no direct way to do it like that.

    What you could do, would be to create a callback wrapper which negates the callback you want to call. Every time you want to negate a callback you can use that wrapper instead of the callback itself:

    <?php
    
    class SomeService
    {
        public function doSomething(callable $callback)
        {
            return call_user_func($callback);
        }
    }
    
    final class CallbackWrapper
    {
        public static function call(callable $callback, bool $negate = false): callable
        {
            if ($negate) {
                return function() use ($callback) {
                    return !call_user_func($callback);
                };
            }
    
            return $callback;
        }
    }
    
    class Test
    {
        public function __construct()
        {
            $this->someService = new SomeService();
        }
    
        public function doSomething()
        {
            // Scenario 1: use direct callback
            $result = $this->someService->doSomething([$this, 'thisIsACallback']);
    
            var_dump($result);
    
            // Scenario 2: use wrapper without negating
            $result = $this->someService->doSomething(
                call_user_func_array('CallbackWrapper::call', [[$this, 'thisIsACallback']])
            );
    
            var_dump($result);
    
            // Scenario 3: use wrapper with negation
            $result = $this->someService->doSomething(
                call_user_func_array('CallbackWrapper::call', [[$this, 'thisIsACallback'], true])
            );
    
            var_dump($result);
        }
    
        public function thisIsACallback(): bool
        {
            return true;
        }
    }
    
    $test = new Test();
    $test->doSomething();
    

    The results would be:

    // Scenario 1
    bool(true)
    
    // Scenario 2
    bool(true)
    
    // Scenario 3
    bool(false)
    

    Please note that this is the simplest approach I could think of, but if you need more complex callbacks to be called then you might need to update the wrapper.

    PS: Even if it's possible, I don't recommend using this approach, as using a nested function, as you already did, is much more clear and easier to read/understand.