PSR-2: Coding Style Guide

Die PSR-2 Spezifikation beinhaltet Vorgaben zur Formatierung einer PHP-Datei. Sich an diese Richtlinien zu halten, ist vor allem dann sinnvoll, wenn man mit anderen Leute zusammen an einem Projekt arbeitet. Halten sich alle an die gleichen Stilvorgaben, verschwendet niemand Zeit, sich erst in fremden Code einzulesen.

1. Allgemeines

Die erste Voraussetzung ist, dass sich der Code ebenfalls an die Vorgaben der PSR-1 Spezifikationen hält, welche sich mit dem allgemeinen Programmierstil befasst.

Weitere Vorgaben sind:

  • Alle PHP-Dateien müssen den UNIX LF als Zeilenumbruch benutzen
  • Die Datei muss mit einer Leerzeile abschließen
  • Bei einer reinen PHP-Datei entfällt das schließende ?> Tag
  • Einrückungen bestehen aus 4 Leerzeichen
  • Keywords wie true, false und null werden klein geschrieben
  • Nach der öffnenden Klammer und vor der schließenden keine Leerzeichen
  • Befinden sich eine schließende Klammer und eine geschweifte öffnende Klammer in einer Zeile, so werden sie durch ein Leerzeichen getrennt
  • Bei Aufzählungen kommt vor dem Komma kein Leerzeichen, danach schon

Zeilen

  • Zeilen sollten nicht länger als 80 Zeichen sein
  • Sie dürfen nicht länger als 120 Zeichen sein
  • Keine Leerzeichen am Ende einer Zeile
  • Nicht mehr als ein Statement pro Zeile
  • Leerzeilen dürfen zum Zwecke der Übersichtlichkeit hinzugefügt werden

Namespaces und use-Deklarationen

  • Nach namespace-Deklarationen muss eine Leerzeile folgen
  • Alle use-Deklarationen stehen unterhalb der namespace-Deklaration
  • Pro use-Deklaration muss ein use-Keyword benutzt werden
  • Nach dem use-Block muss eine Leerzeile folgen
<?php
namespace Bereich\Package;
use EinInterface;
use IrgendeineKlasse as EineKlasse;
use AndererBereich\ZweitesPackage\MeineKlasse;
...

2. Klassen, Methoden und Attribute

  • abstract und final-Deklarationen stehen der Sichtbarkeit-Deklaration voran
  • Die static-Deklaration kommt nach der Sichtbarkeit
  • Beim Aufruf einer Funktion kommt kein Leerzeichen zwischen den Methoden- oder Funktionsnamen und der öffnenden Klammer

Extends und implements

  • Die Schlüsselwörter extends und implements müssen in der gleichen Zeile deklariert werden wie Klassenname
  • Die öffnende geschweifte Klammer kommt in eine eigene Zeile
  • Die schließende geschweifte Klammer kommt in die Zeile nach dem body
  • Eine Liste von implements kann in mehrere Zeilen geschrieben werden, dabei muss jedes Element in eine eigene Zeile geschrieben werden

Attribute

  • Bei allen Attributen muss die Sichtbarkeit angegeben werden
  • Das var-Keyword darf nicht zum Deklarieren von Attributen benutzt werden
  • Pro Statement darf nur ein Attribut deklariert werden
  • Keine Präfixe benutzen, die die Sichtbarkeit eines Attributs wiedergeben

Methoden

  • Bei allen Methoden muss die Sichtbarkeit angegeben werden
  • Keine Präfixe verwenden, die die Sichtbarkeit einer Methode anzeigen sollen
  • Nach dem Methodennamen kommt kein Leerzeichen, sondern sofort die öffnende Klammer
  • Nach der öffnenden Klammer und vor der schließenden kommt kein Leerzeichen
  • Die öffnende geschweifte Klammer kommt in eine eigene Zeile nach dem Methodennamen
  • Die schließende geschweifte Klammer kommt in die Zeile nach dem Body der Methode
  • Default-Argumente kommen ans Ende der Liste der Argumente
  • Eine Liste von Argumenten kann wie bei implements über mehrere Zeilen geschrieben werden, dabei gilt: jedes Argument kommt in eine eigene Zeile
