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 21:36:03

mod_gzip, mod_deflate und sonstige Komprimierungsverfahren für Web-Inhalte

Bei den meisten Webprojekten dauert die Übertragung des HTML-Codes zum Client länger als die Generierung der Inhalte auf dem Server. Zusätzlich besteht immer das Problem, dass Bandbreite auf Servern / Webhosting-Paketen oft (teuer bezahlte) Mangelware ist. Dieses Problem und wie man es umgehen kann, habe ich bereits in einem früheren Beitrag beschrieben. Diesmal möchte ich weitere Möglichkeiten der Bandbreiteneinsparung zusammenfassen.

Die Basis für die Komprimierung des HTML-Codes ist es, dass moderne Browser mit GZIP umgehen können. Dazu senden Sie einen entsprechenden Header-Eintrag im HTTP-Request namens Accept-Encoding. Anhand dieses Eintrags kann serverseitig bestimmt werden, ob der Browser des anfragenden Clients GZIP-Koprimierung der Inhalte unterstützt. Wenn ja, darf man diese Möglichkeit auch nutzen und kann alle zum CLient zu übertragenden Inhalte komprimieren. Das spart bis zu 80%!

Doch vorweg noch eine andere Sache: Alle hier genannten Komprimierungsverfahren sind nur sinnvoll, wenn die serverseitige Generierung des HTML-Codes wirklich schneller ist als die Übertragung zum Client. Wenn ihr mehrsekündige SQL-Abfragen ausführt, solltet ihr auf Komprimierung lieber verzichten, da die Komprimierung stets warten muss bis das gesamte HTML-Dokument fertig ist. Wenn ihr also solch lange Abfragen habt und diese auch nicht verbessert bekommt, dann solltet ihr auf Komprimierung verzichten, da der User dann zumindest schon etwas von der Seite sieht und nicht ewig warten muss.
Also: Erster Schritt ist die Optimierung der SQL-Abfragen bzw. allgemein der Seitengenerierung (eventuell durch Caching) und anschließend die GZIP-Komprimierung.

Nun gibt es mehrere Möglichkeiten die Komprimierung vorzunehmen: mod_gzip, mod_deflate, ob_gzhandler.

mod_gzip und mod_deflate
Der Apache-Webserver (und andere auch, aber ich möchte es anhand des Apache beschreiben) unterstützt die GZIP-Komprimierung ohne Code-Veränderung der Anwendung. Das bedeutet ihr müsst nicht eure ganzen Scripte umschreiben, sondern steuert alles über besagte Module mod_gzip und mod_deflate sowie die .htaccess-Datei. Ob mod_gzip oder mod_deflate derzeit installiert ist, lässt sich einfach per phpinfo() prüfen.

Die Komprimierung mit mod_deflate aktiviert man nun, indem folgende Zeilen in die .htaccess geschrieben werden:

# Komprimierung nach MIME-Type: Text, HTML und XML komprimieren AddOutputFilterByType DEFLATE text/html text/plain text/xml       # ODER: bestimmte Dateiendungen komprimieren (hier .html) <Files *.html> SetOutputFilter DEFLATE    </Files>
Nun werden also alle angegebenen Dateien gepuffert und komprimiert.

Eine andere feine Geschichte ist es, dass man nicht nur HTML sondern auch andere Textdaten komprimieren kann (CSS, JavaScript und was man sonst noch so hat):

<Location />     SetOutputFilter DEFLATE      SetEnvIfNoCase Request_URI  \            \.(?:gif|jpe?g|png)$ no-gzip dont-vary      SetEnvIfNoCase Request_URI  \            \.(?:exe|t?gz|zip|gz2|sit|rar)$ no-gzip dont-vary    </Location>
Diese Variante komprimiert alle Daten außer Bildern und Binärdaten (exe sowie bereits komprimiete Archive).

Wer mod_gzip hat, der muss einen anderen Code in die .htaccess eintragen.

