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 / index.php

Random Themen, Tutorials und co

24.09.2019 06:37:53

Datenbanken: Schlüssel und Indizes

Datenbanken: Schlüssel und Indizes

  1. Ziel und Zweck
  2. Das Wesen von Indizes
  3. Primärschlüssel
  4. Unique-Schlüssel
  5. Index-Schlüssel
  6. Fremdschlüssel
  7. Fulltext-Schlüssel
  8. Welche Indizes sollen nun gesetzt werden
  9. Für Fortgeschrittene: Die Reihenfolge der Spalten im Index

Ziel und Zweck

Schlüssel und Indizes (beide Wörter sind im Falle von Datenbanken gleichbedeutend) sind elementare Bestandteile einer Datenbank. Aus Geschwindigkeitsgründen aber auch aus Gründen der internen Verarbeitung durch das DBMS (Database Management System) ist es wichtig, bestimmte Spalten einer Tabelle mit Schlüsseln zu belegen. Schlüssel können stets auf eine oder mehrere Spalten einer Tabelle gelegt werden.
Als kleine Anmerkung möchte ich darauf hinweisen, dass die Mehrzahl von Index Indizes ist (es gibt keine Indexe).

Das Wesen von Indizes

Indizes werden intern vom DBMS als sortierte Listen gespeichert. Das bedeutet zweierlei: Das Eintragen eines neuen Datensatzes in eine Tabelle, auf der ein oder mehrere Indizes liegen, wird geringfügig mehr Zeit in Anspruch nehmen als ohne Indizes, da ja zu jedem Index der neue Wert in die sortierte Liste an die richtige Position eingetragen werden muss.
Im Umkehrschluss allerdings sind Indizes beim Selektieren von Daten ein enormer Geschwindigkeitsvorteil, denn ein Wert kann viel schneller in einer sortierten Liste gefunden werden. Im Abschnitt Index-Schlüssel steht dazu ein Beispiel, das verdeutlicht, wie die Beschleunigung funktioniert.
Die Anzahl von Indizes pro Tabelle ist unbegrenzt. Lediglich beim Primärschlüssel darf nur einer pro Tabelle vorhanden sein. Man muss sich aber immer überlegen, wann ein Index Sinn macht.

Primärschlüssel

Der Primärschlüssel ist kennzeichnend für einen bestimmten Datensatz. Das bedeutet, dass allein mit dem Primärschlüssel ein Datensatz eindeutig festgelegt ist und somit wiedergefunden werden kann. Pro Tabelle kann nur ein Primärschlüssel vergeben werden. In einer Tabelle Users mit den Spalten Username und E-Mail könnte man als Primärschlüssel den Usernamen wählen, denn ein Username sollte stets nur von einem User belegt sein (ansonsten würde es beim Login Probleme geben). Ebenso könnte man die E-Mail-Adresse als Primärschlüssel wählen, aber das ist unsicher, da durchaus mehrere Personen mit der gleichen E-Mail-Adresse unterwegs sein können (z.B. Familie Müller mit andi_laura_mueller@xyz.de) und da würde es Probleme geben, da ein Datensatz dadurch nicht mehr eindeutig identifizierbar ist (2 Datensätze (zum einen Andi, zum anderen Laura) unter einer E-Mail-Adresse).
Warum sollte man trotzdem eine Extra-Spalte ID hinzufügen und diese als Primärschlüssel wählen? Es gibt zwei Gründe: erstens sollte man in den seltensten Fällen so genannte ’sprechende Schlüssel‘ verwenden. Damit ist gemeint, wenn der Schlüssel bereits ein Nutzdatum darstellt, sollte er nicht als Primärschlüssel dienen. Und der zweite Grund ist die Gewohnheit, denn mit einer automatisch hochzählenden ID als Primärschlüssel kann man nichts falsch machen.
Es ist festzuhalten, dass es oft mehrere Schlüsselkandidaten gibt. Meist sollte man eine ID als Primärschlüssel wählen. Das reduziert die Arbeit für die Datenbank, weil von einem Computer mit einer Zahl grundsätzlich besser umgegangen werden kann als mit einer Zeichenkette. Ausnahmen, wo man überlegen kann, ob man einen sprechenden Primärschlüssel verwendet, sind Daten, die bereits als Zahl vorliegen. Bei der ISBN eines Buchtitels ist dies möglich, noch besser sogar bei der Fahrgestellnummer eines Autos. Aber auch in diesen Fällen schadet es keineswegs die ID als Primärschlüssel zu verwenden.
Grundsätzlich ist nur ein Primärschlüssel pro Entität bzw. Tabelle möglich.

