Sortieren mit PHP5 Closures und dem Java Comparator Pattern

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/>";
}

 

 

 

2 Kommentare

  1. Aber wofür genau wird nun __invoke benötigt? Oder habe ich was übersehen?

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

Schlagwörter: Adapter, Amazon, Animation, Annotations, Anonyme Klasse, Ant, Apache, API, Array, ArrayAccess, Attachment, AutoLoader, Bedienung, Bedingung, Benchmark, Bildbearbeitung, BOM, Bootstrap, Bot, Byte Order Mark, Callback, CamelCase, Canvas, Captcha, Cheatsheet, CLI, Closure, Cloud, CodeSniffer, Community, Comparator, Contest, Controller, Converter, CouchDB, Countable, Cronjob, CSV, CustomLibrary, Custom_Model, Data Mapper, Datei, Datenbank, Datenstruktur, Datentypen, Dating, Decorator, Dekorierer, Design Patterns, Dump, Duplikat, each, Eclipse, Entwicklung, Entwurfsmuster, Enum, Erweiterung, Eventhandling, Exception-Handling, Extension, Factory, Fehler, Flash, Foreach, Formatierung, Formular, Funktion, Futon, Header, HTML5, HTTP, IDE, If, Implementierung, InnoDB, Interceptor, Interface, isset, Iterator, Java, JavaScript, jQuery, Konfiguration, Konsole, Kontrollstruktur, kopieren, Late Static Binding, Layout, Linux, Listeners, Logging, Löschen, Magento, Magic Methods, Marketing, Methode, Model, MVC, MySQL, NetBeans, Objekt, Observable, Observer, OOP, Operator, Parameter, Partnersuche, Performance, PHP, phpMyAdmin, PHPUnit, Plugin, Proxy, Qualitätssicherung, Query, Reflection, Request, Response, Rest-API, Rockstar, Routing, S3, Samba, Scheifen, Schleife, Schutz, Secure Shell, Selbstreferenz, Shop, Sicherheit, Sicherung, Singleton Pattern, SOAP, Sortierung, Sourcecode, Spam, Speicherproblem, Spickzettel, SPL, SSH, Statement, Stellvertreter, Strategy Pattern, Stream, String, Sun VirtualBox, Support, Switch, Symfony, Symfony2, Symfony Live, Tag, Template, Template Method, Ternär Operator, Testing, Thumbnail, Tool, Tour, Twig, Type-Cast, Umwandlung, Underscore, unset, Vererbung, Verzweigung, Video, Videospiel, Virtualisierung, Visitor Pattern, Vorschaubild, walk, Webserver, Webservice, Weiterleitung, Wrapper, Youtube, Zeitsteuerung, Zend Framework, Zend_Cloud, Zend_CodeGenerator, Zend_Http_Client, Zend_Service, Zugriffsmethode