Hier im Forum bekommt ihr bei euren fragen schnelle hilfe.Hier geht es rund um das Web SeitenProgrammieren.Alles rund ums Javascript,Html,Php,Css und Sql.Auf fast allen Fragen haben wir eine Antwort.
Der Soforthilfe-chat verspricht das ,was sein Name sagt. Hier sind Leute Online die sofort ihre hilfe anbieten.Seht in der OnlineListe nach und wenn einer Online ist werdet ihr auch antwort bekommen. Admine ,Moderatoren und Helfer sind unsere Spezialisten in Sachen Web Programierung

Sie sind hier : sebastian1012.bplaced.net/ homepage-neu / kreuz-und-quer / tutorials-info-neuigkeiten-php / index.php

Random Php Themen, Tutorials und co

24.09.2019 03:24:11

Richtiger Umgang mit Exceptions?

Exceptions dienen dazu einen Fehlerfall zu signalisieren, der aber auf Wunsch vom Entwickler aufgefangen und dadurch die weitere Verarbeitung des Scripts fortgesetzt werden kann. So weit die Theorie, in der Praxis haben sich mir allerdings einige Fragestellungen ergeben.

Umsetzung von Exceptions in PHP

Exceptions werden in PHP mit der Basisklasse Exception und von dieser Klasse abgeleiteten Klassen erzeugt. Eine Exception, die nicht aufgefangen wird, endet in einem Fatal Error, was den Scriptabbruch zur Folge hat:

throw new Exception('Exception message');

Diese Klasse kann natürlich wie jede andere PHP-Klasse erweitert werden:

class CustomException extends Exception { }   throw new CustomException ('Exception message');

Wenn man den aufgetretenen Fehler behandeln und anschließend die Scriptausführung fortsetzen möchte, kann man Exceptions auffangen:

