Optimierung von PHP-Tags

PHP bietet einige Möglichkeiten, die Skriptsprache im Code einzuleiten und zu beenden. Beispiele:

<?php /* long form */ ...?>

<? /* short form */ ?>

<% /* ASP-style */ %>

In den Fokus rücken, möchte ich dabei das End-Tag. Also z.B „?>“. In Dateien, die ausschließlich PHP-Code enthalten, ist dieses nicht nötig. Wo immer auch möglich, sollte man daher auf das schließende PHP-Tag verzichten, denn dieses hat einen gravierenden Nachteil. Stellen wir uns folgenden Code vor:

<?php
  require_once('test.php');
  echo a();

und die dazugehörige test.php:

<?php

  function a() { /* ... */ }

?>...

Die Punkte am Ende der Datei test.php sollen exemplarisch für Leerzeichen stehen. Genau jene können zum Problem werden, wenn die Ausgabe exakt dem Rückgabewert der Funktion a() entsprechen muss. Beispielsweise, wenn diese durch andere Software eingelesen und interpretiert wird. Die Leerzeichen am Ende werden nämlich eins zu eins bereits bei der Ausführung von „require_once(test.php);“ mit ausgegeben und sind somit Teil der Gesamtausgabe des Skripts. Dieses Verhalten nutzt man sogar oftmals bei der Ausgabe von HTML, wenn PHP und HTML gemischt in einer Datei stehen (z.B. in einem Template). Bei Leerzeichen am Ende einer PHP-Datei kann dieses Verhalten jedoch schnell zu einer langwierigen Fehlersuche führen (gerade wenn sich viele Includes durch mehrere Dateien ziehen). Fehlt das PHP End-Tag, erkennt PHP automatisch, dass der Sourcecode am Dateiende abgeschlossen ist. Leerzeichen am Ende sind dann kein Thema mehr. Sie werden bereits vor dem Syntax-Check vom Interpreter entfernt.

Fazit:

Der bewusste Verzicht auf PHP-Ende-Tags ist nützlich und hilft Fehlern vorzubeugen.

MySQL-Optimierungen die Zweite

Heute geht es um die Beschleunigung von Abfragen. Natürlich gibt es auch in diesem Themenbereich viele unterschiedliche Möglichkeiten, weshalb ich nur eine sehr kleine Auswahl darstelle:

MySQL-LIMIT

SELECT firstname, lastname
FROM user
LIMIT 1000, 1000;

Stellen wir uns vor, es sollen alle Vor- und Nachnamen in Tausender-Schritten aus der Datenbank gezogen werden.
Man kann dazu – wie im obigen Beispiel – zwei Parameter für LIMIT nutzen. Offset und RowCount. Das funktioniert soweit ganz gut. Solange jedenfalls, bis die Tabellen größer werden. Dann kann der Offset-Parameter zur echten Bremse werden.
Besser ist es dann, wenn man sich softwareseitig einfach den letzten maximalen Primärschlüssel zwischenspeichert und die Abfrage, dann wiefolgt ändert:

SELECT firstname, lastname
FROM user
WHERE id > 1000
LIMIT 1000;

Nun kann es natürlich geschehen, dass id’s gelöscht werden. Daher muss man sich auch die letzte ID merken und nicht einfach immer den Wert 1000 „draufrechnen“. Durch die Umstellung kann man deutlich schnellere Abfragen erreichen. Da auf der Primärschlüsselspalte ein Index liegt, fällt MySQL die Suche nach der nächstgrößeren ID leicht.

Subqueries statt Joins

Joins resultieren oft in temporären Tabellen, was ein EXPLAIN EXTEND schnell verraten kann:

EXPLAIN EXTENDED SELECT ... JOIN ...

Das Resultat lautet oftmals: Using temporary

Using temporary bedeutet leider einen ziemlichen Performanceverlust bei einer großen Datenmenge. Besser ist es dann, man nutzt ein Subquery:

 

EXPLAIN EXTENDED SELECT (SELECT b.myfield
                         FROM b
                         WHERE b.id = a.foreignKey) ...
                 FROM a

Für die Subqueries steht dann wieder ein Index bereit, der die Sache beschleunigt. Die lästige temporäre Tabelle fällt dann einfach weg und die Abfragen gehen zügig voran 😉

MySQL-Optimierungen

Immer wieder gelangt man als Entwickler an den Punkt, an dem die Datenbankabfragen einfach einen Tick schneller sein könnten. Mögliche Performanceauswirkungen sollte man deshalb bereits beim Entwerfen einer Tabelle berücksichtigen.
Dazu gibt es einige Möglichkeiten, von denen ich nachfolgend einige aufführen möchte.

NULL, wo auch NULL gebraucht wird.

NULL-Werte können eine sinnvolle Sache sein. Nicht immer werden diese aber benötigt. Mit der Zeit sieht man hin und wieder beispielsweise Spalten, die NULL erlauben und gleichzeitig einen DEFAULT-Wert besitzen. Das kann unter Umständen sinnvoll sein. Oder aber, die möglichen NULL-Werte werden überhaupt nicht genutzt. In diesem Fall sind sie überflüssig und kosten nur Performance. MySQL muss somit immer auch den NULL-Fall prüfen und aufwendigere Funktionen durchlaufen.

MyISAM und ROW_FORMAT=FIXED

Für MyISAM-Tabellen bietet es sich für Geschwindigkeitsoptimierungen hinsichtlich Lesezugriffen an, das Tabellenformat auf FIXED zu stellen. Damit muss die Datensatzlänge nicht aufwendig berechnet werden. Das spart Zeit. Dafür müssen allerdings feste Datentypen (also beispielsweise CHAR, statt VARCHAR) verwendet werden. Dadurch benötigt man aber mehr Speicher.

Zend-Framework: Layout im Admin-Modul umstellen

Falls man das Layout im Admin-Modul seiner Anwendung, die mit dem Zend-Framework entwickelt wurde, umstellen möchte, kann man bei Google einige Lösungsansätze finden. Ich habe nach einer möglichst einfachen gesucht. Herausgekommen ist Folgendes:

namespace A\B\C;

class Layout extends
  \Zend_Layout_Controller_Plugin_Layout {

  public function preDispatch(
    \Zend_Controller_Request_Abstract $request) {

    if('admin' === $request->getModuleName()) {
      $this->getLayout()->setLayout(
        $request->getModuleName()
      );
    }
}

Die Namespaces kann man bei Bedarf natürlich weglassen, wenn man diese nicht benötigt. Um das Ganze dann noch zu komplettieren, muss man in der Bootstrap der startMVC-Methode noch den Parameter „pluginClass“ mitgeben. Das kann dann beispielweise so aussehen:

$view->layout = Zend_Layout::startMvc(array(
  'layout' => 'site', //change to yours
  'viewSuffix' => 'tpl',
  'ViewBasePath' => APPLICATION_PATH . '/tpl/layouts/',
  'pluginClass' => 'A\B\Plugin\Layout'
))->setView($view);