Ein Primärschlüssel kann auch aus mehreren Spalten zusammengesetzt sein. Hat man eine Tabelle Automodell wäre der Modellname allein eventuell ein Schlüsselkandidat. Wir gehen dabei davon aus, dass kein Hersteller B den Modellnamen von Hersteller A ‚klaut‘ (wenn das nicht sogar markenrechtlich verboten ist). Wenn man aber einen Autohändler fragen würde, käme der sofort mit einem Beispiel, wo der Modellname als Primärschlüssel nicht funktioniert: Manche Hersteller entdecken nämlich alte Modellnamen wieder. Mir ist das beispielsweise bei Skoda bekannt. In den 1950er Jahren gab es bereits einen Skoda Octavia. Und dieses Modell gibt es auch heute noch oder besser wieder, allerdings mit ganz anderen inneren und äußeren Werten. Stellt man beide Autos nebeneinander, kann man schnell feststellen, dass es sich nicht um das gleiche Modell handelt. Die Datenbank aber, die nur den Modellnamen kennt, würde beide Modelle für gleich halten.
Wenn man nun als Primärschlüssel den Modellnamen und das Baujahr verknüpft, funktioniert das wieder. Dann erkennt auch die Datenbank, dass es zwei verschiedene Modelle sind. Sicherer (und in diesem Fall auch wieder performanter) ist es aber, eine automatisch hochzählende ID als Primärschlüssel zu vergeben.

Unique-Schlüssel

Unique-Schlüssel verhalten sich ähnlich wie Primärschlüssel. Sie dienen dazu, dass der Wert eines Datensatzes in der Spalte, die mit dem Unique-Schlüssel belegt ist, bei keinem anderen Datensatz vorkommen darf. In meinem Beispiel mit der Tabelle ‚Users‘ hätte ich beispielsweise auf den Usernamen einen Unique-Index gesetzt (wenn man eine ID als Primärschlüssel wählt), weil ein Username nur ein mal vorkommen darf (Stichwort Login).
Ansonsten kann auch ein Unique-Index mehrere Spalten vereinen. Das ist sogar manchmal sehr sinnvoll. Als Beispiel sei nur der Umstand aufgeführt, wenn es in einer Tabelle ‚Users‘, die neben den Spalten Username und E-Mail noch das Geburtsdatum enthält, ein Unique-Index auf E-Mail und Geburtsdatum gesetzt wird. Wenn wir davon ausgehen, dass User immer ihr wirkliches Geburtsdatum angeben, ist es somit gestattet, dass sich Personen, die eine gemeinsame E-Mail-Adresse haben, auch beide anmelden können (es sei denn sie haben am gleichen Tag Geburtstag).
Die Datenbank verhält sich so, dass sie den Versuch einen Wert in ein Feld einzutragen, der in der Tabelle schon ein Mal vorhanden ist und auf dessen Spalte ein Unique-Index liegt, einfach abwehrt. Wenn das Eintragen in PHP mittels mysql_query() versucht wird sollte man den Rückgabewert dieser Funktion auswerten (1 bzw. true bei Erfolg, 0 bzw. false bei Misserfolg) und den User darüber informieren, weshalb das Eintragen keinen Erfolg hatte, sonst weiß der nämlich nicht Bescheid und wird entnervt aufgeben.

Index-Schlüssel

Index-Schlüssel existieren alleinig zur Beschleunigung von Datenbankabfragen. Wenn sich jemand über eine langsame Datenbank ärgert, ist meistens kein Index gesetzt, der die Spalten in der WHERE-Bedingung abdeckt. Wie weiter oben erklärt speichert ein Index die Spalten-Inhalte, die zum Index gehören, mit einer Vorsortierung. Dadurch können Bedingungen und Sortierungen leichter ausgeführt werden. Am Beispiel lässt sich das leicht verdeutlichen: Angenommen die Namen im Telefonbuch wären nicht nach Städten und alphabetisch geordnet, sondern man bekäme ein Telefonbuch von ganz Deutschland und alle Namen stehen zufällig an irgendeiner Position. Wie schnell schafft man es den eigenen Namen zu finden? Und diese Zeit vergleichen wir, wenn wir ein nach Orten und in den Orten noch nach Namen sortiertes Telefonbuch bekommen. Zweitere Methode geht wohl wesentlich schneller. Und wer denkt die Zeitdifferenzen seien in diesem Beispiel extrem, der irrt. Man muss nur mal versuchen aus einer Artikeldatenbank mit 500000 Artikeln sich alle auflisten lassen mit dem Anfangsbuchstaben A. Und danach legt man einen Index auf den Artikelnamen und wiederholt das Experiment. Eine Größenordnung von 50-200-facher Beschleunigung ist keine Seltenheit.