<IfModule mod_gzip.c>       mod_gzip_on       Yes        mod_gzip_dechunk  Yes        mod_gzip_item_include file      \.(html?|txt|css|js|php|pl)$        mod_gzip_item_include handler   ^cgi-script$        mod_gzip_item_include mime      ^text/.*        mod_gzip_item_include mime      ^application/x-javascript.*        mod_gzip_item_exclude mime      ^image/.*        mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*    </IfModule>
Hierbei wird die Entscheidung, ob eine Datei komprimiert werden soll (oder nicht) über reguläre Ausrücke der Dateiendung oder des MIME-Typs getroffen.

ob_gzhandler
Was aber, wenn man ein Shared-Hosting-Paket hat und der Webhoster mod_gzip und mod_deflate weder unterstützt noch bereit ist zu installieren? Dann kann man immernoch PHP-Funktionen verwenden. Das funktioniert über die Funktion ob_gzhandler.
Dazu muss in jede Datei, die man komprimieren möchte, folgender Code ganz am Anfang eingefügt werden:

<?php    ob_start("ob_gzhandler");     ?>

Man muss dabei nicht selbst überprüfen, ob der Client GZIP unterstützt, da das die Funktion selbst übernimmt. Das einzige Problem an dieser Variante ist, dass wirklich alle zu komprimierenden Dateien bearbeitet werden müssen, es sei denn …

… auto_prepend_file
Wer nicht alle Dateien einzeln bearbeiten möchte, kann einfach eine PHP-Datei mit dem ob_start(„ob_gzhandler“) erstellen (nur diesen Code, nichts weiter) und sagt dem Apache anschließend, dass er diese Datei vor dem Bearbeiten einer anderen Datei einfügen soll, indem folgender Code in die .htaccess eingefügt wird:

&lt;FilesMatch "\.(txt|html|htm|php)"&gt;       ForceType application/x-httpd-php        php_value auto_prepend_file /the/full/path/gzip-enable.php &lt;/FilesMatch&gt;
Dieser Code besagt, dass die Datei immer eingefügt werden soll, wenn die Dateiendung txt, html, htm oder php ist. Anschließend wird der Webserver angewiesen diese Datei zwingend durch den PHP-Parser zu schicken (sonst hätte man bei Aufruf einer txt-Datei einfach den Code ob_start(„ob_gzhandler“) am Anfang stehen). Und zuletzt wird noch der Pfad zu dem soeben erstellten Script angegeben.
Wem diese Variante gefällt, der kann natürlich die Prepend-Dateien auch für andere Dateitypen erweitern (CSS und JS – Achtung, hier muss der Content-Type gesetzt werden) und die so komprimierten Dateien auch noch cachen:

<?php         // Datenpufferung aktivieren, am Ende gesamten Inhalt an ob_gzhandler übergeben     ob_start ("ob_gzhandler");         // korrekten Content-Type setzen     header ("content-type: text/css; charset: UTF-8");         // Client-Cache anweisen sicht strikt an diese Anweisungen zu halten     header ("cache-control: must-revalidate");         // wie lange soll der Content gecached werden     $offset = 60 * 60;         // Ablauf-Datum setzen     $expire = "expires: " . gmdate ("D, d M Y H:i:s", time() + $offset) . " GMT";         // Header senden    header ($expire);    ?>

Und dann das hier in die .htaccess:

<FilesMatch "\.(css)">       ForceType application/x-httpd-php           php_value auto_prepend_file "/the/full/path/of/this/file/gzip-css.php"       </FilesMatch> 

Falls ihr noch weitere Möglichkeiten kennt zum Komprimieren oder sinnvolle Erweiterungen für die hier angegebenen Codes, würde ich mich über Kommentare freuen!

php

24.09.2019 21:36:36

PHP-Script-Caching

Caching Technologien auf dem eigenen Server können eine Menge an Performance ausmachen. Solche Cache-Software wirbt mit teilweise über 400%iger Beschleunigung des Codes – was natürlich sehr attraktiv ist.
Und tatsächlich bringen diese Beschleuniger etwas.

Das ganze funktioniert recht simpel: Jedes PHP-Script muss vor der Ausführung (logischerweise) kompiliert werden. Die Cache-Software hält das Skript im Kompilierten Zustand in einem Cache und stellt bei erneutem Aufruf des Skript die bereits kompilierte Version direkt zur Verfügung. Der gesamte Kompilierungs-Overhead muss somit nicht wiederholt werden.

