Coding Guidlines – 10 Vorschläge und einige Zweifel

In letzter Zeit habe ich mehrfach bei verschiedenen kleineren Projekten, v.a. im PHP-Bereich gelesen, dass sogenannte “Coding Guidlines” entworfen bzw. einfach festgesetzt werden. Ob diese nun jedoch in der Praxis wirklich umsetzbar bzw. sinnvoll sind, darüber machen sich nur die wenigsten Gedanken und wundern sich, warum die anderen Entwickler meckern oder warum sie selbst Probleme bei der Einhaltung haben.

Grundsätzlich lässt sich jedoch sagen, dass Coding Guidlines, d.h. Abmachungen über den für das Projekt einzuhaltenden Programmierstil, äußert sinnvoll sind, da es ja durchaus zu Problemen führen kann, wenn jeder nach seinem Stil arbeitet und nun zwei Personen nacheinander (oder noch schlimmer: nebeneinander) an der gleichen Datei arbeiten sollen.

Auch lassen sich durch einen durchdachten Programmierstil einige Fehler vermeiden, welche jedoch syntaktisch korrekt sind und deswegen nur äußerst schwer zu finden sind. Bei kommerziellen Projekten kann ein solcher Fehler viel Geld kosten!

1. Klammern bei Kontrollstrukturen
Bei vielen Programmiersprachen gibt es die Möglichkeit, z.B. bei if-Anweisungen, welche lediglich einen einzigen Befehl enthalten würden, die Klammern wegzulassen.

if ($user->isAdmin) doSomeAdminThings();

Dieser Code ist syntaktisch korrekt und arbeitet auch so, wie es erwartet wird. Wenn Adminrechte verfügbar sind, dann wird die Funktion doSomeAdminThings() ausgeführt. Wenn wir nun jedoch nach einiger Zeit eine weitere Funktion einfügen, so kann dies zu unersichtlichen Fehlfunktionen führen.

if ($user->isAdmin) doSomeAdminThings(); doSomeAdminWork();

Beim Überfliegen des Quellcodes wird dieser Fehler nur von den wenigsten bemerkt. Jeder erwartet, dass sowohl doSomeAdminThings() als auch doSomeAdminWork() lediglich ausgeführt werden, wenn der Benutzer Administrator ist. Doch kann man nun feststellen, dass die Funktion doSomeAdminWork() immer ausgeführt wird, egal ob der User Admin ist oder nicht. Grund hierfür ist, dass lediglich ein Befehl von der If-Bedingung ohne Klammern umschlossen wird.

if ($user->isAdmin) 
{
       doSomeAdminThings(); 
       doSomeAdminWork();
}

Forderung: Um Probleme im Programmfluss durch fehlerhafte Programmierung zu vermeiden sollten alle Kontrollstrukturen immer mit Klammern versehen werden.

2. Kommentare im Quellcode
Wenn es um das Kommentieren von Quellcode geht, dann scheiden sich die Geister. Manche Projekte fordern, dass alles bis ins letzte Detail beschrieben wird, andere lehnen jegliche Kommentare strikt ab und fordern, dass alles einfach klar ersichtlich sein muss.
Als erstes zum Thema “bis ins letzte Detail” mit Kommentaren versehen.

// Addition von a und b
$c = $a + $b;
// Ausgabe der Summe von $a und $b, repräsentiert durch die Variable $c
echo $c;

Ich denke jedem wird bereits beim ersten Betrachten klar, dass hier eindeutig übertrieben wurde, da hier eindeutig ohne Probleme erkannt werden kann was passiert.

...
for ($i=count($itemsOfVar) - count($itemsOfOthers); $i<count(base::getObj('objects')->userCounter); $i = $i * 2)
...

Eine solche Quellcode-Zeile, welche den Programmierer auf den ersten Blick mit Informationen erschlägt, sollte unbedingt kommentiert werden. Wenn jemand Fremdes diesen Code sieht, so wird er nur mit einem größeren Zeitaufwand erschließen können, was genau geschieht.

Forderung: Beim Kommentieren sollte der gesunde Menschenverstand genutzt werden. Es sollte nicht übertrieben werden, jedoch kann man grundsätzlich sagen, dass eine Kommentarzeile zu viel nicht so sclimm ist, wie eine Kommentarzeile zu wendig!

3. Type-Hints verwenden
In vielen Foren höre ich oft den Kommentar “Ich weiß schon was ich wann und warum meinen Funktionen übergebe” wenn ich zu bedenken gebe, dass in Klassen keine Type-Hints verwendet worden sind. Die meisten sehen Type-Hints als etwas an, dass man lediglich dazu benutzt, um anderen Programmierern eine Hilfestellung zu geben, man selbst habe das anscheinend nicht nötig.