class NeueKlasse extends EineKlasse implements EinInterface
{
 public function beispielFunktion($a, $b, $c = 2)
 {
  if ($a == $c) { 
   diesunddas();
  } elseif ($b == $c) { 
   diesundjenes($b);
  } else { 
   MeineKlasse::tudas($a, $b);
  }
 }

  final public static function irgendwas()
 {
  machwas();
 }
}

3. Kontrollstrukturen

  • Nach dem Kontrollstruktur-Schlüsselwort kommt ein Leerzeichen
  • Kein Leerzeichen nach der öffnenden und vor der schließenden Klammer
  • Zwischen der schließenden Klammer und der öffnenden geschweiften Klammer muss ein Leerzeichen sein
  • Der Körper der Kontrollstruktur muss einmal eingerückt sein
  • Die schließende Klammer steht in der Zeile nach dem body

if-else

  • Das else und das elseif befinden sich in der gleichen Zeile wie die geschweifte schließende Klammer des if-Körpers
  • elseif sollte statt else if genutzt werden

switch-case

  • Das case-Statement muss einmal eingerückt werden
  • Der case-body wird noch einmal eingerückt, das break-keyword befindet sich auf der gleichen Ebene wie der body
  • Wenn das break in einem nicht-leeren case weggelassen wird, muss ein Kommentar wie //no break eingefügt werden

4. Closures

  • Closures müssen mit einem Leerzeichen nach dem function-keyword und einem vor und nach dem use-keyword deklariert werden
  • Die öffnende geschweifte Klammer kommt in die gleiche Zeile wie die Deklaration
  • Die schließende geschweifte Klammer kommt in die Zeile nach dem body
  • Kein Leerzeichen nach der öffnenden Klammer oder der schließenden Klammer der Argumenten- Liste




Weitere Informationen sind auf der Seite http://www.php-fig.org/psr/psr-2/ von der PHP Framework Interop Group zu finden.

imagecreatefromjpeg() recoverable error: Premature end of JPEG file

Freitag, kurz vor Feierabend und ein guter Kunde meldet, dass beim Webupdate seines ERP-Systems Fehler auftreten. Nach kurzer Analyse stellt sich folgender PHP5-Fehler bzw. Warnung heraus:
„imagecreatefromjpeg() recoverable error: Premature end of JPEG file“ oder zu deutsch: in der Funktion imagecreatefromjpeg() wird ein JPEG-Bild eingelesen, das eine Warnung auswirft, weil wohl ein anderes Dateiende erwartet wird.

Nach kurzer Recherche bin ich auf Probleme in Zusammenhang mit der Bilderbibliothek GD2 und PHP5, die bis in das Jahr 2008 zurückreichen, gestoßen.
Verschiedene Kameras als auch Fotoprogramme speichern die JPEG. bzw. JPG-Bilder nicht so, wie GD2 diese gerne hätte. Hier die Lösung für verschiedene PHP Versionen:

php 4: Keine Anpassung erforderlich, alles sollte laufen

php 5.0 – 5.1.2: Führe ein Upgrade auf die letzte php 5-Version durch

php 5.1.3 – Aktuelle: = Setze folgende Einstellung in deiner PHP-Datei bevor die Funktion magecreatefromjpeg() aufgerufen wird:

ini_set('gd.jpeg_ignore_warning', TRUE);

Nach dieser Änderung wird GD2 diesen Fehler ignorieren und auch ein nicht erwartungsgemäßes Ende der Datei hinnehmen.

Damit sollte der Fehler „imagecreatefromjpeg() recoverable error: Premature end of JPEG file“ gelöst sein!

PHP De-Obfuscation

In den letzten Tagen hatte ich viel mit obfusciertem PHP-Quellcode (Frameworks & Schadcode) zu tun, der zumeist über einen langen String definiert war und dann mittels einfacher eval()-Funktion erst zur Laufzeit das ein oder andere dunkle Geheimnis preisgab.