Fremd-Schlüssel

Obwohl Sie standardmäßig von MySQL, dem führenden Open Source-DBMS, nicht unterstützt werden (nur mit dem Tabellentyp InnoDB), sollen Fremdschlüssel kurz erklärt werden.
Wenn zwei Tabellen über eine Relation verknüpft werden, geschieht dies über das Eintragen des Primärschlüssels der nicht eindeutigen Tabelle in die Partner-Tabelle (1:n, bei 1:1 genauso, da ist es egal, auf welche Seite der Primärschlüssel geschrieben wird) oder durch das Erstellen einer Zwischentabelle bei einer n:m-Beziehung.
Diese Spalten, die allein zum Zuordnen geeignet sind und nichts mit den eigentlichen Daten in der Tabelle zu tun haben, werden in einem Fremdschlüssel gespeichert. Hat man beispielsweise eine Artikeldatenbank (ID, Name, Preis) und dazugehörige Kategorien (ID, Name), fügt man der Artikel-Tabelle eine Spalte Kategorien_ID hinzu und belegt diese Spalte mit einem Fremdschlüssel, der anzeigt, auf welche Tabelle bzw. genauer auf welchen Datensatz sich die ‚Relationsspalte‘ Kategorien_ID bezieht.
Soll nun eine Kategorie gelöscht werden, der 100 Artikel zugeordnet sind, bemerkt das DBMS diese Verknüpfung. Man hat dann die Wahl zuerst alle 100 Artikel einer anderen Kategorie zuzuordnen, die Kategorie doch beizubehalten oder die Kategorie trotzdem zu löschen (dann verweist die Kategorien_ID dieser 100 Artikel allerdings ins Nichts). Fremdschlüssel sind eine schöne Funktion, um Abhängigkeiten zweier Tabellen untereinander umzusetzen.
Wie gesagt, gibt es in MySQL keine Fremd-Schlüssel. Als Empfehlung würde ich aber zumindest einen Index-Schlüssel auf die Spalte legen, die aus der Beziehung entsteht, denn oft werden viele Abfragen über das Fremdschlüssel-Attribut bedingt.

Fulltext-Schlüssel

Fulltext-Schlüssel bilden eine Ausnahme vom besagten Wesen der Schlüssel. Diese Schlüssel können nur auf Felder mit den Datentypen TEXT oder BLOB angewendet werden.
Einen Fulltext-Index benötigt man nur, wenn man mittels MATCH eine ’natürliche Ähnlichkeit‘ zweier Beiträge feststellen möchte. Wie das funktioniert soll hier vorerst nicht näher beleuchtet werden, aber so werden die häufigen ‚Ähnlichen Artikel‘ in Online-Shops und Ähnlichem dynamisch gefunden. Man übergibt der Match-Funktion einen String und sie findet in den angegebenen Spalten ähnlich lautende Artikel. Und damit das funktioniert, muss auf allen im Match-Befehl angegebenen Spalten ein FULLTEXT-Index liegen (pro MATCH-Befehl ein FULLTEXT-Index, das heißt, wenn in dem MATCH in 2 Spalten gesucht werden soll, muss 1 Fulltext-Index mit 2 Spalten angelegt werden).
Als SQL-Code sieht ein MATCH-Befehl dann so aus:

SELECT ID FROM artikel WHERE MATCH (titel, beschreibung) AGAINST ('ein grüner Wollpullover, der so kuschelig weich und warm ist')

Und ein solcher Befehl würde – vorausgesetzt man hat solche Artikel in der Artikeltabelle – dann auch ID von Artikeln mit den Titeln ‚Blauer Wollpullover‘, ‚Plüschtier-Teddy‘ (wenn in der Spalte beschreibung irgendwie etwas mit „kuschelig“ steht) usw.

Welche Indizes sollen nun gesetzt werden

Die Pflicht

Der Primärschlüssel sollte immer als erstes besetzt werden. Regeln dazu findet man im Kapitel über Primärschlüssel.
Unique-Schlüssel ergeben sich ebenfalls aus den Anforderungen. Wenn eine bestimmte E-Mail-Adresse beispielsweise in einer Newsletterdatenbank nur ein Mal enthalten sein soll, wird ein Unique-Index auf diese Tabelle gelegt.
Wenn man ein DBMS verwendet, das Fremdschlüssel unterstützt, können diese bereits anhand des ERM abgelesen werden.
Für eine ‚Ähnliche Artikel‘-Funktion wird immer ein Fulltext-Index benötigt, sonst gibt das DBMS eine Fehlermeldung aus. Wenn eine solche Funktion also geplant ist bzw. implementiert werden soll, ist dieser Index durch die beteiligten Spalten der MATCH-Funktion ebenfalls bereits vorgegeben.

