Was mich schon oft bei PHP ein wenig störte ist, dass es zum Sortieren nicht so schöne Comparatoren wie in Java gibt.
Nun habe ich in der Vergangenheit ja schon das eine oder andere über Closures in PHP und Java Comparatoren für PHP geschrieben und wollte das nun mal zusammenbringen.
Einige PHP-Sortierfunktionen erlauben Callbacks, um die Reihenfolge zu bestimmen. Da ich das Ganze aber gerne in gut testbaren OOP-Einheiten hätte, müssen wir einen kleinen Umweg gehen.
Zunächst das obligatorische Interface.
namespace E7; interface Comparator { public function compare($a, $b); }
Diese Schnittstelle implementieren wir mit einer abstrakten Klasse.
namespace E7; class BaseComparator implements Comparator { public function compare($a, $b) { if ($a == $b) { return 0; } return $a < $b ? -1 : 1; } public function __invoke() { return call_user_func_array(array($this, 'compare'), func_get_args()); } }
Und schon haben wir einen einfachen, und zugegebenermaßen nicht typsicheren, Comparator.
Durch die magische Methode __invoke() kann ein Objekt nun wie eine Callbackfunktion verwendet werden.
Für die Sortierung von Objekten kann das dann einfach erweitert werden.
class PersonComparator extends BaseComparator { public function compare($a, $b) { // hier Prüfung auf Typ etc. return parent::compare($a->getId(), $b->getId()); } }
Um die Sortierreihenfolge negieren zu können, können wir einen einfachen Wrapper nutzen.
class ReverseOrder implements Comparator { private $_comparator; public function __construct(Comparator $comparator) { $this->_comparator = $comparator; } public function compare($a, $b) { return $this->_comparator->compare($a, $b) * -1; } }
Beispiel…
//-- $people = array(); for ($i = 0; $i < 10; $i ++) { $people[] = new Person(); } echo 'BEFORE<br/>'; foreach($people as $person) { echo "$person<br/>"; } $comparator = new ReverseOrder( new PersonComparator() ); uasort($people, function($a, $b) use ($comparator) { return $comparator->compare($a, $b); }); // oder uasort($people, $comparator); echo 'AFTER<br/>'; foreach($people as $person) { echo "$person<br/>"; }
31.08.2012 um 10:13 Uhr
Aber wofür genau wird nun __invoke benötigt? Oder habe ich was übersehen?
31.08.2012 um 12:42 Uhr
Upps… Das Wichtigste fehlte im Beispiel. Ich habe den letzten Codeteil entsprechend erweitert. Wir können das Objekt nun direkt als Callback übergeben. Danke für den Hinweis.