Die zwei besten Beschleuniger sind der ZendOptimizer von Zend Inc. und der nicht-kommerzielle (OpenSource) Turck MMcache.

Der ZendOptimizer bringt ein einfaches Installations-Script mit sich welches den Optimizer selber installiert. Die MMcache Version ist recht einfach zu kompilieren und über die php.ini einzubinden.

Ich empfehle MMcache weil diese Version frei ist und mehr Einstellungsmöglichkeiten bietet als der ebenfalls „frei erhältliche“ ZendOpti.

Wer übrigens nicht auf den Server zugreifen bzw. dort Programme kompilieren kann, dem empfehle ich JP Cache. Bei diesem Cache Tool handelt es sich um eine ähnliche Funktionsweise wie bei Ausgaben in Cache speichern. Allerdings kann man die Ausgaben auch in einer Datenbank speichern und es sind einige „Komfortfunktionen“ enthalten, wie das Senden eines 304-Headers (Not modified).

Der Turck MMCache wird nicht mehr weiterentwickelt bzw. rangiert er mittlerweile unter neuem Namen: E Accelerator

php

24.09.2019 21:35:52

Datei, bist du da?

Nachdem schon ein paar Mal angefragt wurde, was am Performantesten ist auf die Existenz einer Datei zu prüfen, hab ich ein paar wenige Funktionen in Augenschein genommen. Darunter hab ich für den lokalen Zugriff: file_exists und die jeweiligen aufgeschlüsselten Funktionen: is_file und is_dir getestet, sowie für dem remote Zugriff: get_headers, fsockopen, file_get_contents und curl.

Zuerst möchte ich mit den lokalen Varianten anfangen:
Es gibt keine Großen Unterschieden, doch zeigen über mehrere Testläufe die direkten Funktionen: is_file und is_dir einen kleinen Vorsprung.
Der Benchmark lief mit 100.000 Durchläufen und das 100 mal um einen guten Mittelwert zu erhalten, nachdem es ja auch immer auf den Load des Rechners ankommt. Doch auch nach mehrmaligem Durchlaufen beläuft sich das Ergebnis immer darauf, dass file_exists bei 100.000 Zugriffe um 0.03-0.05s langsamer ist. Das bedeutet, file_exists ist als einzelner Aufruf, wenn wir den Aufruf von clearstatcache() nicht beachten um 0.3-0.5μs langsamer.

file_exists  -&gt; is_file  0.522522807121 -> 0.471928157806  0.634317629337 -> 0.579254751205  0.525248453617 -> 0.468847446442

auch meine späteren Tests mit Ordnern haben das Ergebnis noch einmal bestätigt.

datei  -> file_exists: 0.56094949007  datei  -> is_file : 0.503009655476  ordner -> is_dir : 0.503238601685  ordner -> file_exists: 0.558529658318

Man sollte also, wenn man weiß, dass es sich um eine Datei oder einen Ordner handelt eher auf is_file oder is_dir zurückgreifen um dessen Existenz zu überprüfen. Ich frag mich woran das liegt und wenn jemand Lust hat, kann er gerne mal Code wälzen. Meine Vermutung ist, dass file_exists erst prüft, was es ist und dass das den Overhead ausmacht.

Man sollte hier noch dazusagen, dass sehr viele File-Funktionen in PHP ihre Rückgabe cachen. Das bedeutet, wenn ihr einmal den Status abgefragt habt, dann wird er beim nächsten mal aus dem Cache geladen. Wenn ihr also 2 mal den Status einer Datei in einer Datei abfragen wollt, müsst ihr UNBEDINGT den Cache wieder mit clearstatcache() löschen (sonst erhaltet ihr beim zweiten Aufruf die gleiche Rückgabe wie beim ersten Mal)