Das erste Hindernis besteht darin, diesen String in lesbaren Quellcode zu formatieren. Nach kurzer Recherche im Web und ein paar Experimenten hatte ich folgendes kleine PHP-Script:

ob_start();
eval("?" . ">" . base64_decode( $code) . "<" . "?php");
$output = ob_get_contents();
ob_end_clean();
echo $output;

1. Die Funktion ob_start() aktiviert die Pufferung von Ausgaben ein. Während die Ausgabe-Pufferung aktiv ist, erfolgt keine Ausgabe an den Client. Stattdessen werden alle Ausgaben in einem internen Puffer gespeichert.

2. Die Funktion eval() wertet einen String als PHP-Code aus. Sie ist nützlich, wenn man bspw. PHP-Code aus einer Datenbank lädt. Für dieses Experiment jedoch wäre sie ungeeignet, da der PHP-Quellcode direkt ausgeführt würde. Wenn der String mit „?>“ (PHP endet) beginnt, wird der Code nicht ausgeführt und man hat eine saubere Ausgabe. Die Funktion base64_decode() war nötig, da der Code base64-kodiert war.

3. Mit der Funktion ob_get_contents() wird der Inhalt des internen Puffers in eine String-Variable kopiert. Die String-Variable enthält sämtliche Ausgaben seit dem Befehl ob_start().

4. Die Funktion ob_end_clean() löscht daraufhin den internen Ausgabepuffer und beendet das Outputbuffering.

5. Der Quellcode steht u.U immer noch in einer Zeile ist jedoch lesbar. Er kann nun angesehen und mit einer leistungsstarken IDE formatiert werden.

Im Quellcode der gegebenen PHP-Datei sind die Texte ASCII-codiert und die Strings deshalb nicht lesbar. Alle Zeichen wurden mit dem entsprechenden ASCII-Code abgebildet (z.B.: „\x73“). Das berechnen des Quellcodes mit eval() hilft hier nur soweit man während der Laufzeit noch auf diese Strings lesbar zugreifen kann. Möchte man jedoch schon den Quellcode lesen können, hilft die Funktion str_replace() weiter:

$output = str_replace("\\x73", "s", $input);

Der doppelte Backslash „\\“ ist nötig, um den Backslash selber zu escapen.

Um das an dieser Stelle zu beschleunigen:

$ascii_hex = array(
"\\x20", "\\x21", "\\x22", "\\x23", "\\x24", "\\x25", "\\x26", "\\x27", "\\x28",
"\\x29", "\\x2a", "\\x2b", "\\x2c", "\\x2d", "\\x2e", "\\x2f", "\\x30", "\\x31",
"\\x32", "\\x33", "\\x34", "\\x35", "\\x36", "\\x37", "\\x38", "\\x39", "\\x3a",
"\\x3b", "\\x3c", "\\x3d", "\\x3e", "\\x3f", "\\x40", "\\x41", "\\x42", "\\x43",
"\\x44", "\\x45", "\\x46", "\\x47", "\\x48", "\\x49", "\\x4a", "\\x4b", "\\x4c",
"\\x4d", "\\x4e", "\\x4f", "\\x50", "\\x51", "\\x52", "\\x53", "\\x54", "\\x55",
"\\x56", "\\x57", "\\x58", "\\x59", "\\x5a", "\\x5b", "\\x5c", "\\x5d", "\\x5e",
"\\x5f", "\\x60", "\\x61", "\\x62", "\\x63", "\\x64", "\\x65", "\\x66", "\\x67",
"\\x68", "\\x69", "\\x6a", "\\x6b", "\\x6c", "\\x6d", "\\x6e", "\\x6f", "\\x70",
"\\x71", "\\x72", "\\x73", "\\x74", "\\x75", "\\x76", "\\x77", "\\x78", "\\x79",
"\\x7a", "\\x7b", "\\x7c", "\\x7d", "\\x7e", "\\x7f"
);