Die Kür

Bei den Index-Schlüsseln muss man etwas nachdenken, denn es bringt wenig, auf jede Spalte einfach einen Index zu legen, egal ob er gebraucht wird oder nicht. Ich habe die Erfahrung gemacht, dass es für ein Webprojekt durchaus Sinn macht, für jede Funktion, die häufig ausgeführt wird, einen Index anzulegen. Ein Beispiel: Auf einer Communityseite loggen sich täglich viele User ein. Wenn wir als Abfrage im Script folgendes haben:

SELECT ID FROM users WHERE username='".$user."' AND passwort='".$pass."'

würde ich einen Index auf username und passwort legen (einen Index für beide Spalten zusammen). In einer Tabelle Personen einen Index auf die Hausnummer zu legen, macht dagegen keinen Sinn und verlangsamt das Eintragen neuer und das Updaten vorhandener Datensätze nur unnötig.
Wichtig dabei: Pro SELECT-Anweisung kann das DBMS nur einen Index auswählen. Deshalb ist ein verbundener Index im eben genannten Login-Beispiel performanter als jeweils einen Index auf username und passwort zu legen.
Für eine weitere Optimierung lege ich noch den Abschnitt zur Bedeutung der Reihenfolge der Spalten in einem Index ans Herz.

Für Fortgeschrittene: Die Reihenfolge der Spalten im Index

Wer das letzte Quäntchen aus seiner MySQL-Datenbank holen möchte, sollte sich die Reihenfolge der Spalten in den einzelnen Indizes ansehen. Die Indizes erzeugen vorsortierte Listen, das wurde bereits gesagt. Das DBMS kann den Index dann allerdings auch nur in dieser Reihenfolge abarbeiten. Am Beispiel aller Telefonnummern Deutschlands erzeugt die Sortierung nach Städten und anschließend (bei gleicher Stadt) nach Namen eine andere Liste als die Sortierung nach Namen und (bei gleichem Namen) nach der Stadt.
Daraus und aus dem Umstand, dass nur ein Index pro Abfrage genutzt werden kann, folgt, dass die Reihenfolge der Spalten im Index der der Abfrage entsprechen sollte.
Als Beispiel: Unsere Datensätze:

Name Stadt Telefonnummer
Müller Ulm 12345
Meier München 54321
Müller München 56789
Schmidt Leipzig 98765

Wir legen einen Index Name_Stadt (geben zuerst Name, dann Stadt an) an, der folgende Liste erzeugt:
(Meier,München); (Müller,München); (Müller,Ulm); (Schmidt,Leipzig)
Zum Vergleich legen wir noch einen Index Stadt_Name an. Dieser erzeugt folgende Liste: (Leipzig,Schmidt); (München,Meier); (München,Müller); (Ulm,Müller)
Im Script steht nun folgende Abfrage:

SELECT Name,Stadt FROM personen WHERE Name='Müller' AND Stadt='München'

Bei dieser Minidatenbank mag das Ergebnis nicht wirklich unterschiedlich sein, aber bei größeren Datenmengen ist der Unterschied eindeutig vorhanden. Der Index Name_Stadt würde demzufolge schnellere Ergebnisse erzielen, da zuerst eine Untermenge aller Datensätze gesucht wird, in denen der Name='Müller' ist. Der Index Stadt_Name kann hierfür nur bei der Stadt genutzt werden (das Finden aller Datensätze mit dem Namen Müller muss ohne Index erfolgen). Die Performance mit dem Index Stadt_Name ist dadurch etwas besser als ganz ohne Index, aber es ginge schneller.
Mit dem Index Name_Stadt kann dann auch sehr flott der Eintrag der Müllers aus München gefunden werden. Die Reihenfolge spielt also keine unbedeutende Rolle!
Andererseits können somit auch Indizes eingespart werden. Wenn man beispielsweise einfach alle Personen selektieren möchte, die den Namen Müller tragen:

SELECT Stadt FROM Personen WHERE Name='Müller'

In diesem Fall kann der Index Name_Stadt ebenfalls genutzt werden. Es muss also kein Extra-Index auf die Spalte Name gelegt werden.