public function addDatabase(abstractDatabaseObject $dbObj) { ... }

Nun können an die Funktion addDatabase nur noch Objekte von Klassen übergeben werden, die von abstractDatabaseObject erben, es ist also sichergestellt, dass es sich um ein DatabaseObject handelt. Natürlich könnte man nun auch innerhalb der Funktion mit if-Abfragen usw. überprüfen ob es sich wirklich um ein solches handelt, der Aufwand wäre jedoch im Vergleich gigantisch. Auch sind Type-Hints sehr nützlich wenn man eine IDE wie etwa NetBeans einsetzt, da nun die Quellcode-Vervollständigung erkennt, von welchem Type eine Variable ist.

Forderung: Type-Hints müssen bei allen Funktionen verpflichtend eingesetzt werden!

4. Verwendung von PHP-Tags
Viele PHP-Programmierer, v.a. Hobbyentwickler, neigen aus Faulheit dazu, lediglich mit Auch bei End-Tags bei Dateien, die lediglich Klassen enthalten gibt es verschiedene Meinungen, notwendig sind diese nähmlich nicht, möglich jedoch schon. Um zu Verhindern, dass eventuelle Leerzeichen o.ä. außerhalb des PHP-Codes ausgegeben werden, empfiehlt es sich bei Klassen, den End-Tag nicht zu nutzen. Grundsätzlich muss aber natürlich immer ein End-Tag verwendet werden, wenn PHP in einer gemischten Umgebung verwendet wird.

Forderung: PHP-Skripte werden immer mit <?php begonnen. Bei Dateien die lediglich Klassen enthalten wird kein abschließender PHP-Tag verwendet.

5. Nomenklatur von Variablen
Innerhalb eines Skripts ist es einer Variable grundsätzlich nicht anzusehen, von welchem Typ sie ist. Auch gibt es bei verschiedenen Programmierern verschiedene Schreibweisen, wie etwa $eineKleineKlasse oder $eine_kleine_klasse. Um ein übersichtliches Skript zu erhalten, sollte hier unbedingt einheitlich gearbeitet werden. Auch Konstante sollten klar als solche erkennbar sein.

Forderung: Temporäre Variablen werden durchgehend klein geschrieben, mehrere Wörter werden mit Unterstrichen getrennt. Klassenvariablen werden beginnen mit einem Kleinbuchstaben wenn sie Private oder Protected sind, ansonsten mit einem Großbuchstaben, mehrere Wörter werden durch Großbuchstaben getrennt. Konstante werden durchgehend groß geschrieben.

6. Getter und Setter
Aus anderen Programmiersprachen wie etwa C# ist das Konzept von Getter und Setter bekannt, welches die beiden Aktionen “Inhalt abrufen” und “Inhalt zuweisen” als Funktionen für eine Eigenschaft einer Klasse vorgibt. Auch in PHP ist diese Funktion sehr sinnvoll, da z.B. durch das fehlen einer Setter-Methode eine Information als Read-Only gekennzeichnet werden kann und zusätzliche Aktionen möglich sind, z.B. das Speichern in der Datenbank beim Setter.

Das folgende Beispiel soll den Nutzen dieser Methode verdeutlichen:

class flugzeug
{
        protected $flugzeugName;
        protected $pilotName;
 
        public function getFlugzeugName()
        {
              return $this->flugzeugName;
        }
 
        public function getPilotName()
        {
              return $this->pilotName;
        }
 
        public function setPilotName($name)
        {
              $this->pilotName = $name;
        }
}

Während der Pilot eines Flugzeuges theoretisch jederzeit wechseln kann (=> Getter und Setter), wird der Name eines Flugzeuges nicht geändert (=> Getter, jedoch kein Setter). Würde man den Flugzeugnamen nun etwa im Konstruktor übergeben so könnte er für die gleiche Instanz der Klasse nicht mehr geändert werden, da keine Setter-Methode existiert.

Forderung: Nutzung von Getter-/Setter-Methoden für den Zugriff auf öffentliche Variablen einer Klasse sowie zur Datenkapselung.

7. Übergabe von Wahrheitswerten als Parameter
Übereifrige fordern manchmal auch, dass keine Wahrheitswerte an Funktionen übergeben werden sollen, sondern dann für jede Wahrheitswertunterscheidung eine extra Funktion eröffnet werden soll. Das hierdurch lediglich Redundanz erzeugt wird, da der Code kopiert werden muss, bedenken jedoch nur wenige!

Forderung: Wahrheitswerte dürfen, soweit nicht der komplette Funktionsablauf ein anderer ist, als Parameter übergeben werden.

