【问题标题】:Magic method for when a class instance is called like a function?当类实例像函数一样被调用时的魔术方法?
【发布时间】:2012-03-24 09:20:18
【问题描述】:

我想做的是创建一个Comparable 类,类似于.NET 中的IComparable,这样您就可以像这样实例化它:

$cmp = new MultiArrayCompare(2);

然后您可以通过以下方式对数组进行排序:

usort($myArray, $cmp);

它会对第二个索引上的数组进行排序。为此,我想usort 会尝试像函数一样调用$cmp,所以我必须以某种方式覆盖该行为。 __call 看起来不像我想要的那样(想了一会儿,它就像 Python 的 __call__)。

如果这是不可能的...是否有另一种好方法来创建此问题的通用解决方案?您可以在哪里创建用户定义的排序函数,但给它传递一些值(在本例中为“2”)?


使用__invoke 我能够创建这些类:

abstract class Comparable {
    abstract function Compare($a, $b);

    function __invoke($a, $b) {
        return $this->Compare($a, $b);
    }
}

class ArrayCompare extends Comparable {
    private $key;

    function __construct($key) {
        $this->key = $key;  
    }

    function Compare($a, $b) {
        if($a[$this->key] == $b[$this->key]) return 0;
        return $a[$this->key] < $b[$this->key] ? -1 : 1;
    }
}

class ArrayCaseCompare extends Comparable {
    private $key;

    function __construct($key) {
        $this->key = $key;  
    }

    function Compare($a, $b) {
        return strcasecmp($a[$this->key], $b[$this->key]);
    }
}

我可以用来对数组数组进行排序:

$arr = array(
    array(1,2,3),
    array(2,3,4),
    array(3,2,4),
)

usort($arr,new ArrayCompare(1));

【问题讨论】:

    标签: php


    【解决方案1】:

    您正在寻找__invoke:

    当脚本尝试将对象作为函数调用时,会调用 __invoke() 方法。

    示例:

    class SortAscending
    {
        public function __invoke($a, $b)
        {
           return $a - $b;
        }
    }
    
    $numbers = array(4,7,2,3,9,1);
    usort($numbers, new SortAscending);
    print_r( $numbers );
    

    输出(demo):

    Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 7 [5] => 9 ) 
    

    更苗条的替代方案是使用闭包。你的代码就是:

    $arr = array(
        array(1,2,3),
        array(2,3,4),
        array(3,2,4),
    );
    
    $key = 1;
    usort($arr, function($a, $b) use ($key) {
        return $a[$key] - $b[$key];
    });
    

    或 - 可重用和可配置 (demo):

    $ArrayCompare = function ($index) {
        return function($a, $b) use ($index) {
            return $a[$index] - $b[$index];
        };
    };
    usort($arr, $ArrayCompare(1));
    

    除此之外,您基本上可以使用任何您想要的方法并在回调中指定方法:

    usort($arr, array(new ArrayCompare(1), 'Compare'));
    

    上面的方法不用魔法也能达到同样的效果。有关其他选项,请参阅 chapter on callbacks

    【讨论】:

    • 漂亮!不知道/忘记了它的存在。
    • 闭包很好,但它们不可重复使用(它们仅适用于 5.3+)。我预见我的ArrayCompare 课程会被多次使用:)
    • @Mark __invoke 也仅在 5.3 中添加。您可以将闭包分配给变量,例如$emptyFn = function() {} 然后你传递它,所以它是可重复使用的:)
    • 触摸。尽管如此,仍然不能 as 重复使用。该函数将永久绑定到该变量(如类实例),因此您无法使用相同的方法体轻松创建新函数。
    • 天啊.. 好吧,.. 如果你想使用全局变量,你仍然在使用它......怪物......无处不在 ;)
    【解决方案2】:

    为什么不创建一个提供一组静态compareXXX() 方法的Comparer 类?

    在我看来,考虑到面向对象的方法,这看起来更简洁......

    【讨论】:

    • 我要问的是面向对象的......静态方法不起作用,因为我需要访问实例变量(在我的示例中为 2)。跨度>
    【解决方案3】:

    也许您正在寻找__invoke()

    手册示例:

    class CallableClass
    {
        public function __invoke($x)
        {
            var_dump($x);
        }
    }
    $obj = new CallableClass;
    $obj(5);
    var_dump(is_callable($obj));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-10
      • 2023-03-14
      • 2014-10-28
      • 2015-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多