Und natürlich wird der Index nicht nur bei der WHERE-Bedingung eingesetzt sondern auch beim Sortieren. Da das WHERE immer vor dem ORDER BY ausgeführt werden muss, würde also auch folgende Abfrage beschleunigt:

SELECT Telefonnummer FROM Personen WHERE Name='Müller' ORDER BY Stadt

Dagegen wäre der Index Name_Stadt für folgende Abfrage recht langsam sein:

SELECT Telefonnummer FROM Personen WHERE Stadt='München' ORDER BY Name

Er käme lediglich beim Sortieren zum Einsatz. Das Filtern würde recht lange dauern, falls kein anderer Index festgelegt wurde.

datenbank

24.09.2019 21:40:56

CSS-Tuning mittels DATA-URI (base64)

Dieser Artikel soll als Ergänzung zum Artikel CSS-Sprites dienen, den Jan vor einem Jahr veröffentlicht hat. Ergebnis soll so sein: Internetwerbung (Quellcode der CSS-Datei)
Wie Jan bemerkt hat, ist die Verwendung von CSS-Sprites teilweise sehr tricky und liefert z.b. schlechte Ergebnisse wenn:

  • die kombinierten Bilder verschiedene Farbtiefen haben, weil das Ergebnisbild entweder die größte Farbtiefe aller Bilder hat und dadurch sehr groß wird, oder die kleinste Farbtiefe aller Bilder hat und dadurch eher unansehnlich wird. Zwischenwerte sind denkbar aber auch immer nur ein Kompromiss zwischen Qualität und Dateigröße
  • Angenommen man möchte 2 Bilder kombinieren: 1.) 1x1000px und 2.) 1000x1px, dann wird das Ergebnisbild entweder 1001x1000px oder 1000x1001px groß, d.h. was man durch Verringerung an HTTP-requests einspart, zahlt man durch zusätzliche Übertragungsdaten wieder drauf
  • das Problem mit Repeat bei sehr langen Seiten
  • beim Einfügen neuer Teilbilder muss ggf. das ganze „Sammelbild“ neu zusammengesetzt werden (inkl. Änderungen in der CSS-Datei).

Die Lösung heißt DATA-URI (base64)

Die Grundlagen dazu sind sehr gut in der englischen Wikipedia nachzulesen. Allerdings möchte ich euch einige (selbstentwickelte) Tuningmaßnahmen vorstellen. Zuerst muss gesagt werden, dass man mit dieser Methode sämtliche HTTP-Requests aus seiner CSS-Datei entfernen kann. Dadurch wird die CSS-Datei allerdings um die Summe aller verwendeten Bilder größer. Deshalb unbedingt Cache-Header verwenden!!! Da meine „Tuningmaßnahmen“ sehr serverlastig sind (Bildbearbeitung mittels PHP), empfehle ich dringend die Ergebnis-CSS-Datei zu cachen: Ich habe einen einfachen (aber wirkungsvollen) Cache implementiert.

So solls am Ende aussehen