8. Aufteilung von Klassen auf Dateien
Vorallem bei Anfänger-Projekten findet man ewig lange PHP-Dateien, manchmal besteht sogar das gesamte Projekt lediglich aus der index.php. Von solchen, in meinen Augen, Angebereien mit Dateien die aus tausenden von Zeilen bestehen ist eindeutig abzusehen, da niemand ernsthaft behaupten kann, dass eine solche “Monsterdatei” noch wartbar ist. Durch klare Strukturierung von Klassen in Dateien ergibt sich zudem der Vorteil, dass einzelne Klassen wesentlich leichter auffindbar sind und auch einfacher hinzugeladen werden können.

Forderung: Für jede Klasse muss eine eigene Datei erstellt werden, welche den gleichen Namen wie die Klasse tragen sollte.

9. Einbinden von Klassen
Die ersten Zeilen der meisten index.php-Dateien größerer Projekte beginnen mit include-Anweisungen. Es gibt Projekte wo die index.php lediglich aus insgesamt mehreren hundert include-Befehlen besteht.

include("aaa.php");
include("aab.php");
...
include("zzz.php");

Nun argumentieren manche Programmierer, dass sie ja jede der eingebundenen Klassen unter bestimmten Umständen benötigen und sie deshalb einbinden müssen. Auch könne man die Klassen nicht erst bei der Benutzung per include einbinden, da es ja so vorkommen könnte, dass eine Datei mehrmals eingebunden wird. Dieses Problem lässt sich jedoch sehr leicht über eine Autoloader-Funktion lösen, wenn man für alle Klassen allgemeingültige Regeln bei ihrer Bennenung und Speicherung einhält. Sollte dies aufgrund zu komplexer Strukturen nicht möglich sein, so könnte man etwa auch mit einer Loader-Klasse arbeiten:

abstract class ClassHelper
{
      private static $alreadyIncluded = array();
 
      public static function includeClass($file)
      {
             if (!array_key_exists($file, self::$alreadyIncluded))
             {
                    include($file);
                    self::$alreadyIncluded[] = $file;
             }
      }
}

Forderung: Klassen sollen erst eingebunden werden, wenn sie vom Skript benötigt werden.

10. Aufteilung auf mehrere Zeilen
Wenn man ein bisher unbekanntes PHP-Skript vor sich hat, dann versucht man i.d.R. erstmal den Ablauf zu verstehen. Je länger nun z.B. die Bedingungen usw. sind, desto schwieriger wird das.

if (($user->isAdmin || $user->isMod) && (!$system->isEnabled && !$user->isLocked)) { }

Wesentlich übersichtlicher wäre es doch nun etwa so:

if ( ($user->isAdmin || $user->isMod)
    &&
    (!$system->isEnabled && !$user->isLocked)) {}

Auch bei Arrays und Funktionen mit vielen Parametern sollte man mehrere Zeilen verwenden um die Übersichtlichkeit zu wahren. (Hinweis: Auch Strings können über mehrere Zeilen gehen!

Forderung: Die Grenze von 80 Zeichen pro Zeile sollte nur in Ausnahmefällen überschritten werden!


Kommentare zu “Coding Guidlines – 10 Vorschläge und einige Zweifel”

  1. wsl sagt:

    Sehr schöner Post, wenn für mich auch fast nichts neues dabei. Zwei kleine Anmerkungen:
    Zu 6.: Wozu gibts __get() und __set() ?
    Zu 8.: Warum nicht einfach __autload() benutzen?
    Dafür sind diese magic mehtods gedacht, also warum nicht einfach benutzen? ;)

  2. Simon sagt:

    Zu Punkt 6: In PHP gibt es einiges was man besser nicht nutzt, das so nebenbei erwähnt. Persönlich nutze ich _get und _set zwar auch als Teil meines OR-Mappers, was mir jedoch einigen Ärger mit meiner IDE eingebracht hat. Über die getXYZ() und setXYZ($value) Funktionen lässt sich der Zugriff auf die einzelnen Variableninhalte besser regulieren und es können leichter Zusatzaktionen ausgeführt werden. Natürlich könnte das auch mit generischen _get/_set-Methoden geregelt werden, würde aber bei größeren komplexen Klassen schnell unübersichtlich werden.

    Zu Punkt 8: Autoload ist bei komplexen Ordnerstrukturen eigentlich absolut unsinnig, da, wenn viele verschiedene Dateipfade überprüft werden müssen, wesentlich mehr Ausführungszeit benötigt wird und auch eine höhere Festplattenbelastung entsteht.

Hinterlasse eine Antwort