Search code examples
phpdynamicinvocationmethod-invocation

Is there an array-to-callable function?


Setting

Within SomeClass there is f2member taking two integer arguments and producing its sum. The test passes showing that the call actualy works and retrieves the expected result. Which is calling $gwith two parameters 1 and 1 returning 2.

Important: This works only for php 5.4.11 and upwards compatibility check

class SomeClass extends PHPUnit_Framework_TestCase
{   
    function f2member($a,$b)
    {
        return $a + $b;
    }

    /**
     * @test
     */
    public function test()
    {
        $g = array($this,'f2member');
        $this->assertEquals(2, $g(1,1)); // squiggle line under $g
    }
}

Problem

However, this produces a warning inside phpStorm on every method invocation and a squiggle line under $g:

squiggle line warning in phpStorm

Function name must be callable - string, Closure or class implementing __invoke, currently array

The origin of the warning is clear to me and now i am looking for ways to avoid these warnings. A requirement is, that i dont want to change the style of calling the function. Another thing i don't want to do is to deactivate this warning. I would rather prefer to wrap something around it, which provides the necessary information to the type system.

Attempt

I already encountered several solutions to remove the warnings. One is to define a user defined function, which only documents the required target type.

/**
 * @param array $arr
 *
 * @return callable
 */
function callable_for($arr)
{
    return $arr;
}

This returns an array, but also explicitly tells the type system what comes out of the callable_for function. With this type annotation in place phpStorm now stops complaining about this warning, although it still returns an array.

$g = callable_for(array($this,'f2member'));

Question

Isn't there something out of the box like my callable_for in php to achieve this? If the answer is no, then i am looking for the most concise solution we can find.

I already tried looking on SO, php.net and google. Maybe, I just searched for the wrong word combinations, here are just two samples:

  • array to callable php
  • create callable method handle php

BigPicture

Just in case suspects arise this is a X/Y problem: I have another function taking a callable as a parameter. With closures it is very natural to define something, which can be invoked later on. However, how do i define a callable for a member or a static method without wrapping it in another delegation Closure? The array notation allows to be used to uniformly pass: closures or static/member method handles to my later function. I am now trying to find a concise solution to this, which comes close to this.


Solution

  • Thus, another advancement could be, to modify callable_for to take two arguments and wrap both responsibilities; to create the array and to document the target return type.

    /**
     * @param mixed $context
     * @param string $method
     *
     * @return callable
     */
    function callable_for($context, $method)
    {
        return array($context, $method);
    }
    

    Using this implementation raises the conciseness of $g's declaration to an acceptable level.

    $g = callable_for($this,'f2member');
    

    This function still returns an array, but the type system can use the given information to correctly treat this array for dynamic method invocation.