try {   // Code, der eine Exception werfen könnte } catch(CustomException $e) {   // Fehlerbehandlung }

Was ist eine Ausnahme?

Da Exceptions schnell missbraucht werden können, indem sie zur Programmablaufsteuerung benutzt werden, bedarf es einer Definition, was überhaupt eine Ausnahme ist oder anders: Wann sollte man überhaupt eine Exception werfen?
Eine Ausnahme ist dann eine Ausnahme, wenn eines der folgenden Merkmale zutrifft:

  • Die Ausführbarkeit des folgenden Codes hat bestimmte Vorbedingungen (z.B. ein übergebener String darf nicht leer sein, der Datentyp muss den Erwartungen entsprechen).
  • Eine Funktion prüft vor der Rückgabe, ob das zurück zu gebende Ergebnis valide ist (richtiger Datentyp, Integrität der Daten gewährleistet).

Wenn beide Fälle nicht zutreffen, sollte keine Exception geworfen werden.
Klingt erstmal logisch, in der Praxis steht man allerdings hin und wieder vor schwierigen Entscheidungen. Ein Beispiel:

class UserFinder {   public function findUserById($id) {     $user = ... // User-Datensatz aus der Datenbank holen, der die ID $id hat       if($user === null) {       throw new UnderflowException('Could not find a user with id '.$id);     }   } }

Theoretisch wäre es nicht nötig hier eine Exception zu werfen. Es könnte genauso gut null zurück gegeben werden.
In meinen Augen ist für diese Entscheidung wichtig, welcher Rückgabetyp per DocTag angegeben wurde. Ist es @return User, dann wäre eine Exception sinnvoll. Allerdings stellt null auch hier immer einen Sonderfall dar (wie auch bei TypeHints als Vorbedingung: Selbst wenn man per TypeHint eine Klasse definiert, kann immer auch null übergeben werden Wenn TypeHints bei der Funktionsdeklaration benutzt werden, kann null nicht übergeben werden, danke @Fabian_ikono). Was denkt ihr, ist in diesem Fall das Werfen einer Exception sinnvoll?

Wie viele Exception-Klassen sollte man erstellen?

Ein anderes Problem stellt die Spezifität einer Exception-Klasse dar. PHP bringt über die Standard PHP Library (SPL) bereits einige Exceptions mit. Hier gibt bereits für viele Fehlerfälle vorgefertigte Exception-Klassen, die man einfach nutzen kann.

Gehen wir einmal davon aus, dass wir uns dafür entschieden haben, ausschließlich die SPL-Exceptions zu nutzen. Wir haben eine Model-Klasse mit Setter-Methoden und in allen Setter-Methoden wird eine InvalidArgumentException geworfen, wenn der übergebene Wert nicht zu dem erwarteten passt, also eine Vorbedingung nicht erfüllt ist (oder anders gesagt: Würde man keine Exception werfen, entstünde ein inkonsistenter Zustand).

class Book {   protected $title;   protected $pages;     public function setTitle($title) {     if(empty($title)) {       throw new InvalidArgumentException('Book title must not be empty');     }       $this->title = $title;   }     public function setPages($pages) {     if(!is_int($pages)) {       throw new InvalidArgumentException('Page count must be an integer number');     }       $this->pages = $pages;   } }

Ein Problem entsteht nun, wenn wir beide Setter in einem Try-Catch-Block aufrufen:

$book = new Book(); try {   $book->setTitle($_GET['title']);   $book->setPages($_GET['pages']); } catch(InvalidArgumentException $e) {   echo $e->getMessage(); }

Das Problem ist, dass wir nun zur Laufzeit nicht wissen, welcher der beiden Befehle fehlgeschlagen ist, weil beide die gleiche Exception werfen. Es gibt nun 3 Möglichkeiten, dieses Dilemma zu umgehen:

  • man macht 2 Try-Catch-Blöcke, sodass man immer weiß, welche Operation fehlgeschlagen ist
  • man erstellt 2 unterschiedliche Exception-Klassen, die beide im gleichen Try-Catch-Block abgefangen werden können
  • man nutzt den Fehler-Code, den man einer Exception zusätzlich mitgeben kann

Bei 2 Try-Catch-Blöcken hat man das Problem, dass man auch immer an eben diese Festlegung denken müsste. Noch schlimmer: Es kann passieren, dass in dem aufgerufenen Code weitere Funktionen aufgerufen werden und an irgendeiner Stelle eine InvalidArgumentException geworfen wird. Diesen Fehler wollte man eventuell gar nicht auffangen oder ihn zumindest loggen, da er eine tiefer liegende Ursache hat. Ein tiefer liegender Fehler würde somit ggf. kaschiert.

Die Variante verschiedene Exception-Klassen zu erstellen ist eleganter, allerdings ist hier die Frage, wie weit man das treiben möchte. Soll für jeden Fehlerfall eine eigene Exception-Klasse erstellt werden, sodass man stets genau weiß, an welcher Stelle der Fehler aufgetreten ist und man auf keinen Fall ungewollt einen anderen Fehler kaschiert? In diesem Fall würde die Anzahl an Exception-Klassen schnell sehr groß werden.
Ein Mittelweg könnte in diesem Fall das Schreiben von schichtenabhängigen Exceptions sein, auch bekannt unter Exception Chaining. Jede Schicht darf nur die Exceptions der direkt darunter liegenden Schicht auffangen. Wenn zum Beispiel eine Datei eingelesen werden soll, ist es der View-Klasse relativ egal, ob die Datei keine Leserechte (FilePermissionException) hat oder die Datei kaputt (FileDamagedException) ist. Für die View-Klasse ist nur wichtig, dass die Datei nicht lesbar ist. Deshalb müsste der Controller die genannten Exceptions auffangen und anschließend eine weitere Exception werfen. An diese neue Exception könnte man die ursprüngliche Exception anhängen (der 3. Konstruktorparameter der Exception-Klasse).

Und dann könnte man die Zuordnung noch über den Fehlercode lösen, aber damit habe ich noch nie gearbeitet. Habt ihr damit Erfahrungen?

Ich freue mich auf eure Meinung dazu in den Kommentaren!

Wohin packt man die Exception-Klassen?

Die zweite grundlegende Frage betrifft den Ort, an dem man die Exception-Klassen ablegt. Folgende Varianten sind mir in den Sinn gekommen:

  • man packt die Exception-Klassen in die gleiche Datei, in der sie auch geworfen werden.
  • Exception-Klassen kommen in eine separate Datei und liegen
    • im gleichen Ordner wie die aufrufende Klasse
    • im Ordner der Anwendungsschicht (Model-Exceptions, Routing-Exceptions usw.)
    • in einem globalen Exception-Ordner, der im obersten Quellcode-Ordner liegt
  • alle Exception-Klassen kommen in eine Datei

Meine präferierte Lösung war bisher immer die Exception-Klassen in separate Dateien und in einen Ordner namens „exceptions“ zu legen, der unter dem Ordner für die jeweilige Schicht liegt (jede Schicht hat Ihre eigenen Exceptions).
Auch zu diesem Thema bin ich auf eure Kommentare gespannt!

php

24.09.2019 04:08:54

Composer, Satis, Satisfy: Fremdbibliotheken von Composer, Packagist und Github entkoppeln

+%09%09%3Cp%3EIn+der+heutigen+Webentwicklung+m%26%23252%3Bssen+wir+das+Rad+nicht+st%26%23228%3Bndig+neu+erfinden.+Es+gibt+eine+Vielzahl+von+Bibliotheken+%26%238211%3B+sowohl+server-+als+auch+clientseitig+%26%238211%3B+die+einem+das+Entwicklerleben+vereinfachen.+Damit+wir+aber+nicht+bei+s%26%23228%3Bmtlichen+Fremdbibliotheken+regelm%26%23228%3B%26%23223%3Big+nachpr%26%23252%3Bfen+m%26%23252%3Bssen%2C+ob+es+eine+neue+Version+gibt%2C+gibt+es+Paketverwaltungen.+Im+PHP-Bereich+ist+%3Ca+href%3D%22https%3A%2F%2Fgetcomposer.org%2F%22%3EComposer%3C%2Fa%3E+der+Quasi-Standard+f%26%23252%3Br+diese+Aufgabe.%3Cbr+%2F%3E+Dieser+Beitrag+beschreibt%2C+wie+wir+mit+Composer+komfortabel+%28%3D+inkl.+%3Ca+href%3D%22https%3A%2F%2Fgetcomposer.org%2Fdoc%2Farticles%2Fversions.md%22%3EVersion+Constraints%3C%2Fa%3E%29+auch+Bibliotheken+einbinden+k%26%23246%3Bnnen%2C+die%3A%3C%2Fp%3E+%3Cul%3E+%3Cli%3Enicht+auf+Packagist+zu+finden+sind+%28z.B.+private+Bibliotheken%29+und+%2F+oder%3C%2Fli%3E+%3Cli%3Ekeine+composer.json+haben+oder+in+fr%26%23252%3Bheren+Versionen+nicht+hatten%3C%2Fli%3E+%3C%2Ful%3E+%3Cp%3E%3Cspan+id%3D%22more-1416%22%3E%3C%2Fspan%3E%3C%2Fp%3E+%3Cp%3EDie+meisten+PHP-Bibliotheken+sind+bei+%3Ca+href%3D%22https%3A%2F%2Fpackagist.org%2F%22%3EPackagist%3C%2Fa%3E+%28dem+Standard-Repository+von+Composer%29+gelistet+und+k%26%23246%3Bnnen+dadurch+ohne+weitere+Einstellungen+in+der+composer.json+%3Ca+href%3D%22https%3A%2F%2Fgetcomposer.org%2Fdoc%2F01-basic-usage.md%23the-require-key%22%3Eunter+%26%238222%3Brequire%26%238220%3B+angegeben+werden%3C%2Fa%3E.%3C%2Fp%3E+%3Cp%3EBei+Bibliotheken%2C+die+nicht+bei+Packagist+gelistet+sind%2C+m%26%23252%3Bssen+wir+Composer+mitteilen%2C+wo+es+die+Pakete+finden+kann%3A+Wir+m%26%23252%3Bssen+die+Repositorys+selbst+definieren.%3Cbr+%2F%3E+F%26%23252%3Br+die+Einbindung+%26%23252%3Bber+Github+haben+wir+dann+z.B.+viele+Bl%26%23246%3Bcke+wie+z.B.+folgenden%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brepositories%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2391%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bname%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bversion%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3B2.0.0%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsource%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Burl%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bgit%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Breference%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bv2.0.0%26quot%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2393%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%26quot%3Brepositories%26quot%3B%3A+%5B++++++%7B++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bpackage%26quot%3B%2C++++++++%26quot%3Bpackage%26quot%3B%3A+%7B++++++++++%26quot%3Bname%26quot%3B%3A+%26quot%3Bsciactive%2Fpnotify%26quot%3B%2C++++++++++%26quot%3Bversion%26quot%3B%3A+%26quot%3B2.0.0%26quot%3B%2C++++++++++%26quot%3Bsource%26quot%3B%3A+%7B++++++++++++%26quot%3Burl%26quot%3B%3A+%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%2C++++++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bgit%26quot%3B%2C++++++++++++%26quot%3Breference%26quot%3B%3A+%26quot%3Bv2.0.0%26quot%3B++++++++++%7D++++++++%7D++++++%7D++%5D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3EDie+hier+angegebene+Versionsnummer+2.0.0+basiert+auf+den+%3Ca+href%3D%22https%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%2Ftags%22%3EGit-Tags%3C%2Fa%3E+der+Bibliothek.%3C%2Fp%3E+%3Cp%3EDas+Problem+ist%2C+dass+wir+die+Version+fest+definiert+haben.+Composer+w%26%23252%3Brde+uns+immer+nur+genau+diese+Version+liefern.+Wir+m%26%23252%3Bssten+uns+demzufolge+selbst+darum+k%26%23252%3Bmmern%2C+zu+gucken%2C+ob+es+eine+neue+Version+der+Bibliothek+gibt+und+dann+das+Composer-Repository+ggf.+anpassen.+Viel+sch%26%23246%3Bner+w%26%23228%3Bre+es+ja+aber%2C+wenn+man+Bugfix-Releases+%28und+manchmal+auch+Minor-Releases%29+automatisch+beim+%3Ca+href%3D%22https%3A%2F%2Fgetcomposer.org%2Fdoc%2F03-cli.md%23update%22%3EComposer+Update%3C%2Fa%3E+bek%26%23228%3Bme.%3Cbr+%2F%3E+Leider+kann+man+bei+der+Definition+der+Composer-Repositorys+aber+die+Version+nicht+durch+ein+Sternchen+setzen%2C+sondern+dort+wird+immer+ein+existierendes+Tag+%28oder+ein+Branch+oder+Commit%29+erwartet.%3C%2Fp%3E+%3Cp%3EUnd+hier+kommt+%3Ca+href%3D%22https%3A%2F%2Fgithub.com%2Fcomposer%2Fsatis%22%3ESatis%3C%2Fa%3E+ins+Spiel.+Satis+stellt+ein+Composer-Repository+zur+Verf%26%23252%3Bgung%2C+%26%23228%3Bhnlich+wie+Packagist%2C+mit+dem+Unterschied%2C+dass+es+auf+unseren+Servern+l%26%23228%3Buft+und+wir+selbst+bestimmen+k%26%23246%3Bnnen%2C+welche+Bibliotheken+darin+enthalten+sein+sollen+%28und+welche+Versionen%29.%3Cbr+%2F%3E+Satis+hat+dadurch+einige+Vorteile%3A%3C%2Fp%3E+%3Cul%3E+%3Cli%3EEinbindung+von+nicht+%26%23246%3Bffentlichen+Repositorys%3C%2Fli%3E+%3Cli%3EEinbindung+von+Bibliotheken%2C+die+nicht+bei+Packagist+gelistet+sind%3C%2Fli%3E+%3Cli%3ENutzung+von+Bibliotheken%2C+die+keine+Composer.json+haben+%28mit+%3Ca+href%3D%22https%3A%2F%2Fgithub.com%2FFitchLearning%2Fsatisfy%22%3ESatisfy%3C%2Fa%3E%2C+dazu+weiter+unten+mehr%29%3C%2Fli%3E+%3Cli%3EUnabh%26%23228%3Bngigkeit+von+Github+und+Packagist+w%26%23228%3Bhrend+des+Builds+%28falls+diese+Seiten+mal+offline+sind+oder+man+selbst+kein+Internet+hat%29%3C%2Fli%3E+%3C%2Ful%3E+%3Cp%3EUm+Satis+einzurichten+m%26%23252%3Bssen+wir+zuerst+Satis+als+eigenes+Composer-Projekt+herunterladen%3A%3Cbr+%2F%3E+%3Cpre%3Ephp+composer.phar+create-project+composer%2Fsatis+--stability%3Ddev%3C%2Fpre%3E%3C%2Fp%3E+%3Cp%3EAnschlie%26%23223%3Bend+erstellen+wir+eine+grundlegende+Konfiguration+%28satis.json%29+innerhalb+des+gerade+erstellten+Projektes%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bname%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3BOur+Satis+repository%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhomepage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttp%3A%2F%2Flocalhost%2Fsatis%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brequire-all%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23003366%3B+font-weight%3A+bold%3B%22%3Etrue%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%7B++++++%26quot%3Bname%26quot%3B%3A+%26quot%3BOur+Satis+repository%26quot%3B%2C++++++%26quot%3Bhomepage%26quot%3B%3A+%26quot%3Bhttp%3A%2F%2Flocalhost%2Fsatis%26quot%3B%2C++++++%26quot%3Brequire-all%26quot%3B%3A+true++%7D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3EDies+definiert%2C+lediglich%2C+wie+unser+Repository+hei%26%23223%3Bt+%28%3Ccode%3Ename%3C%2Fcode%3E%29+und+wie+man+es+%26%23252%3Bber+den+Browser+aufrufen+kann+%28%3Ccode%3Ehomepage%3C%2Fcode%3E%29.%3Cbr+%2F%3E+require-all+bedeutet%2C+dass+Satis+s%26%23228%3Bmtliche+Versionen+der+Bibliotheken+laden+soll+%26%238211%3B+dies+ist+die+Standardeinstellung%2C+denn+schlie%26%23223%3Blich+soll+ja+in+der+composer.json+des+eigentlichen+Projektes+sp%26%23228%3Bter+die+genaue+Version+festgelegt+werden+k%26%23246%3Bnnen.%3C%2Fp%3E+%3Cp%3ENun+m%26%23252%3Bssen+wir+Satis+noch+mitteilen%2C+welche+Bibliotheken+wir+ben%26%23246%3Btigen.+F%26%23252%3Br+Repositorys%2C+die+eine+composer.json+haben%2C+kann+dies+direkt+in+der+satis.json+definiert+werden.%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brepositories%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2391%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bcomposer%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Burl%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttps%3A%2F%2FURL-of-private-Gitlab-Repository%26quot%3B%3C%2Fspan%3E++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2393%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%26quot%3Brepositories%26quot%3B%3A+%5B++++++%7B+++++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bcomposer%26quot%3B%2C+++++++++++%26quot%3Burl%26quot%3B%3A+%26quot%3Bhttps%3A%2F%2FURL-of-private-Gitlab-Repository%26quot%3B+++++++%7D++%5D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3EProjekte%2C+die+keine+composer.json+haben+%28wie+viele+JS-Bibliotheken%2C+weil+Client-Entwickler+wiederum+einen+%3Ca+href%3D%22http%3A%2F%2Fbower.io%2F%22%3Eeigenen+Paketmanager%3C%2Fa%3E+haben%29+k%26%23246%3Bnnen+mittels+%3Ca+href%3D%22https%3A%2F%2Fgithub.com%2FFitchLearning%2Fsatisfy%22%3ESatisfy%3C%2Fa%3E+hinzugef%26%23252%3Bgt+werden.+Satisfy+analysiert+dazu+alle+Git-Tags+der+definierten+Bibliotheken+und+erstellt+daf%26%23252%3Br+einzelne+Eintr%26%23228%3Bge+in+der+Satis-Konfigurationsdatei.%3C%2Fp%3E+%3Cp%3EDamit+Satisfy+wei%26%23223%3B%2C+welche+Git-Repositories+es+einlesen+soll%2C+ben%26%23246%3Btigen+wir+eine+packagelist.json%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Burl%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bminversion%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3B2.0%26quot%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++...+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%7B++++++%26quot%3Bsciactive%2Fpnotify%26quot%3B%3A+%7B++++++++++%26quot%3Burl%26quot%3B%3A+%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%2C++++++++++%26quot%3Bminversion%26quot%3B%3A+%26quot%3B2.0%26quot%3B++++++%7D%2C++++++...++%7D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3EAus+der+urspr%26%23252%3Bnglichen+satis.json+und+aus+der+packagelist.json+kann+Satisfy+nun+eine+erweiterte+Satis-Konfiguration+erstellen%3A%3Cbr+%2F%3E+%3Cpre%3Esatisfy+--repofile+satis.json+--packagefile+packagelist.json+--output+satis.expanded.json%3C%2Fpre%3E%3Cbr+%2F%3E+Die+entstehende+satis.expanded.json+enth%26%23228%3Blt+s%26%23228%3Bmtliche+Git-Tags+aller+angegebenen+Git-Repositorys+als+einzelne+Satis-Repositorys%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brepositories%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2391%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bname%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bversion%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3B2.0.0%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsource%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Burl%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bgit%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Breference%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bv2.0.0%26quot%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bname%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bversion%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3B2.0.1%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsource%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Burl%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bgit%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Breference%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bv2.0.1%26quot%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+...+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2393%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%26quot%3Brepositories%26quot%3B%3A+%5B++++++%7B++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bpackage%26quot%3B%2C++++++++%26quot%3Bpackage%26quot%3B%3A+%7B++++++++++%26quot%3Bname%26quot%3B%3A+%26quot%3Bsciactive%2Fpnotify%26quot%3B%2C++++++++++%26quot%3Bversion%26quot%3B%3A+%26quot%3B2.0.0%26quot%3B%2C++++++++++%26quot%3Bsource%26quot%3B%3A+%7B++++++++++++%26quot%3Burl%26quot%3B%3A+%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%2C++++++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bgit%26quot%3B%2C++++++++++++%26quot%3Breference%26quot%3B%3A+%26quot%3Bv2.0.0%26quot%3B++++++++++%7D++++++++%7D++++++%7D%2C++++++%7B++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bpackage%26quot%3B%2C++++++++%26quot%3Bpackage%26quot%3B%3A+%7B++++++++++%26quot%3Bname%26quot%3B%3A+%26quot%3Bsciactive%2Fpnotify%26quot%3B%2C++++++++++%26quot%3Bversion%26quot%3B%3A+%26quot%3B2.0.1%26quot%3B%2C++++++++++%26quot%3Bsource%26quot%3B%3A+%7B++++++++++++%26quot%3Burl%26quot%3B%3A+%26quot%3Bhttps%3A%2F%2Fgithub.com%2Fsciactive%2Fpnotify%26quot%3B%2C++++++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bgit%26quot%3B%2C++++++++++++%26quot%3Breference%26quot%3B%3A+%26quot%3Bv2.0.1%26quot%3B++++++++++%7D++++++++%7D++++++%7D++...++%5D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3ENun+m%26%23252%3Bssen+wir+Satis+nur+noch+anweisen%2C+die+satis.expanded.json+einzulesen%3A%3Cbr+%2F%3E+%3Cpre%3Ephp+satis+build+satis.expanded.json+%26lt%3Bper+HTTP+erreichbares+Verzeichnis%26gt%3B%3C%2Fpre%3E%3C%2Fp%3E+%3Cp%3EAngenommen%2C+wir+nutzen+Apache+und+haben+einen+Link+namens+%26%238222%3Bsatis%26%238220%3B+auf+das+per+HTTP+erreichbare+Verzeichnis+erstellt%2C+k%26%23246%3Bnnen+wir+im+Browser+http%3A%2F%2Flocalhost%2Fsatis+aufrufen+und+sehen+eine+%26%23220%3Bbersicht+%26%23252%3Bber+s%26%23228%3Bmtliche+Bibliotheken+und+deren+Versionen%2C+die+nun+zur+Verf%26%23252%3Bgung+stehen+%28so+%26%23228%3Bhnlich+wie+auf+Packagist%29.+Nat%26%23252%3Brlich+macht+es+mehr+Sinn+Satis+auf+einen+zentralen+Server+zu+packen%2C+damit+alle+darauf+zugreifen+k%26%23246%3Bnnen%2C+aber+f%26%23252%3Br+Testzwecke+reicht+auch+erstmal+eine+lokale+Installation.%3C%2Fp%3E+%3Cp%3ENun+k%26%23246%3Bnnen+wir+die+composer.json+unseres+Projektes+%26%23246%3Bffnen+und+auf+unser+Satis-Repository+verweisen%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brepositories%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2391%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btype%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bcomposer%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Burl%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttp%3A%2F%2Flocalhost%2Fsatis%26quot%3B%3C%2Fspan%3E+++++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bpackagist%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23003366%3B+font-weight%3A+bold%3B%22%3Efalse%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%2393%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%26quot%3Brepositories%26quot%3B%3A%5B++++++%7B++++++++%26quot%3Btype%26quot%3B%3A+%26quot%3Bcomposer%26quot%3B%2C++++++++%26quot%3Burl%26quot%3B%3A+%26quot%3Bhttp%3A%2F%2Flocalhost%2Fsatis%26quot%3B++++++++%7D++++++%7D%2C++++++%7B++++++++++%26quot%3Bpackagist%26quot%3B%3A+false++++++%7D++%5D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3EDas+%3Ccode%3E%26quot%3Bpackagist%26quot%3B%3A+false%3C%2Fcode%3E+bewirkt%2C+dass+Composer+bei+fehlenden+Paketen+nicht+auf+Packagist+nachsieht.+Dadurch+hat+man+die+komplette+Kontrolle+%26%23252%3Bber+alle+eingebundenen+Fremdbibliotheken%2C+wenn+Satis+sinnvollerweise+das+einzige+Composer-Repository+ist.%3C%2Fp%3E+%3Cp%3ENun+k%26%23246%3Bnnen+wir+auf+s%26%23228%3Bmtliche+Bibliotheken+und+Versionen%2C+die+unter+http%3A%2F%2Flocalhost%2Fsatis+aufgelistet+sind%2C+im+require-Block+zugreifen%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brequire%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bsciactive%2Fpnotify%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3B2.%2A%26quot%3B%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%26quot%3Brequire%26quot%3B%3A+%7B++++++%26quot%3Bsciactive%2Fpnotify%26quot%3B%3A+%26quot%3B2.%2A%26quot%3B++%7D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3EMan+beachte+das+Sternchen+bei+der+Versionsangabe.+Obwohl+die+JS-Bibliothek+PNotify+keine+composer.json+hat+und+auch+nicht+bei+Packagist+gelistet+ist%2C+k%26%23246%3Bnnen+wir+die+%3Ca+href%3D%22https%3A%2F%2Fgetcomposer.org%2Fdoc%2Farticles%2Fversions.md%22%3EVersion+Constraints%3C%2Fa%3E+von+Composer+nutzen.+Es+funktioniert+nat%26%23252%3Brlich+genauso+f%26%23252%3Br+Bibliotheken%2C+die+eine+composer.json+haben+und+trotzdem+nicht+bei+Packagist+sind.%3C%2Fp%3E+%3Cp%3EEinen+weiteren+Vorteil+hat+die+Nutzung+von+Satis+noch%3A+Es+k%26%23246%3Bnnte+vorkommen%2C+dass+man+gerade+eine+neue+Version+bauen+m%26%23246%3Bchte%2C+aber+Github+oder+Packagist+gerade+nicht+erreichbar+sind.+In+diesem+Fall+k%26%23246%3Bnnen+wir+keine+neue+Version+erstellen%2C+was+unter+Umst%26%23228%3Bnden+teuer+sein+kann.%3Cbr+%2F%3E+Satis+kann+als+Proxy+f%26%23252%3Br+Github+und+Packagist+fungieren%2C+indem+es+per+Cronjob+regelm%26%23228%3B%26%23223%3Big+die+Github-Repositorys+%26%23252%3Bberpr%26%23252%3Bft+und+neue+Versionen+herunterl%26%23228%3Bdt.+Wird+sp%26%23228%3Bter+eine+bestimmte+Version+per+Composer+angefordert%2C+kann+auf+die+zwischengespeicherte+Version+zur%26%23252%3Bckgegriffen+werden+und+man+ist+somit+unabh%26%23228%3Bngig+von+Github+oder+Packagist.%3C%2Fp%3E+%3Cp%3EF%26%23252%3Br+die+Archivierung+von+Versionen+ist+folgende+Erg%26%23228%3Bnzung+in+der+satis.json+n%26%23246%3Btig%3A%3C%2Fp%3E++%3Cdiv+class%3D%22wp_syntax%22+style%3D%22position%3Arelative%3B%22%3E%3Ctable%3E%3Ctr%3E%3Ctd+class%3D%22code%22%3E%3Cpre+class%3D%22javascript%22+style%3D%22font-family%3Amonospace%3B%22%3E%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bname%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3BOur+Satis+repository%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhomepage%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bhttp%3A%2F%2Flocalhost%2Fsatis%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brequire-all%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23003366%3B+font-weight%3A+bold%3B%22%3Etrue%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brequire-dependencies%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23003366%3B+font-weight%3A+bold%3B%22%3Etrue%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Brequire-dev-dependencies%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23003366%3B+font-weight%3A+bold%3B%22%3Etrue%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Barchive%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23123%3B%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bdirectory%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bdist%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bformat%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Btar%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%2C%3C%2Fspan%3E+++++++++%3Cspan+style%3D%22color%3A+%233366CC%3B%22%3E%26quot%3Bskip-dev%26quot%3B%3C%2Fspan%3E%3Cspan+style%3D%22color%3A+%23339933%3B%22%3E%3A%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23003366%3B+font-weight%3A+bold%3B%22%3Etrue%3C%2Fspan%3E+++++%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E+%3Cspan+style%3D%22color%3A+%23009900%3B%22%3E%26%23125%3B%3C%2Fspan%3E%3C%2Fpre%3E%3C%2Ftd%3E%3C%2Ftr%3E%3C%2Ftable%3E%3Cp+class%3D%22theCode%22+style%3D%22display%3Anone%3B%22%3E%7B++++++%26quot%3Bname%26quot%3B%3A+%26quot%3BOur+Satis+repository%26quot%3B%2C++++++%26quot%3Bhomepage%26quot%3B%3A+%26quot%3Bhttp%3A%2F%2Flocalhost%2Fsatis%26quot%3B%2C++++++%26quot%3Brequire-all%26quot%3B%3A+true%2C++++++%26quot%3Brequire-dependencies%26quot%3B%3A+true%2C++++++%26quot%3Brequire-dev-dependencies%26quot%3B%3A+true%2C++++++%26quot%3Barchive%26quot%3B%3A+%7B++++++++++%26quot%3Bdirectory%26quot%3B%3A+%26quot%3Bdist%26quot%3B%2C++++++++++%26quot%3Bformat%26quot%3B%3A+%26quot%3Btar%26quot%3B%2C++++++++++%26quot%3Bskip-dev%26quot%3B%3A+true++++++%7D++%7D%3C%2Fp%3E%3C%2Fdiv%3E++%3Cp%3E%3Ccode%3Erequire-dependencies%3C%2Fcode%3E+und+%3Ccode%3Erequire-dev-dependencies%3C%2Fcode%3E+sorgen+daf%26%23252%3Br%2C+dass+auch+s%26%23228%3Bmtliche+Bibliotheken+archiviert+werden%2C+von+denen+unsere+ben%26%23246%3Btigten+Bibliotheken+abh%26%23228%3Bngig+sind.%3Cbr+%2F%3E+%3Ccode%3Edirectory%3C%2Fcode%3E+ist+das+Verzeichnis%2C+in+dem+Satis+die+heruntergeladenen+Versionen+speichert%3Cbr+%2F%3E+%3Ccode%3Eskip-dev%3C%2Fcode%3E+legt+fest%2C+ob+auch+Branches+heruntergeladen+werden+%28%3Ccode%3Etrue%3C%2Fcode%3E%29+sollen+oder+nur+Tags+%28%3Ccode%3Efalse%3C%2Fcode%3E%29.+%3C%2Fp%3E+%3Cp%3EIst+das+eingestellt%2C+archiviert+Satis+die+Bibliotheken+beim+n%26%23228%3Bchsten%3Cbr+%2F%3E+%3Cpre%3Ephp+satis+build+satis.expanded.json+%26lt%3Bper+HTTP+erreichbares+Verzeichnis%26gt%3B%3C%2Fpre%3E%3C%2Fp%3E+%3Cp%3EPS%3A+Ich+habe+in+diesem+Fall+stets+Github+erw%26%23228%3Bhnt.+Satisfy+kann+nat%26%23252%3Brlich+auch+auf+andere+Dienste+zugreifen%2C+solange+Git+zugrunde+liegt%2C+z.B.+%3Ca+href%3D%22https%3A%2F%2Fbitbucket.org%2F%22%3EBitBucket%3C%2Fa%3E%2C+%3Ca+href%3D%22https%3A%2F%2Fabout.gitlab.com%2F%22%3EGitlab%3C%2Fa%3E+usw.%3C%2Fp%3E+%3Cdiv+class%3D%27yarpp-related+yarpp-related-none%27%3E+%3C%2Fdiv%3E+%09%09%09
php

24.09.2019 21:36:13

Nachtest zu gepufferten und ungepufferten SQL-Abfragen

Aufgrund eines fatalen Denkfehlers im letzten Beitrag schiebe ich hier nochmal einen korrigierenden Nachtest nach. Es geht wieder um gepuffertes vs. ungepuffertes Ausführen von SQL-Querys.

Das letzte Mal hab ich eine völlig ungeeignete Abfrage verwendet:

  1. Sie war zu schnell für einen Vergleich bzw. eine eindeutige Entscheidung, ob mysql_unbuffered_query() Sinn macht
  2. Sie enthielt ein ORDER BY, was nicht clever ist, denn MySQL weiß ja auch erst nach Behandlung aller Datensätze, welcher der größte bzw. kleinste Wert ist.

Außerdem habe ich beim letzten Mal lediglich die Gesamtausführungszeit gemessen, was auch sinnfrei war.

Jedenfalls hab ich diesmal eine wesentlich komplexere Abfrage verwendet. Mangels ordentlicher Testdatenbank, mit der ich lange dauernde Querys erzeugen kann (habs einfach nicht gebacken bekommen, eine Abfrage zu schreiben, die 1 – 5 Sekunden dauert – entweder wars im Zehntelsekunden-Bereich oder im Minutenbereich) habe ich folgende, völlig sinnfreie Abfrage verwendet.

SELECT *  FROM `tabelle` a1  INNER JOIN tabelle a2 ON a1.ID=a2.ID  INNER JOIN kategorien ON a1.kategorien_ID=kategorien.ID  WHERE RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5 AND RAND()<0.5 AND RAND()>0.5

Mir war es wichtig, eine Anfrage zu wählen, die so beschaffen ist, dass die Prüfung eines einzlenen Datensatzes genügt, um zu bestimmen, ob er zum Result-Set gehört oder nicht. Dies wäre mit ORDER BY oder GROUP BY nicht möglich.
Die eigentliche Abfrage ist aber völlig egal, denn ihr habt bestimmt auch „richtige“ Abfragen, die einige Sekunden dauern. Grundsätzlich sei an dieser Stelle schon mal gesagt: Je länger eine Abfrage dauert (Gesamtzeit laut PHPMyAdmin), desto sinnvoller ist der Einsatz von mysql_unbuffered_query().

Jedenfalls habe ich mit obiger Query die gepufferte Abfrage-Variante getestet, also mysql_query(). Dabei kam ich auf folgendes Ergebnis:
Zeit bis zur Verarbeitung des ersten Datensatzes: 5.005784034729 s
Zeit bis zur verarbeitung aller Datensätze: 5.0073800086975 s

mysql_unbuffered_query() im Gegenzug kommt auf dieses Ergebnis:
Zeit bis zur Verarbeitung des ersten Datensatzes: 0.24099016189575 s
Zeit bis zur verarbeitung aller Datensätze: 5.2411839962006 s

Leider konnte ich für diesen Test nicht mein Benchmark-Script nutzen, da ich damit ja nur die Gesamtbearbeitungszeit (basierend auf den Ergebnissen des Apache Benchmark-Tools ab.exe) herausbekomme. Das ist auch der Grund, warum es diesmal keine hübsche, bunte Tabelle gibt – ich hoffe die blanken Zahlen reichen diesmal.

Nun also zur Auswertung:
Der Unterschied in der Gesamtzeit ist irrelevant, weil ich wie gesagt nur per microtime() messen konnte. Die Aussage, dass die ungepufferte Version grundsätzlich langsamer sei, würde ich so nicht unterschrieben wollen.
Viel interessanter ist aber der riesige Unterschied bis zur Bearbeitung des ersten Datensatzes. Und genau das ist ja das Ziel von mysql_unbuffered_query(), wie ich dank Tims Kommentar nun auch begriffen habe ;-). Das bedeutet, dass die Bearbeitung durch PHP bereits sehr viel früher beginnen kann.

Also funktionierts doch hervorragend. Eines muss allerdings gesagt werden: mysql_unbuffered_query() macht keinen Sinn bei Querys mit ORDER BY, da sind die Ausführungszeiten zwischen mysql_query() und mysql_unbuffered_query() gleich (Grund: MySQL scheint Quicksort zu verwenden. Mit Heapsort wäre ein Geschwindigkeitsunterschied durchaus möglich).
Außerdem möchte ich noch sagen, dass ungepufferte SQL-Behandlung auch nur sinnvoll ist, wenn die Verarbeitung des Datensatzes schneller geht als die Ermittlung des nachfolgenden im Result-Set. Ansonsten muss MySQL zwangsläufig puffern, weil die Anwendung nicht hinterherkommt.

Außerdem ist wichtig, dass die Wirkung des schnelleren Ausgebens durch PHP verpufft (zumindest merkt der Client davon nix), wenn ihr die Ausgabe durch PHP puffert (z.B. um den Output zu gzippen). Deshalb ists für mich wohl uninteressant, aber ich wollte euch trotzdem die Möglichkeit aufzeigen, die MySQL und PHP da bieten.

Falls Rechtschreibfehler in diesem Beitrag entdeckt werden, bitte ich dies zu entschuldigen. Ich wollte den Test und den Bericht möglichst schnell veröffentlichen, damit ihr nicht von dem vorigen Beitrag verstimmt werdet.

php

24.09.2019 03:54:40

Composer, Satis, Satisfy: Fremdbibliotheken von Composer, Packagist und Github entkoppeln

In der heutigen Webentwicklung müssen wir das Rad nicht ständig neu erfinden. Es gibt eine Vielzahl von Bibliotheken – sowohl server- als auch clientseitig – die einem das Entwicklerleben vereinfachen. Damit wir aber nicht bei sämtlichen Fremdbibliotheken regelmäßig nachprüfen müssen, ob es eine neue Version gibt, gibt es Paketverwaltungen. Im PHP-Bereich ist Composer der Quasi-Standard für diese Aufgabe.
Dieser Beitrag beschreibt, wie wir mit Composer komfortabel (= inkl. Version Constraints) auch Bibliotheken einbinden können, die:

  • nicht auf Packagist zu finden sind (z.B. private Bibliotheken) und / oder
  • keine composer.json haben oder in früheren Versionen nicht hatten

Die meisten PHP-Bibliotheken sind bei Packagist (dem Standard-Repository von Composer) gelistet und können dadurch ohne weitere Einstellungen in der composer.json unter „require“ angegeben werden.

Bei Bibliotheken, die nicht bei Packagist gelistet sind, müssen wir Composer mitteilen, wo es die Pakete finden kann: Wir müssen die Repositorys selbst definieren.
Für die Einbindung über Github haben wir dann z.B. viele Blöcke wie z.B. folgenden:

"repositories": [     {       "type": "package",       "package": {         "name": "sciactive/pnotify",         "version": "2.0.0",         "source": {           "url": "https://github.com/sciactive/pnotify",           "type": "git",           "reference": "v2.0.0"         }       }     } ]

Die hier angegebene Versionsnummer 2.0.0 basiert auf den Git-Tags der Bibliothek.

Das Problem ist, dass wir die Version fest definiert haben. Composer würde uns immer nur genau diese Version liefern. Wir müssten uns demzufolge selbst darum kümmern, zu gucken, ob es eine neue Version der Bibliothek gibt und dann das Composer-Repository ggf. anpassen. Viel schöner wäre es ja aber, wenn man Bugfix-Releases (und manchmal auch Minor-Releases) automatisch beim Composer Update bekäme.
Leider kann man bei der Definition der Composer-Repositorys aber die Version nicht durch ein Sternchen setzen, sondern dort wird immer ein existierendes Tag (oder ein Branch oder Commit) erwartet.

Und hier kommt Satis ins Spiel. Satis stellt ein Composer-Repository zur Verfügung, ähnlich wie Packagist, mit dem Unterschied, dass es auf unseren Servern läuft und wir selbst bestimmen können, welche Bibliotheken darin enthalten sein sollen (und welche Versionen).
Satis hat dadurch einige Vorteile:

  • Einbindung von nicht öffentlichen Repositorys
  • Einbindung von Bibliotheken, die nicht bei Packagist gelistet sind
  • Nutzung von Bibliotheken, die keine Composer.json haben (mit Satisfy, dazu weiter unten mehr)
  • Unabhängigkeit von Github und Packagist während des Builds (falls diese Seiten mal offline sind oder man selbst kein Internet hat)

Um Satis einzurichten müssen wir zuerst Satis als eigenes Composer-Projekt herunterladen:

php composer.phar create-project composer/satis --stability=dev

Anschließend erstellen wir eine grundlegende Konfiguration (satis.json) innerhalb des gerade erstellten Projektes:

{     "name": "Our Satis repository",     "homepage": "http://localhost/satis",     "require-all": true }

Dies definiert, lediglich, wie unser Repository heißt (name) und wie man es über den Browser aufrufen kann (homepage).
require-all bedeutet, dass Satis sämtliche Versionen der Bibliotheken laden soll – dies ist die Standardeinstellung, denn schließlich soll ja in der composer.json des eigentlichen Projektes später die genaue Version festgelegt werden können.

Nun müssen wir Satis noch mitteilen, welche Bibliotheken wir benötigen. Für Repositorys, die eine composer.json haben, kann dies direkt in der satis.json definiert werden.

"repositories": [     {          "type": "composer",          "url": "https://URL-of-private-Gitlab-Repository"      } ]

Projekte, die keine composer.json haben (wie viele JS-Bibliotheken, weil Client-Entwickler wiederum einen eigenen Paketmanager haben) können mittels Satisfy hinzugefügt werden. Satisfy analysiert dazu alle Git-Tags der definierten Bibliotheken und erstellt dafür einzelne Einträge in der Satis-Konfigurationsdatei.

Damit Satisfy weiß, welche Git-Repositories es einlesen soll, benötigen wir eine packagelist.json:

{     "sciactive/pnotify": {         "url": "https://github.com/sciactive/pnotify",         "minversion": "2.0"     },     ... }

Aus der ursprünglichen satis.json und aus der packagelist.json kann Satisfy nun eine erweiterte Satis-Konfiguration erstellen:

satisfy --repofile satis.json --packagefile packagelist.json --output satis.expanded.json

Die entstehende satis.expanded.json enthält sämtliche Git-Tags aller angegebenen Git-Repositorys als einzelne Satis-Repositorys:

"repositories": [     {       "type": "package",       "package": {         "name": "sciactive/pnotify",         "version": "2.0.0",         "source": {           "url": "https://github.com/sciactive/pnotify",           "type": "git",           "reference": "v2.0.0"         }       }     },     {       "type": "package",       "package": {         "name": "sciactive/pnotify",         "version": "2.0.1",         "source": {           "url": "https://github.com/sciactive/pnotify",           "type": "git",           "reference": "v2.0.1"         }       }     } ... ]

Nun müssen wir Satis nur noch anweisen, die satis.expanded.json einzulesen:

php satis build satis.expanded.json <per HTTP erreichbares Verzeichnis>

Angenommen, wir nutzen Apache und haben einen Link namens „satis“ auf das per HTTP erreichbare Verzeichnis erstellt, können wir im Browser http://localhost/satis aufrufen und sehen eine Übersicht über sämtliche Bibliotheken und deren Versionen, die nun zur Verfügung stehen (so ähnlich wie auf Packagist). Natürlich macht es mehr Sinn Satis auf einen zentralen Server zu packen, damit alle darauf zugreifen können, aber für Testzwecke reicht auch erstmal eine lokale Installation.

Nun können wir die composer.json unseres Projektes öffnen und auf unser Satis-Repository verweisen:

"repositories":[     {       "type": "composer",       "url": "http://localhost/satis"       }     },     {         "packagist": false     } ]

Das "packagist": false bewirkt, dass Composer bei fehlenden Paketen nicht auf Packagist nachsieht. Dadurch hat man die komplette Kontrolle über alle eingebundenen Fremdbibliotheken, wenn Satis sinnvollerweise das einzige Composer-Repository ist.

Nun können wir auf sämtliche Bibliotheken und Versionen, die unter http://localhost/satis aufgelistet sind, im require-Block zugreifen:

"require": {     "sciactive/pnotify": "2.*" }

Man beachte das Sternchen bei der Versionsangabe. Obwohl die JS-Bibliothek PNotify keine composer.json hat und auch nicht bei Packagist gelistet ist, können wir die Version Constraints von Composer nutzen. Es funktioniert natürlich genauso für Bibliotheken, die eine composer.json haben und trotzdem nicht bei Packagist sind.

Einen weiteren Vorteil hat die Nutzung von Satis noch: Es könnte vorkommen, dass man gerade eine neue Version bauen möchte, aber Github oder Packagist gerade nicht erreichbar sind. In diesem Fall können wir keine neue Version erstellen, was unter Umständen teuer sein kann.
Satis kann als Proxy für Github und Packagist fungieren, indem es per Cronjob regelmäßig die Github-Repositorys überprüft und neue Versionen herunterlädt. Wird später eine bestimmte Version per Composer angefordert, kann auf die zwischengespeicherte Version zurückgegriffen werden und man ist somit unabhängig von Github oder Packagist.

Für die Archivierung von Versionen ist folgende Ergänzung in der satis.json nötig:

{     "name": "Our Satis repository",     "homepage": "http://localhost/satis",     "require-all": true,     "require-dependencies": true,     "require-dev-dependencies": true,     "archive": {         "directory": "dist",         "format": "tar",         "skip-dev": true     } }

require-dependencies und require-dev-dependencies sorgen dafür, dass auch sämtliche Bibliotheken archiviert werden, von denen unsere benötigten Bibliotheken abhängig sind.
directory ist das Verzeichnis, in dem Satis die heruntergeladenen Versionen speichert
skip-dev legt fest, ob auch Branches heruntergeladen werden (true) sollen oder nur Tags (false).

Ist das eingestellt, archiviert Satis die Bibliotheken beim nächsten

php satis build satis.expanded.json <per HTTP erreichbares Verzeichnis>

PS: Ich habe in diesem Fall stets Github erwähnt. Satisfy kann natürlich auch auf andere Dienste zugreifen, solange Git zugrunde liegt, z.B. BitBucket, Gitlab usw.

php

24.09.2019 21:36:11

Wie erstelle ich eine HTML-Liste aus einem Nested-Set?

Einer der meistgelesenen Beiträge in diesem Blog ist der über Nested Sets. Es bringt natürlich nichts, wenn man damit performante Abfragen realisieren kann aber anschließend das Ergebnis nicht in entsprechenden HTML-Code umwandeln kann, um daraus eine Navigationsliste oder ähnliches zu erstellen. Da laut einiger Kommentare genau dort das Problem liegt, möchte ich in diesem Beitrag beschreiben, wie man ein Nested Set in eine Unordered List umwandeln kann.

Zuerst nochmal unser Ausgangspunkt: Wir haben eine Tabelle kategorien mit 4 Spalten: ID, name, lft, rgt
Dort sind einige Kategorien untergebracht, genau wie im Nested Set-Beitrag erklärt: Der lft-Wert einer übergeordneten Kategorie ist kleiner als alle lft-Werte Ihrer Kind-Kategorien und der rgt-Wert der übergeordneten Kategorie ist größer als alle rgt- (und auch lft-) Werte. Dadurch entstehen Umarmungen und Abfragen aller Kindelemente einer bestimmten Kategorie sind problemlos möglich.
Aber ich möchte hier nicht nochmal das gesamte Modell erklären, das steht ja ausführlich im Nested-Set-Beitrag.

Nun möchten wir sämtliche Kategorien samt Ihrer Untermenüs hierarchisch anzeigen – in einer Unordered List, die für jede hierarchisch untergeordnete Ebene weitere ULs einbaut. Das geht nur rekursiv.
Falls sich jemand fragt, was nun eigentlich gewonnen ist, wenn wir die rekursiven Abfragen, wie Sie „normalerweise“ bei Baumstrukturen in MySQL gemacht werden, wiederum durch etwas Rekursives ersetzen. Nun, die Variante mit parentID erfordert sehr viele MySQL-Abfragen. Ständig muss in einer Schleife bzw. rekursiv durch PHP die DB abgefragt werden, ob diese oder jene Kategorie Kindelemente hat. Das schlaucht den DB-Server unnötig (jede Anfrage hat noch Overhead). Deshalb ist es immer ratsam SQL-Abfragen aus Schleifen rauszuholen und möglichst durch eine einzige Abfrage zu ersetzen (falls dies möglich ist).

Wenn wir nun die rekursive Funktion allein nutzen, um aus dem fertigen Ergebnis eine Liste zusammen zu bauen, bleibt alles im Rahmen von PHP – es muss kein anderes System mehr nach Informationen befragt werden und das bringt den Geschwindigkeitsschub.

Jedenfalls möchten wir nun die Liste darstellen, dazu sind mehrere Funktionen nötig – zuerst unsere Hauptfunktion, die das Result Set holt und das Array erstellt, mit dem wir fortan arbeiten:

  function getMenu() {       $query = "SELECT lft,rgt FROM kategorien";       $einlesen = mysql_query($query);       $lft = array();       $rgt = array();       while($einzeln = mysql_fetch_assoc($einlesen)) {         $lft[] = $einzeln['lft'];         $rgt[] = $einzeln['rgt'];       }       $query = "SELECT n.ID,n.name, COUNT( * ) -1 AS level FROM kategorien AS n, kategorien AS p WHERE n.lft BETWEEN p.lft AND p.rgt AND (";       for($i=0;$i<count($lft);$i++) {         $query .= "(n.lft<=".$lft[$i]." AND n.rgt>=".$rgt[$i].")";         if($i<count($lft)-1) $query .= " OR ";       }       $query .= ") GROUP BY n.lft";       $einlesen = mysql_query($query);             $menu = array();       while($einzeln = mysql_fetch_assoc($einlesen)) {         $menu[] = array($einzeln['ID'],$einzeln['name'],$einzeln['level']);       }       return buildMenu($menu);   }

Diese Funktion selektiert alle Kategorien und speichert die Datensätze im Array $menu ab. Die erste Abfrage nach allen lft- und rgt-Werten wird erst sinnvoll, wenn nur bestimmte Zweige eingelesen werden sollen. Der Einfachheit halber habe ich das hier weggelassen. Würde bedeuten einfach einen Parameter der Funktion getMenu() zu übergeben und diesen dann in diese Query einfließen zu lassen.

Der Rückgabewert der Funktion erfordert den Aufruf der Funktion, die das Result Set in HTML konvertieren soll:

  function buildMenu($subMenu) {     $menu = "";     $cnt_subMenu = count($subMenu);     for($i=0;$i<$cnt_subMenu;$i++) {       if($i<$cnt_subMenu-1 && $subMenu[$i][2]<$subMenu[$i+1][2]) {         $menu .= "<li>".$subMenu[$i][0]." ".$subMenu[$i][1]."<ul>";         $untilIndexUnderThisKat = getKatsUnder($subMenu,$i+1,$subMenu[$i][2]);         $menu .= buildMenu(array_cut($subMenu,$i+1,$untilIndexUnderThisKat));         $i = $untilIndexUnderThisKat;         $menu .= "</ul></li>";       } else {         $menu .= "<li>".$subMenu[$i][0]." ".$subMenu[$i][1]."</li>";       }     }     return $menu;   }

Diese Funktion durchläuft alle Kategorien-Datensätze und schaut, ob im nachfolgenden Eintrag die Ebene (Array-Index [2]) größer ist als beim aktuellen. Wenn dem so ist, wird eine weitere Liste unter dem aktuellen LI angelegt, indem die Funktion buildMenu() rekursiv aufgerufen wird, vorher aber das Array so gesplittet wird, das nur noch die Untermenüs des aktuellen Eintrags darin enthalten sind. Dazu werden zuerst alle darunter liegenden Kategorien geholt (getKatsUnder()) und anschließend das neue Array erstellt – das übernimmt die Funktion array_cut():

  function getKatsUnder($array,$start,$ebene) {     $cnt_array = count($array);     for($i=$start;$i<$cnt_array;$i++) {       if($array[$i][2]==$ebene) return $i-1;     }     return ($cnt_array-1);   }     function array_cut($srcArr,$start,$end) {     $newArr = array();     for($i=$start;$i<=$end;$i++) {       $newArr[] = $srcArr[$i];     }     return $newArr;   }

Ob die Sache noch performanter zu machen ist, kann ich im Augenblick nicht sagen, weil ich diesen Weg alle Einträge auf einmal zu laden selbst nicht mehr anwende und deshalb auch nicht weiter über die Performance nachgedacht habe. Es ist einfach der Weg, wie ich es mir damals ausgedacht habe. Ghostgambler hatte noch was mit current() und next() vorgeschlagen, eventuell gehts in die gleiche Richtung wie meine Lösung – wenn nicht, wäre es schön, wenn Du noch etwas mehr dazu sagen könntest.

Ich hoffe das hilft denjenigen, die immer wieder nach einem Weg gefragt haben… und wie gesagt: Die Lösung erhebt keinen Anspruch die schönste oder beste zu sein – auf jeden Fall funktioniert sie.
Und damit ihr nicht mühsam die Sachen rauskopieren müsst, hänge ich sogar das Script an: Nested Set zu HTML umwandeln

php