Wie sieht es nun mit Remote-Files aus. Hier unterscheiden sich die Ergebnisse extrem, das hat auch mehrere Gründe. Am Performantesten ist wohl eine eigene Socket-Variante. Aber hier muss ich dazu erwähnen, dass ich nur auf die Existenz einer Datei geprüft habe, deswegen hab ich auch über den Socket nicht die komplette Datei geholt, sondern nur die ersten 1024 Bytes um den Status Code auszulesen. Damit war es sogar noch schneller als get_headers.
Ich habe nicht probiert diese 1024 Bytes noch zu tweaken und nur soviele Bytes zu holen, wie ich wirklich für den Status Code bräuchte (damit könnte man sicher nochmal ein paar Micro/Nano-Sekunden rausholen).

Natürlich schwanken diese Ergebnisse von Server zu Server und von Datei zu Datei, aber es sollte klar sein, dass, wenn man nur einen Teil der zurückgelieferten Nachricht braucht, es deutlich schneller ist sich nicht die ganze Datei zu holen.
Der Vollständigkeit halber hier die kompletten Testergebnisse:

Ergebnisse:  get_headers: 0.90237588644  file_get_contents: 4.92363333464  fsockopen: 0.611620130539  curl: 4.66347056866

Zu den Durchläufen und Mittelwerten muss ich auch noch etwas sagen. Ich konnte nicht soviele Durchläufe machen (ist ja doch recht server-belastend). Der Benchmark ist mit 5 Iterationen gelaufen und das 100mal. D.h. fsockopen hat im Mittelwert 0.611s gebraucht um 5mal eine GET Abfrage rauszuschicken und den Status Code auszulesen. Die Abfrage lief auf ein Bild, dass auf einem Server liegt. Bildgröße weiß ich leider nicht, ist aber auch nicht entscheidend.

Anders sieht es natürlich aus, wenn man noch den Inhalt der Datei braucht, dann würde ich auf jeden Fall auf curl zurückgreifen.

Wenn ihr noch Möglichkeiten habt, auf die Existenz zu testen, dann Benchmarke ich die gerne noch. Bitte beschreibt dieVariante einfach in einem Kommentar.

hinzugefuegt:
Nachdem es Anscheinend nicht ganz klar geworden ist, wie ich Benchmarke, füge ich hier mal einen Teil des Bench.-Codes hinzu:

1 2 3 4 5 6 7 8 9 
for($a = 0; $a < 100; $a++) {     $bench->mark('1');     for($i = 0; $i < 100000; $i++) {         file_exists($file);         clearstatcache();     }     $bench->mark('2'); } print $bench->print('2-1');

was er macht ist: er lässt 10^5 mal durchlaufen auf 1ne datei zuzugreifen…misst aber alle 100.000 zugriff in einer Zeit. Alle Ergebnisse die oben sind, sind im prinzip 100.000 Zugriffe.
Der Benchmark für die Remote sachen sehen nicht anders aus, ausser, dass dort nur 10 durchläufe in der inneren Schleife gemacht wurden, aber 100 in der äusseren, um ein möglichst breites feld zu kriegen ????
Hoffe, das hat ein paar Sachen geklärt

php

24.09.2019 04:07:25

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 21:36:28

Feststellen, ob eine Zahl ungerade / gerade ist

Oft möchte man in der täglichen Web-Programmier-Praxis feststellen, ob eine Zahl gerade oder ungerade ist (gerade = durch 2 ganzzahlig teilbar). Ich selbst gebrauche das gern, um Übersicht in Artikellisten zu schaffen, indem ich abwechselnd die Hintergrundfarbe mal heller und mal etwas dunkler darstelle. Das ist wesentlich übersichtlicher als die Variante alle mit gleichem Hintergrund aufzulisten. Wie nun aber prüft man möglichst performant, ob eine Zahl gerade oder ungerade ist?

Ich möchte kurz alle hier zu testenden Varianten vorstellen:

