guanguans.github.io icon indicating copy to clipboard operation
guanguans.github.io copied to clipboard

PHP Callback/Callable 类型使用

Open guanguans opened this issue 3 years ago • 0 comments

PHP Callback/Callable 类型使用

测试代码

<?php

/**
 * Class ParentCallback
 */
class ParentCallback
{
    /**
     * @param  int  $a
     * @param  int  $b
     * @param  int  $c
     *
     * @return int
     */
    public static function parentSum(int $a, int $b, int $c)
    {
        return $a + $b + $c;
    }

    /**
     * @param  int  $a
     * @param  int  $b
     * @param  int  $c
     *
     * @return float|int
     */
    public function parentMultiply(int $a, int $b, int $c)
    {
        return $a * $b * $c;
    }
}

/**
 * Class Callback
 */
class Callback extends ParentCallback
{

    /**
     * @param  int  $a
     * @param  int  $b
     *
     * @return int
     */
    public static function sum(int $a, int $b)
    {
        return $a + $b;
    }

    /**
     * @param  int  $a
     * @param  int  $b
     *
     * @return float|int
     */
    public function multiply(int $a, int $b)
    {
        return $a * $b;
    }
}

/**
 * Class Invoke
 */
class Invoke
{

    /**
     * @param  int  $a
     * @param  int  $b
     *
     * @return float|int
     */
    public function __invoke(int $a, int $b)
    {
        return $a / $b;
    }
}

/**
 * @param  int  $a
 * @param  int  $b
 *
 * @return int
 */
function sum(int $a, int $b)
{
    return $a + $b;
}

/**
 * @param $callback
 * @param  mixed  ...$parameter
 *
 * @return mixed
 */
function do_something(callable $callback, ...$parameter)
{
    return call_user_func($callback, ...$parameter);
}

测试示例

// 闭包
$ret = do_something(function ($a, $b){
    return $a - $b;
}, 5, 6);
printf("闭包测试示例: %s\n", $ret);

// 函数
$ret = do_something('sum', 5, 6);
printf("函数测试示例: %s\n", $ret);

// 静态方法
$ret = do_something([Callback::class, 'sum'], 5, 6);
printf("静态方法示例: %s\n", $ret);

$ret = do_something('\Callback::sum', 5, 6);
printf("静态方法示例: %s\n", $ret);

$ret = do_something([Callback::class, 'self::parentSum'], 5, 6, 7);
printf("静态方法示例: %s\n", $ret);

$ret = do_something([Callback::class, 'parentSum'], 5, 6, 7);
printf("静态方法示例: %s\n", $ret);

$ret = do_something([Callback::class, 'parent::parentSum'], 5, 6, 7);
printf("静态方法示例: %s\n", $ret);

// 方法
$callback = new Callback;
$ret = do_something([$callback, 'multiply'], 5, 6);
printf("普通方法示例: %s\n", $ret);

// invoke
$invoke = new Invoke;
$ret = do_something($invoke, 5, 6);
printf("对象 invoke 示例: %s\n", $ret);

测试结果

闭包测试示例: -1
函数测试示例: 11
静态方法示例: 11
静态方法示例: 11
静态方法示例: 18
静态方法示例: 18
静态方法示例: 18
普通方法示例: 30
对象 invoke 示例: 0.83333333333333

参考链接

<?php

/**
 * The callable types and normalizations are given in the table below:
 *
 *  Callable                        | Normalization                   | Type
 * ---------------------------------+---------------------------------+--------------
 *  function (...) use (...) {...}  | function (...) use (...) {...}  | 'closure'
 *  $object                         | $object                         | 'invocable'
 *  "function"                      | "function"                      | 'function'
 *  "class::method"                 | ["class", "method"]             | 'static'
 *  ["class", "parent::method"]     | ["parent of class", "method"]   | 'static'
 *  ["class", "self::method"]       | ["class", "method"]             | 'static'
 *  ["class", "method"]             | ["class", "method"]             | 'static'
 *  [$object, "parent::method"]     | [$object, "parent::method"]     | 'object'
 *  [$object, "self::method"]       | [$object, "method"]             | 'object'
 *  [$object, "method"]             | [$object, "method"]             | 'object'
 * ---------------------------------+---------------------------------+--------------
 *  other callable                  | idem                            | 'unknown'
 * ---------------------------------+---------------------------------+--------------
 *  not a callable                  | null                            | false
 *
 * If the "strict" parameter is set to true, additional checks are
 * performed, in particular:
 *  - when a callable string of the form "class::method" or a callable array
 *    of the form ["class", "method"] is given, the method must be a static one,
 *  - when a callable array of the form [$object, "method"] is given, the
 *    method must be a non-static one.
 *
 */
function callableType($callable, $strict = true, callable &$norm = null)
{
    if (! is_callable($callable)) {
        switch (true) {
            case is_object($callable):
                $norm = $callable;

                return 'Closure' === get_class($callable) ? 'closure' : 'invocable';
            case is_string($callable):
                $m = null;
                if (preg_match('~^(?<class>[a-z_][a-z0-9_]*)::(?<method>[a-z_][a-z0-9_]*)$~i', $callable, $m)) {
                    [$left, $right] = [$m['class'], $m['method']];
                    if (! $strict || (new \ReflectionMethod($left, $right))->isStatic()) {
                        $norm = [$left, $right];

                        return 'static';
                    }
                } else {
                    $norm = $callable;

                    return 'function';
                }
                break;
            case is_array($callable):
                $m = null;
                if (preg_match('~^(:?(?<reference>self|parent)::)?(?<method>[a-z_][a-z0-9_]*)$~i', $callable[1], $m)) {
                    if (is_string($callable[0])) {
                        if ('parent' === strtolower($m['reference'])) {
                            [$left, $right] = [get_parent_class($callable[0]), $m['method']];
                        } else {
                            [$left, $right] = [$callable[0], $m['method']];
                        }
                        if (! $strict || (new \ReflectionMethod($left, $right))->isStatic()) {
                            $norm = [$left, $right];

                            return 'static';
                        }
                    } else {
                        if ('self' === strtolower($m['reference'])) {
                            [$left, $right] = [$callable[0], $m['method']];
                        } else {
                            [$left, $right] = $callable;
                        }
                        if (! $strict || ! (new \ReflectionMethod($left, $right))->isStatic()) {
                            $norm = [$left, $right];

                            return 'object';
                        }
                    }
                }
                break;
        }
        $norm = $callable;

        return 'unknown';
    }
    $norm = null;

    return false;
}

原文链接

guanguans avatar May 18 '21 01:05 guanguans