$ascii_char = array(
" ", "!", '"', "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?",
"@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P",
"Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\", "]", "^", "_", "`", "a",
"b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
"s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~"
);

$output = str_replace($ascii_hex, $ascii_char, $input);

So hat man schnell den lesbaren Text /PHP-Quellcode.

Sicherlich ist das nicht die eleganteste Lösung und ich lasse mich gerne von schöneren überzeugen!

Ein weiterer Teil der Strings ist wieder base64-kodiert, wie bspw.:

$output = base64_decode("aHR0cDovL3d3dy5kaXJlY3Qtd2Vic29sdXRpb25zLmRlL2Jsb2cvd2ViZGVzaWduL3BocC1kZS1vYmZ1c2NhdGlvbi10ZWlsLTMuaHRtbA==");

Um dieses Problem zu lösen, hilft die PHP-Funktion preg_replace_callback():

function decode($erg) {
    return '"' . base64_decode($erg[1]) . '"';
}
$output = preg_replace_callback("/base64_decode\('([a-z0-9+=]*)'\)/i", 'decode', $input);

Generierung einer PHP-Dokumentation mit NetBeans

PHPDoc ist eine Dokumentationslösung für PHP. Aus der Dokumentation können dann mit bspw. phpDocumentor später die Beschreibung für APIs oder die Entwicklerdokumentationen generiert werden. Auch einige IDEs wie NetBeans oder Zend Studio greifen auf diese Kommentare zurück und benutzen sie für die Codevervollständigung und Beschreibung.

Um eine solche Dokumentation mit NetBeans unter Windows mit dem phpDocumentor zu generieren, muss dieser zunächst installiert werden. Dafür ruft man am besten den Kommandozeileninterpreter von Windows auf (Start → Ausführen → „cmd“). Wichtig ist, dass das aktuelle Verzeichnis dem Benutzerverzeichnis entspricht (%HOMEPATH%). Hier gibt man dann den folgenden Befehl ein:

pear.bat install --alldeps PhpDocumentor

Dies setzt natürlich voraus, dass der Pfad zu PEAR (PHP Extension and Application Repository) in den Pfad-Variablen eingetragen ist. Der Befehl sorgt letztlich dafür, dass die benötigten Erweiterungen und deren Abhängigkeiten für den phpDocumentor heruntergeladen und eingerichtet werden.

Im Benutzerverzeichnis wird nun ein neues Verzeichnis „pear“ mit diversen Unterverzeichnissen und Dateien erstellt.

Damit NetBeans nun noch auf die Konfigurationsdatei unter „%HOMEPATH%\pear\data\PhpDocumentor\phpDocumentor.ini“ zugreifen kann muss eine kleine Änderung an der Datei phpdoc.bat vorgenommen werden. Die Datei liegt in dem PHP-Verzeichnis von Xampp. Damit das Skript die richtige Konfigurationsdatei findet, muss das Verzeichnis gewechselt werden. Dazu muss „CD %HOMEPATH%“ in der Datei eingetragen werden (am einfachsten am Anfang).

Bei Verwendung von PHP 5.3 sollte die php.ini Datei kontrolliert werden und ggf. angepasst werden. Wie unten gezeigt, sollte die folgende Zeile kein Kommentar sein:

"date.timezone = "Europe/Berlin"

Dieses findet man wenn man nach dem folgendem Block sucht:

[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = "Europe/Berlin"

Nun kann man NetBeans starten und unter „Tools → Options → PHP → PhpDoc“ den Pfad auf seine „phpdoc.bat“ Datei setzen. Der Button zur Suche funktioniert nur, wenn der Pfad in der PATH-Variable hinterlegt ist. Optionen kann man direkt hinter der Datei eintragen wie: „-o HTML:frames:earthli“. Auch andere Formate für das spätere PhPDoc sind möglich.

Mit einem Rechtsklick auf sein Projekt kann man den Menüpunkt „Generate PhpDoc“ auswählen. Bei dem ersten Aufruf wird man zur Auswahl des Ziel-Verzeichnisses aufgefordert. Das Verzeichnis kann man über den Browser auswählen, muss dann jedoch die Backslashes in normale Slashes umwandeln („\“ → „/“), da andernfalls NetBeans kommentarlos die Generierung verweigert! So kann man nun schnell und ohne Umwege die PHP Dokumentation unter NetBeans 7.0 unter Windows ausführen.