// Variante 1 - die Amateurhafte $o = 0; for($i=0;$i<1000;$i++) {   echo $i." ist ";   $o++;   if($o==1) echo "ungerade";   else {     echo "gerade";     $o=0;   } }   // Variante 2 - die Mathematische for($i=0;$i<1000;$i++) {   echo $i." ist ";   if($i%2) echo "ungerade";   else echo "gerade"; }   // Variante 3 - die Binäre for($i=0;$i<1000;$i++) {   echo $i." ist ";   if($i & 1) echo "ungerade";   else echo "gerade"; }   // Variante 4 - die Kurze for($i=0;$i<1000;$i++) {   echo $i." ist ";   echo ($i & 1)?"ungerade":"gerade"; }

Die 1. Variante ist sicherlich nicht oft umgesetzt, wird aber oft von Anfängern als erster Vorschlag geliefert. In diesem Fall wird eine zweite Zählvariable mitgeführt, die zwischen 1 und 2 rangiert (sicherlich kommt sie auch auf 0, das ist aber nicht relevant für die Überprüfung, ob eine Zahl gerade ist). Aber das ständige Inkrementieren und Zurücksetzen kostet unnötig Zeit, zumal wir ja bereits eine Zählvariable von der for-Schleife haben – und die sollten wir dann auch nutzen.

In der 2. Variante wird die Modulo-Operation genutzt. Diese gibt den ganzzahligen Rest einer Division zurück. Es ist demzufolge klar, dass wenn bei der Division durch 2 ein Rest bleibt, dass die Zahl dann ungerade ist. Und weil in PHP true==1 kann man die if-Abfrage ohne Vergleich schreiben.

Die 3. Variante kann nur von einem Informatiker stammen ????
Hierbei wird die binäre Operation & 1 ausgeführt. Dazu wird die letzte Stelle der Zählvariable in Binärschreibweise mit 1 und-verknüpft (wichtig hierbei: nicht && schreiben, da das lediglich Prüfen würde, ob $i > 0!). Ist die letzte Stelle eine 0, so ergibt 0 & 1 = 0. Ist sie eine 1, so ist 1 & 1 = 1 (0 steht hierbei für false und 1 für true). Und genau das ist das gewünschte Ergebnis, da die letzte Binärstelle für 20 zuständig ist und demzufolge exakt definiert, ob die Zahl gerade oder ungerade ist.
Diese Variante folgt wohl dem Motto

There are 10 types of people: those who understand binaries and those who don’t

????

Die 4. Variante hat nur den Zweck den trinären Operator (=Konditionaloperator) zu testen. Die Logik ist die gleiche wie bei Variante 3, könnte aber genauso gut mit dem Modulo-Operator umgesetzt werden. Vorteil dieser Variante ist, dass der Code sehr schön kurz ist.

So, nun genug Theorie, hier sind die Ergebnisse.

Datei Gesamtlaufzeit durchschnittliche Laufzeit pro Durchlauf Verhältnis zur schnellsten Variante
ungerade_binary.php 24.785640 s 2.479 ms 100 %
ungerade_mod.php 25.486648 s 2.549 ms 103 % (+ 3%)
ungerade_binary_ trinaer.php 26.718419 s 2.672 ms 108 % (+ 8%)
ungerade_counter.php 26.838592 s 2.684 ms 108 % (+ 8%)

Am schnellsten in die Binär-Und-Verknüpfung. Das ist auch nicht weiter verwunderlich, da die (meisten) heutigen Rechner nunmal im Binärmodus arbeiten und somit damit auch am schnellsten zurechtkommen. Die Modulo-Operation ist etwas langsamer, aber noch akzeptabel.
Etwas überrascht hat mich die Performance des trinären Operators. Hätte gedacht, dass dieser nicht nur kurz sondern auch flott ist, aber dem ist nicht so. Werde dann also in Zukunft lieber „richtige“ if-else-Reihen aufbauen.
Und nicht überraschend ist die Langsamkeit der zusätzlichen Zählvariable. Auch wenn diese Variante genauso langsam ist wie mit dem Konditionaloperator, habe ich sie rot markiert, weil sie einfach anfängerhaft ist und in keinem Projekt vorkommen sollte. Dazu kommt noch, dass sie zusätzlichen Speicher verschwendet durch die zusätzliche Variable.

Zum Nachprüfen stelleich wie immer auch die Quellcodes sowie die Benchmark-Ergebnisse zur Verfügung.

php