Auf einer meiner Internetwerbung Seiten (die Seite ist egal, und dient nur Demonstationszwecken) kann man sich die CSS-Datei ansehen. Wenn man den Quelltext nach der Zeichenkette: „url(„ durchsucht, sieht man alle verwendeten Bilder als Teil der CSS-Datei und nicht mittels Link-Tag referenziert. Der Kommentar am Anfang ist nur informativ für mich und sagt mir die Zeichenzahl meiner CSS-Datei (=Größe).

Anleitung zum Umbau der CSS-Datei

Wer erstmal „nur“ testen möchte, kann gerne die Code-Schnipsel von Wikipedia verwenden. Ich habe diese weiterentwickelt und stelle sie euch hier exklusiv zur Verfügung.

  • CSS-Datei irgendwo sichern … man weiß ja nie ????
  • Falls noch nicht in der .htaccess vorhanden: Addtype application/x-httpd-php .css einfügen, damit die CSS-Datei durch den PHP-Interpreter läuft (alternativ kann auch die style.css in style.css.php umbenannt werden, aber dann müssen auch alle Referenzen im HTML-Code geändert werden)
  • Dem Root-Verzeichnis Schreibrechte 777 geben (aus Sicherheitsgründen nach Erzeugung der CSS-Datei die Schreibrechte wieder entfernen)
  • In der Index-Datei den Pfad zur CSS-Datei ändern in: style_neu.css
  • Folgenden PHP-Code an den Anfang der CSS-Datei einfügen: CSS-Code für base64-Codierung
  • Die alte CSS-Datei aufrufen, damit die neue CSS-Datei erstellt wird.
    Was Passiert? :
    Die bisherige CSS-Datei wird umgebaut und liefert die neue komprimierte CSS-Datei mit eingefügten Base64-Bildern. Die neue CSS-Datei wird unter dem Namen style_neu.css im Root-Verzeichnis gespeichert (ggf. kann man das anpassen (bedenkt: Schreibrechte 777 für den betroffenen Ordner setzen)). Die ganze Umwandlung besteht aus 3 Schritten:
    ob_start(‚compress‘);
    Der Ausgabepuffer wird aktiviert und komprimiert die bisherige CSS-Datei indem überflüssige Zeichen entfernt werden. Bemerkung: Falls jemand eine Methode (Code-Schnipsel) kennt, wie man die CSS-Datei noch weiter verkürzen kann (CSS-Shorthand-Erkennung, Zusammenfassung gleicher Parameter (wie in meinem CSS-Beispiel), u.ä.), bitte kurze Nachricht an mich (die Geschwindigkeit der Umwandlung ist egal, weil das Ergebnis gecached wird).
    function data_url
    Diese Funktion ist das eigentliche Herzstück. Sie wird für jedes Bild der CSS-Datei aufgefufen. Pflichtparameter sind: die Bild-Url ($file) und der MIME-Datentyp ($mime). Optionale Parameter sind die Seitenlängen des Bildes (damit das Bild, falls es nur verkleinert gebraucht wird, vor der Umwandlung in Base64 gestaucht werden kann) und die Qualität (0 <= X <= 100). Die ersten Zeilen legen fest, ob sich eine Bildbearbeitung lohnt. Ich lege fest, dass kleine Bilder (kleiner als 70 x 70px) prinzipiell Base64-Codiert werden sollen, weil bei diesen Bildern die HTTP-Requestzeit im Verhältnis zur eigentlichen Übertragungszeit sehr hoch ist. Dateien werden durch die Base64-Codierung ca. 1/3 größer. Dadurch kann es passieren, dass bei großen Bildern die Übertragungszeit stärker wächst, als die Zeiteinsparung durch Verringerung der HTTP-Requests. Und deshalb lasse ich diese Bilder lieber normal referenziert. Außerdem möchte ich Bilder codieren, die in der fertigen CSS-Datei (um mindestens 30%) kleiner sind als das Originalbild, weil die Einsparung durch die physische Verkleinerung des Bildes den zusätzlichen Traffic durch die Base64-Codierung wett macht.
    Ähnlich verhält es sich bei Bildern, die lediglich in geringer Qualität gebraucht werden (z.B. bei sehr kontrastarmen Bildern merkt man Qualitätsunterschiede kaum). Alle Bilder, die nicht ausgewählt wurden, werden wie bisher normal referenziert. Alle anderen (also die meisten der CSS-Datei) werden je nach Mime-Typ gestaucht. Die Verwendung des Ausgabepuffers in der Funktion ist ein Trick, damit man das Bild als String bekommt, den man dann in Base64 umwandeln kann. Die restlichen Funktionen sind reine Arbeitsfunktionen ohne (geistige) Leistung meinerseits.
    Schreiben-Funktion
    Wie man sich schon denken kann, übernimmt diese Datei den Schreibvorgang der fertig erzeugten CSS-Datei. Es wird noch der Header: Content-Type: text/css eingefügt und festgelegt, dass die Style_neu.css komprimiert an den Browser versendet wird. Das lohnt sich sehr, weil Base64-Code sehr stark komprimiert werden kann.
  • alle Stellen der CSS-Datei, an denen ein Bild mittels url(‚xxxxx.yyy‘) referenziert wird, werden ersetzt durch url([Base64-Code]), wobei Breite und Höhe unbedingt angegeben werden sollten, und den optionalen Parameter Qualität zwischen 1 und 100 (einfach mal ausprobieren, wie weit man die Qualität runterschrauben kann, bis man den Unterschied sieht).
  • kontrollieren, dass jedes externe Bild in genau einer CSS-Regel verwendet wird (notfalls Regeln neu sortieren), damit das selbe Bild nicht mehrfach in die CSS-Datei eingefügt wird. Beispiel: Bisher:
    .a{    height:18px;    margin:0 0 0 5px;    padding:0px;    background-image:url('a.jpg');    } .b{    height:20px;    margin:0 0 0 5px;    padding:0px;    background-image:url('a.jpg');    } .c{    height:22px;    margin:0 0 0 5px;    padding:0px;    background-image:url('a.jpg');    }

    Also 3 CSS-Regeln, die sich nur in verschiedenen Height-Werten unterscheiden.
    Müssen umgewandelt werden in:

    .a,.b,.c    {    margin:0 0 0 5px;    padding:0px;    background-image:url(<?=data_url('a.jpg','image/jpg',10,10)?>); /*angenommen breite und höhe sind 10px*/    } .a{    height:18px;    } .b{    height:20px;    } .c{    height:22px;    }

    Ergebnis: das Hintergrundbild ist jetzt nur noch in einer Regel und wird deshalb auch nur einmal in die CSS-Datei eingefügt. Kleiner Nebeneffekt: die redundanten margin- und padding-Werte wurden entfernt.

Benchmark

Ich habe auf meiner Seite beide Varianten mit YSlow getestet:
Vorher: 0,56s
Nachher: 0,79s (css nicht gecached (umwandlung on the fly))
Nachher: 0,25s (css gecached)

Fazit

Im ungecachetem Zustand wird die Zeiteinsparung durch die Verringerung der HTTP-Requests vollständig durch die Erstellungszeit der neuen CSS-Datei aufgebraucht. Ein Cache ist also unbedingt notwendig!

Nur nebenbei

Jan hatte ja schon erwähnt, dass die meisten Browser eine interne Beschränkung haben, was die Anzahl der gleichzeitigen HTTP-Requests angeht. Erwähnenswert ist, dass die meisten Betriebssysteme ebenfalls eine interne Beschränkung haben. D.h. es ist doppelt wichtig die Anzahl der Requests der eigenen Seite gering zu halten, weil es eben 2 Flaschenhälse gibt.

Nur Nebenbei II

Wer noch die letzten Prozente rausholen möchte, kann die gecachede Datei gleich gezipt speichern, damit beim Ausliefern die Zeit zum Zippen entfällt. Bei mir bringt das etwa 0,05s … (klingt wenig, entlastet den Server aber)

Ein kleines Quiz am Ende

Was wird hier gemacht?

echo '<link rel="icon" href="data:image/ico;base64,'.base64_encode(file_get_contents('images/favicon.ico')).'" />';
css

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:23:36

Einen Datensatz aus einem Result-Set laden

Heute gibt es mal wieder einen Beitrag zur Performance im Umgang von PHP und MySQL – angeregt duch eine Mail von Alex Kuhrt. Eigentlich wollte ich dieses Thema schon lange mal untersuchen, aber bin leider nicht dazu gekommen. Eine der häufigsten (wenn nicht die häufigste) Anwendungen beim Umgang mit MySQL ist das Weiterverarbeiten der Daten eines Result-Sets. Dafür bietet PHP jede Menge Möglichkeiten: mysql_fetch_object, mysql_fetch_array, mysql_fetch_assoc und mysql_fetch_row. Eventuell gibt es auch noch mehr, weiß ich jetzt nicht. Jedenfalls ist es schon verwunderlich, dass PHP da so viele Funktionen anbietet. Ich wollte der Sache mal auf den Grund gehen und die verschiedenen Varianten hinsichtlich ihrer Performance untersuchen.

Die Gemeinsamkeit aller Varianten ist, dass Sie aus einem Result-Set einen einzelnen Datensatz laden, je nachdem, auf welchen Datensatz der interne zeiger gerade zeigt. Grundlegender Unterschied ist, dass die Spalten der Datensätze unterschiedlich angesprochen werden. Bei mysql_fetch_object wird beispielsweise ein Objekt zurückgegeben, über das dann mittels -> auf eine bestimmte Spalte zugegriffen werden kann. mysql_fetch_assoc gibt ein assoziatives Array zurück. Man kann also über [’spaltenname‘] auf die jeweiligen Spalten des Datensatzes zugreifen. mysql_fetch_row gibt ein normales Array mit numerischen Indizes zurück. Die Indizes sind in der Reihenfolge, wie sie in der Anfrage-Query angegeben wurden. Der Zugrif erfolgt dann beispielsweise über [0]. Und mysql_fetch_array vereint das alles, indem es als zweiten Parameter mit einem Flag die Art der Rückgabe bestimmt.

Die einzelnen Zugriffsarten sollten doch aber nicht das einzigste sein, was die Entscheidung, welche Funktion man nimmt, beeinflusst, denn eine Umstellung fällt nicht wirklich schwer. Wollen wir uns deshalb einmal die Performance ansehen.
Dazu dient ein Script, das folgendermaßen aussieht:

$einlesen = mysql_query("SELECT ID,adresse,geobreite,geolaenge FROM hotels"); while($item = mysql_fetch_*($einlesen)) {   echo $item['ID'] // oder $item->ID oder $item[0] }

Als Datengrundlage wurde eine Tabelle mit rund 20000 Datensätzen benutzt, was hier aber unerheblich ist, da es ja um die Performance der PHP-Funktionen geht.

Hier also nun die mit Spannung erwartete Tabelle:

Datei Gesamtlaufzeit durchschnittliche Laufzeit pro Durchlauf Verhältnis zur schnellsten Variante
mysql_fetch_row.php 310.897048 s 310.897 ms 100 %
mysql_fetch_assoc.php 319.98842 s 319.099 ms 103 % (+ 3%)
mysql_fetch_array.php 328.532407 s 328.532 ms 106 % (+ 6%)
mysql_fetch_object.php 376.40720 s 376.041 ms 121 % (+21%)

mysql_fetch_row ist also die schnellste Veriante, dicht gefolgt von mysql_fetch_assoc. Dahinter reihen sich mysql_fetch_array sowie weit abgeschlagen mysql_fetch_object ein.
Nun zu den Erklärungsversuchen. mysql_fetch_row gibt ein Array mit numerischen Indizes zurück. Dies ist im Vergleich zu mysql_fetch_assoc schneller, da die assoziativen Indizes nach den Spaltennamen nicht angelegt werden müssen. Trotzdem habe ich die assoc-Variante grün markiert, weil sie gegenüber dem numerischen Index viel verständlicher ist, wenn man mal über den Code drüberguckt. Außerdem passieren mit mysql_fetch_row viele Fehler, wenn man mal einfach eine Spalte in die Query hinzufügt (und dies nicht am Ende der Spaltenliste tut). Anschließend kommt mysql_fetch_array, das in seiner Standardversion das Flag MYSQL_BOTH nutzt und somit ein Array zurückgibt, das sowohl numerische als auch assoziative Indizes hat. Das Array ist also doppelt so groß wie bei den zuvor genannten Varianten – das ist Verschwendung und gehört natürlich bestraft. Nutzt man mysql_fetch_array mit dem Flag MYSQL_ASSOC, kommt es auf einen ähnlichen Wert wie mysql_fetch_assoc, aber dann kann man auch gleich diese Funktion nehmen.
Und ganz abgeschlagen kommt dann noch mysql_fetch_object. Wie so oft im Programmiererleben geht Objektorientierung auf Kosten der Performance. Da ich immer die Notwendigkeit von OOP anhand des daraus folgenden Nutzens festmache, würde ich bei der hier besprochenen Anwendung darauf verzichten, da es eigentlich keinen Mehrwert bietet.

Als Fazit empfehle ich die Nutzung von mysql_fetch_assoc, einfach weil diese Variante wenig fehleranfällig (gegenüber mysql_fetch_row) ist und dazu noch recht performant. Wem es auf die reine Performance ankommt und weiß, was er tut, der kann auch mysql_fetch_row nutzen (bzw. mysql_fetch_array mit dem Flag MYSQL_NUM).

Zum Schluss gibts noch die Ergebnisse und die Quelltexte.

PS: Ich hoffe dieser Beitrag genügt den grammatikalischen und ortographischen Ansprüchen einiger Leser ????

datenbank

24.09.2019 21:36:38

Nicht mehr includen als nötig

Wenn man häufig genutzte Funktionen in eine extra Datei auslagert, die dann einfach per include() oder require() in alle anderen Scripte der Seite eingebunden werden kann, ist das eine feine Sache. Es gibt allerdings ein Problem mit einer solchen Include-Datei: Meistens werden für bestimmte PHP-Scripte nur einige Funktionen benötigt und nicht alle, die in der Funktionsdatei stehen.

Man sollte deshalb darauf achten, dass man mehrere Funktionsdateien so anlegt, dass sie die Funktionen enthalten, die auch von allen Scripten, die diese Datei einfügen, benötigt werden. Wenn Funktionen in sehr vielen Dateien benutzt werden (bspw Überprüfung, ob man eingeloggt ist), kann man diese in eine Datei mit anderen Funktionen packen, die in den gleichen PHP-Dateien auch noch benutzt werden. Eine spezifische Funktion für 2 Seiten gehört allerdings keineswegs in eine solche globale Funktionsdatei. Dafür sollte man eine Extra-Datei anlegen, wo dann diese Funktion enthalten ist.

Die Laufzeit mag gar nicht so sehr steigen durch das Benutzen einer großen Funktionendatei, aber der Speicherbedarf steigt, denn jede Funktion, die includet wird, muss auch im Speicher vorrätig sein, damit auf sie bei Bedarf zugegriffen werden kann, denn PHP weiß ja noch nicht, ob die Funktion dann wirklich gebraucht wird.

php