mpen
mpen

Reputation: 282865

Magic method for when a class instance is called like a function?

What I want to do is create a Comparable class, similar to IComparable in .NET, such that you can instantiate it like so:

$cmp = new MultiArrayCompare(2);

And then you can sort an array via:

usort($myArray, $cmp);

And it will sort an array of arrays on the 2nd index. To do that, I imagine usort would try to call $cmp like a function, so I'd have to override that behaviour somehow. It doesn't look like __call does what I want (thought for a minute it was like Python's __call__).

If this isn't possible... is there another nice way to create a general solution to this problem? Where you can create a user-defined sorting function but give pass it some value ("2" in this case)?


Using __invoke I was able to create these classes:

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]);
    }
}

Which I can use to sort an array of arrays:

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

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

Upvotes: 2

Views: 179

Answers (3)

Gordon
Gordon

Reputation: 316969

You are looking for __invoke:

The __invoke() method is called when a script tries to call an object as a function.

Example:

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 );

Output (demo):

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

The slimmer alternative is using a Closure. Your code would simply be:

$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];
});

or - reusable and configurable (demo):

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

Apart from that, you can use basically any method you want and specify the method in the callback:

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

The above would achieve the same without magic. See the chapter on callbacks for additional options.

Upvotes: 5

CodeZombie
CodeZombie

Reputation: 5377

Why you don't create a Comparer class that provides a set of static compareXXX() methods?

To me this looks much cleaner with an object oriented approach in mind...

Upvotes: 1

bfavaretto
bfavaretto

Reputation: 71918

Maybe you are looking for __invoke()?

Example from the manual:

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

Upvotes: 3

Related Questions