Too Cool for Internet Explorer

OSX 10.5.x + PHP + pecl_ncurses


After several days playing around with newt i am quite disappointed to come to a point where i have to say, that newt has not only a very limited documentation (i think there's only documentation of about 50% of the newt functionality) -- which would not be that of a problem, though. The bigger problem is, that things are very unstable. "Segmentation faults" reproducable on different operating systems and platforms -- not sure though, if the problem is newt or pecl_newt. However: this is definitly not what i want. So, let's move on to something better.

I had a look at the apple's opensource repository the other day and stumbled over a ncurses package. I had problems with the normal gnu ncurses package before, it would not install but hang when building the terminfo database. With the ncurses package i found at apple's opensource repository, i get it to install. Here is the howto:

Note: The first three steps below are for osx 10.5.x. For osx 10.6.6 simply download ncurses 5.7 and build it using ./configure --with-shared.

  1. Download ncurses from apple's opensource repository. You can find it by clicking on the link of the Mac OSX version you are running, eg. 10.5.8.
  2. Unpack the package und you get a directory called something like "ncurses-21" or so. Next cd into this direktory and run make and sudo make install. If make complains about a missing directory /tmp/ncurses/Build, just create it mkdir -p /tmp/ncurses/Build and start again.
  3. Now an intermediate build should have been generated. Next cd /tmp/ncurses/Build, make and sudo make install. Now ncurses should have been successfully installed.
  4. Ready to install the ncurses php extension by either executing sudo pecl install ncurses or downloading ncurses from pecl.php.net and building manually.
  5. Don't forget to add ncurses.so to your php.ini

Now everything should be ready to start writing ncurses apps -- however: for me it sill did not work. The simplest ncurses app resultet in the following error message:

Error opening terminal: vt100.

What's going on? dtruss might be your friend to figure this out. Execute the following command, where example.php is your ncurses app you want to run: sudo dtruss php example.php. You should see output like:

[...]
lstat("/Users/harald\0", 0xBFFFE41C, 0xFFFFFFFFBFFFEFC8)                 = 0 0
lstat("/Users\0", 0xBFFFE2EC, 0xFFFFFFFFBFFFEFC8)                = 0 0
ioctl(0x3, 0x4004667A, 0xBFFFF17C)               = -1 Err#25
ioctl(0x3, 0x402C7413, 0xBFFFF150)               = -1 Err#25
fstat(0x3, 0xBFFFF1B0, 0xBFFFF150)               = 0 0
mmap(0x0, 0x226, 0x1, 0x2, 0x3, 0x100000000)             = 0x17FA000 0
lseek(0x3, 0x0, 0x1)             = 518 0
munmap(0x17FA000, 0x1F3)                 = 0 0
close_nocancel(0x3)              = 0 0
ioctl(0x1, 0x4004667A, 0xBFFFECEC)               = 0 0
stat("/Users/harald/.terminfo\0", 0xBFFFEC00, 0xBFFFECEC)                = 0 0
access("/Users/harald/.terminfo/73/vt100\0", 0x4, 0xBFFFECEC)              = -1 Err#2
stat("/usr/local/share/terminfo\0", 0xBFFFEC00, 0xBFFFECEC)              = -1 Err#2
write_nocancel(0x2, "Error opening terminal: vt100.\n\0", 0x24)            = 36 0

You can see, that ncurses is looking for a file vt100 in a terminfo directory and fails looking at /usr/local/share/terminfo. I indeed had no directory /usr/local/share/terminfo but a directory /usr/share/terminfo. I was not able to figure out, where i could specify the correct directory configuration for this, so i just created a symlink: sudo ln -snf /usr/share/terminfo /usr/local/share/terminfo. After creating the symlink, i was able to execute my example ncurses application.



OSX 10.5.x + PHP + pecl_newt


I always wanted to provide nice user interfaces for some of my commandline tools written in PHP, but was not able to solve one problem until recently. The big problem when writing user interfaces for commandline utilities written in PHP is: ... what library to use for actually building the user interface?

  • You could try to write your own library using ANSI escape sequences -- but your user interfaces would either be very limited or it would be a hell of work to write an extensive library providing more than just the basics.

  • You could try to get the ncurses extension to work -- i failed.

  • You could try to get the newt extension to work -- i failed ...

... until recently. Every once in a while i tried to dig up more information of how to get newt to work, because this is the library i would prefer over the other solutions. However, it did not compile on my system. Recently i searched again and was able to dig up a patch for newt-0.5.22.11. So, here are the steps to get things work:

  1. Download newt 0.52.11 and extract it.

  2. Download the patch for newt from the page above and apply it as described in the short tutorial provided on that page.

  3. Download slang, which is required by newt. I decided to download the latest snapshot (pre2.2.3-60) and things worked just fine with it. Extract and build it -- i did not use any special flags for this.

  4. Download popt, which is required for newt. I decided to download popt-1.16, which seems to be the latest release and can be found at the bottom of the download page. Extract and build it -- i did not use any special flags for this.

  5. After installing slang and popt and patching newt, you should now be able to build and install newt.

  6. Download pecl_newt, or install it using pecl install .... Don't forget to add newt.so to your php.ini.

pecl_newt provides some examples which should be executed to verify, that installation succeeded.



ORMs sind doof


ORM

Wikipedia sagt dazu:

Objektrelationale Abbildung (englisch object-relational mapping, ORM) ist eine Technik der Softwareentwicklung, mit der ein in einer objektorientierten Programmiersprache geschriebenes Anwendungsprogramm seine Objekte in einer relationalen Datenbank ablegen kann. Dem Programm erscheint die Datenbank dann als objektorientierte Datenbank, was die Programmierung erleichtert. [...]

Inzwischen bringt ja so ziemlich jedes PHP Framework seine eigene ORM Implementierung mit, es gibt aber auch einige Framework-unabhängige ORM Implementierungen. Ich habe mir in den letzten Jahren immer mal wieder verschiedenste ORM Implementierungen angesehen -- immer dann, wenn in mir der Wunsch nach einer objektorientierten Zugriffsweise auf meine Datenbanken aufkam. Leider jedoch konnte mich bisher keine ORM Implementierung überzeugen.

ORMs sind doof

Auch in mir kommt immer mal wieder der Wunsch auf objektorientiert auf meine Datenbanken zuzugreifen, da dies den Zugriff auf einzelne Datensätze -- Objekte -- erheblich vereinfacht. Jedoch -- zu welchem Preis wird diese Vereinfachung erkauft?

Modellierung

Ich modelliere meine Datenbanken schon seit Jahren mit dem ER Modeller dbWrench. Das ist meiner Meinung nach super komfortabel. Ich sehe auf einen Blick all meine Tabellen und die Abhängigkeiten bzw. Verknüpfungen zwischen einzelnen Tabellen. Über die Funktion "Forward Engineering" kann dbWrench mein Datenbankschema in der Datenbank immer aktualisieren. Da ich bei MySQL den Tabellentyp InnoDB verwende, sind auch in der Datenbank sämtliche Verknüpfungen festgehalten und liessen sich z.b. über die INFORMATION_SCHEMA Tabelle leicht auslesen.

Nun ist es leider so, dass offenbar so ziemlich jede ORM Implementierung die Datenbankdefinition auf Ihre Weise bekommen möchte. Da muss man entweder seitenweise XML oder YAML Konfiguration, oder gar ellenlangen PHP Code schreiben -- nur um der Anwendung eine Information bekannt zu geben, die eigentlich exakt so schon in der Datenbank vorhanden ist?

Abstraktion

Wie weit muss man die Datenbankzugriffe abstrahieren? Nun, es gibt da sicherlich die verschiedensten Anforderungen. Ich denke bei der Entwicklung von Unternehmenssoftware kann man die Anforderungen ziemlich genau spezifizieren. Man entscheidet sich zu einem gewissen Zeitpunkt für ein bestimmtes Datenbankprodukt. Normalerweise wird diese Entscheidung nicht nach wenigen Monaten oder Jahren über den Haufen geworfen -- es sei denn es gibt sehr triftige Gründe dafür.

Deshalb bin ich der Meinung, dass die Abstraktion nicht so weit gehen muss, dass sämtliche Datenbankzugriffe abstrahiert werden und für beliebige Datenbanksysteme geeignet sind. Im Gegenteil: ich entscheide mich ja nicht für eine bestimmte Datenbank nur aus Kostengründen, sondern auch, weil diese vielleicht Features mitbringt, die ein anderes Datenbanksystem nicht unterstützt.

So erweitert z.b. MySQL den SQL Standard um eigene spezifische Befehle, die es in anderen Datenbanken nicht gibt, die aber sehr praktisch sind. Das ist kein Alleinstellungsmerkmal von MySQL. Beispiel: Hätte ich mich für Oracle entschieden, wäre ich doch dumm, würde ich zum Abbilden / Abfragen von Hierarchischen Strukturen nicht CONNECT BY verwenden -- nur weil dies nicht Teil des SQL Standards ist und dies so mit keiner anderen Datenbank funktioniert.

Nur: keine ORM Implementierung kann auf diese einzelnen Datenbankfeatures eingehen -- womit ich beim nächsten Punkt angelangt wäre.

Abfrage

Das grösste Manko aller (PHP) ORM Implementierungen ist meiner Meinung nach die Abfrage einer Datenbank. Ich gebe zu: ich mag SQL -- es gibt mir das passende Werkzeug zum Abfragen einer relationalen Datenbank in die Hand -- es wurde zu diesem Zweck entwickelt! Ich schreibe gern SQL, da es strukturiert und übersichtlich ausschaut und mich schnell zum Ziel führt. Ich gebe weiterhin zu: Ich nutze auch gern MySQL spezifische SQL Features -- aus den oben genannten Gründen.

Nun ist es jedoch so, dass die ORM Implementierungen in der Regel den Zugriff soweit abstrahieren, dass -- normalerweise -- kein SQL mehr geschrieben wird. CONNECT BY und ähnliche Dinge wären damit Unmöglich. Heutzutage hat sich folgende Schreibweise zum Erstellen von Datenbankabfragen etabliert:

$dbo
    ->select(array(
        'media.media_id', 'media.media_name', 'member.member_name', ...
    ))
    ->from('media')
    ->join('member', 'member.member_id = media.member_id')
    ->where('media.category_id = ?')
    ->order('media.media_id')
...

Ich bin kein Fan einer solchen Schreibweise:

  1. Es ist kein SQL ;-)
  2. Es ist wesentlich mehr Aufwand als beim Schreiben von SQL erforderlich
  3. Ich kann keine Datenbankspezifischen SQL Erweiterungen verwenden
  4. Ich habe keine Kontrolle darüber, welchen SQL Code die ORM Implementierung daraus generiert
  5. Es liegt in der Natur der Sache, dass ein derartiges Konstrukt niemals auch nur annähernd so performant sein kann wie ein simples SQL Statement übergeben an die Datenbank
  6. Ich kann das Statement nicht per Copy / Paste zwischen meinem Datenbank Client und der Anwendung hin und her kopieren -- praktisch, wenn man das ganze erstmal testen will
  7. Wenn ich den Datenbanktreiber einer nicht-relationalen Datenbank hinterlege, weil ich mich z.b. entscheide statt MySQL MongoDB anzusprechen, wird diese Schreibweise ohnehin ad Absurdum geführt. (Nur als beispiel -- ich weiss nicht, ob irgendeine PHP ORM Implementation überhaupt nicht-relationale Datenbanken unterstützt)

Natürlich bietet so ziemlich jede ORM Implementierung einen Fallback zur Herkömmlichen Absetzung von SQL Anfragen ohne ein Objekt-Mapping. Nur, wenn ich damit an einer Stelle in meiner Anwendung anfange: warum dann überhaupt eine derartige Abstraktion nutzen?

Fazit

Meiner Ansicht nach erkauft man sich den konsequenten Einsatz eines ORM zu einem zu hohen Preis. Deshalb habe ich den Einsatz eines solchen für mich immer wieder verworfen. Mein Wunsch wäre ein SQL -> Objektmapper. D.h.: Ich schreibe SQL, zurück bekomme ich Objekte, mit denen ich weiterarbeiten kann ...



MongoDB + PHP + OSX


Ich spiele gerade ein wenig mit Mongo DB. Da ich das die Tage sicherlich noch mal gebrauchen kann, mache ich mir hier mal ein paar wichtige Notizen zur Installation:

  • Der Aktuelle PHP Treiber ist nicht kompatibel zum aktuellen stable Release (0.8.0) auf der Mongo DB Seite. Deshalb muss ein daily-build von Mongo DB heruntergeladen werden. Am besten lädt man hier das Paket inkl. aller Tools und Treiber (beinhaltet auch den Source der PHP Erweiterung). Mongo DB also herunterladen und irgendwohin installieren (z.b. /opt/mongo).
  • Zum Compilieren des PHP Treibers benötigt man die Boost C++ Libraries. Beim Compilieren der Boost Libraries wurden die Namen der Bibliotheken derart angelegt, dass sie beim Compilieren des PHP Treibers von Mongo DB nicht gefunden werden konnten. Deshalb habe ich nach dem ./configure --prefix=/opt/boost von Boost die Datei Makefile editiert und die Zeile BJAM_CONFIG= nach BJAM_CONFIG='--layout=system' geändert.
  • Nachdem Boost Installiert wurde, lässt sich nun der Mongo DB Treiber compilieren. Bei mir befindet sich der PHP Treiber unterhalb des Verzeichnisses /opt/mongo/drivers_and_tools/mongo-php-driver. Ein phpize, ./configure --with-mongodb=/opt/mongo/ --with-boost=/opt/boost/, make, make install und der Treiber ist gebaut und installiert.

Eigentlich ganz einfach ... aber ich vergesse es sonst garantiert wieder :-)



gearman job queue server


Die letzten Wochen habe ich für die Arbeit einen Workflow Prozessor entwickelt, über den wir zukünftig unsere Medien Uploads verarbeiten werden. Unter anderem gehört zu seinen Aufgaben die Erzeugung von Still Images aus Videos, Wave- und Spektrum-Analyse aus Audiodateien, Erzeugung von Thumbnails und Previews für die Darstellung auf der Web Seite. Der Workflow Prozessor arbeitet von der Console bzw. als Hintergrundprozess (Dämon) und ist in PHP entwickelt. Wird er auf einem Server gestartet, kann der Hauptprozess beliebig viele Worker-Prozesse forken.

Ein Problem bei der Entwicklung war die Verteilung der Prozesse an die Worker. Hier habe ich zunächst auch auf eine reine PHP basierte Lösung gesetzt und die Verteilung der Aufgaben vom Hauptprozess übernehmen lassen, der über Socket-Pairs mit den Worker-Prozessen kommunizieren kann. Bei Tests während der Entwicklung im kleinen Rahmen und mit wenigen Jobs hat das auch wunderbar funktioniert. Bei umfangreicheren Tests traten dann allerdings die Probleme zu Tage.

Einmal abgesehen davon, dass die ganze Sache nicht wirklich der Performance-bringer war, war sie dank der direkten Kommunikation Client->Worker auch fehleranfällig und schlecht bzw. gar nicht skalierbar, denn wie soll man mit dieser Methode mehrere bzw. beliebige Worker-Server transparent ansprechen?

Zum Glück habe ich schon seit einiger Zeit gearman in meinen Lesezeichen. Gearman ist eine Serversoftware, die -- einfach ausgedrückt -- den Zweck erfüllt Jobs zu verteilen. Mögliche Einsatzgebiete sind z.b. die Verteilung von Jobs an Systeme, die besser für die jeweilige Aufgabe geeignet sind, die Lastverteilung auf beliebige Server oder um Jobs parallel abarbeiten zu können. Hierfür wird seitens gearman eine (persistente) Warteschlange implementiert, an die ein Client einen Job senden kann. Ein Worker kann sich einen Job aus der Warteschlange holen und diesen abarbeiten.

Gearman wurde ursprünglich von Brian Fitzpatrick (livejournal, memcached, ...) in Perl entwickelt und vor einiger Zeit von Brian Aker und Eric Day nach C portiert. Der Entwicklungsstand des C Zweiges von gearman ist seiner Version (0.3) zu schliessen noch relativ jung. Insgesamt aber scheint sich das ganze schon derart bewährt zu haben, dass gearman produktiv und unter hoher Last z.b. von digg und yahoo eingesetzt wird.

Zum erfolgreichen Kompilieren von gearman unter OSX muss sichergestellt sein, dass man eine aktuelle Version der libevent Bibliothek installiert hat. Danach lässt sich auch gearman problemlos unter OSX bauen und installieren. Sowohl für libevent wie auch für gearman funktioniert das wie üblich:

$ ./configure
$ make
$ make install

Anschliessend kann man gearman starten. Der Parameter "-d" bewirkt dabei, dass gearman als Hintergrundprozess losgelöst vom Terminal gestartet wird:

/usr/local/bin/gearman -d

Per default lauscht gearman auf Port 4370. Dies ist der offizielle Port, der von der IANA für gearman reserviert wurde.

Client/Worker APIs zur Kommunikation mit gearman gibt es bereits für C (enthalten im gearman Download), Perl, MySQL (als UDF) und PHP. Zum Ansprechen von gearman unter PHP gibt es ein pear package, net_gearman, sowie seit kurzem auch eine native Erweiterung für PHP, die allerdings noch experimentell ist und dementsprechend mit Vorsicht zu geniessen ist. Unter OSX konnte ich sie z.b. zwar einwandfrei kompilieren und installieren, die ersten Versuche scheiterten dann aber an segfaults. Nicht weiter tragisch, da das pear paket bisher einwandfrei seine Arbeit macht.

Eine Client Anwendung, die Jobs an den gearman Server übergibt, ist mit dem pear Paket schnell entwickelt:

#!/usr/bin/env php
<?php

require_once('Net/Gearman/Client.php');

$set = new Net_Gearman_Set();

function result($resp) {
    print_r($resp);
}

$files = array(
    '/Users/harald/Movies/00001.mov'
);

foreach ($files as $file) {
    print "adding task $file\n";
    
    $task = new Net_Gearman_Task('test', array(
        'filename'  => $file
    ));

//    $task->attachCallback('result');
    $set->addTask($task);
}

print "running set ...\n";

$client = new Net_Gearman_Client(array('127.0.0.1:4730'));
$client->runSet($set);

?>

Der erste Parameter beim Instanzieren der Klasse Net_Gearman_Task spezifiziert übrigens den Namen des Jobs, der vom Worker registriert werden muss, damit dieser entsprechende Jobs annehmen und verarbeiten kann. Normalerweise wartet der Client, bis alle Jobs abgearbeitet sind und kann wenn eine Callback Methode registriert wurde, mögliche Ergebnisse verarbeiten, die die Worker zurückliefern. Dieses Verhalten kann möglicherweise unerwünscht sein, z.b. wenn viele Jobs eingereicht werden und der Client während der Abarbeitung nicht blockiert sein soll. Hierfür implementiert die Net_Gearman_Task Klasse ein öffentliches Property:

public $type = self::JOB_NORMAL;

Um das Verhalten zu ändern, sind folgende Typen definiert:

  • Net_Gearman_Task::JOB_NORMAL -- Standardverhalten: Client blockiert während der Abarbeitung der Jobs
  • Net_Gearman_Task::JOB_BACKGROUND -- Client reicht Jobs an gearman weiter und blockiert nicht
  • Net_Gearman_Task::JOB_HIGH -- selbes Verhalten wie JOB_NORMAL, allerdings wird die queue übersprungen und ein Job kann sofort von einem Worker verarbeitet werden. Sinnvoll z.b. für höher priorisierte Jobs

Eine einfacher Klasse, die die Aufgabe 'test' implementiert, könnte folgendermassen aussehen:

<?php

class test extends Net_Gearman_Job_Common {
    function run($args) {
        print_r($args);
        
        print "sleeping 5 seconds ...\n";
        sleep(5);
        print "done!\n";
        
        return "OK!";
    }
}

?>

Der Worker selbst wird folgendermassen aufgebaut:

#!/usr/bin/env php
<?php

define('NET_GEARMAN_JOB_CLASS_PREFIX', '');
define('NET_GEARMAN_JOB_PATH', '.');

require_once('Net/Gearman/Worker.php');

$worker = new Net_Gearman_Worker(array('127.0.0.1:4730'));
$worker->addAbility('test');
$worker->beginWork();

?>

Normalerweise erwartet Net_Gearman Klassen, die Aufgaben implementieren unterhalb eines Verzeichnisses, das in der PEAR Verzeichnisstruktur angesiedelt ist. Weil man dies normalerweise nicht möchte, gibt es folgende Konstanten, die man mit eigenen Werten füllen kann um so alternative Verzeichnisse zu setzen:

// spezifiziert ein Prefix für den Namen der Klasse
define('NET_GEARMAN_JOB_CLASS_PREFIX', '');

// spezifiziert das Verzeichnis, in dem Klassen gesucht werden
define('NET_GEARMAN_JOB_PATH', '');

Unschön ist, dass für einzubindende Klassen Dateien mit folgendem Namensschema erwartet werden, z.b: test.php. Da ich meine Klassen immer z.B. -- test.class.php -- benenne, ist dies also leider eine kleine Unschönheit, mit der man offenbar leben muss.

Zum Abschluss sei noch zu sagen, dass gearman in den ersten Tests mit mehreren 1000 Jobs in der Queue seine Arbeit tadellos und sehr zufriedenstellend erledigt hat.

Empfohlene weiterführende Links:



php + ncurses + osx


Benötige ich demnächst bestimmt noch 1-2x, deshalb hier auf alle Ewigkeit festgehalten. Zunächst ncurses saugen und über ...

./configure --prefix=/usr/local --with-shared --disable-rpath --without-debug --without-ada --enable-safe-sprintf --enable-sigwinch --without-progs
make
make install

... konfigurieren und installieren. Anschliessend kann man ncurses über pecl installieren:

pecl install ncurses

... und mit der ncurses Entwicklung in Verbindung mit php loslegen.

p.s.: ich bin kein Fan von "(darwin)ports" oder "fink", aber manchmal sind sie ganz nützlich um sich die richtigen Konfigurationsschalter herauszusuchen: http://ncurses.darwinports.com/



Stellenangebot: PHP Entwickler in München


Zur Verstärkung unseres kleinen Teams in München suchen wir ab sofort einen erfahrenen PHP Entwickler (m/w) für unsere Webseiten clipdealer.de und pixelio.de. Bei clipdealer.de handelt es sich um einen neuen online Marktplatz für lizenzfreie Footage Clips und pixelio.de ist die größte kostenlose Bilddatenbank für lizenzfreie Fotos im deutschsprachigen Raum.

Ihre Aufgaben:

  • eigenverantwortliche Optimierung und Weiterentwicklung unserer Webseiten
  • die Konzeption und Erstellung neuer Module

Sie sind zuverlässig, teamfähig, haben Spaß am Programmieren und verfügen über folgende fachlichen Kenntnisse:

  • PHP5 sowie Kenntnisse mit einem objektorientierten Framework
  • MySQL 5 (u. a. Trigger, Stored Procedures)
  • Objektorientiertes JavaScript
  • Sicherer Umgang mit der Shell sowie mit den gängigen Tools
  • Linux und evtl. OSX Kenntnisse
  • Kenntnisse einer Versionsverwaltung z.b. Subversion oder CVS

Von Vorteil sind darüber hinaus folgende Kenntnisse:

  • Konfiguration eines Webservers (Apache, Lighttpd)
  • Performanceoptimierung (z.B. Lastverteilung bei Webservern und Datenbanken, Caching etc.)
  • Mehrjährige Erfahrung in der Entwicklung von PHP basierten, stark frequentierten Internetseiten

Wir bieten ein angenehmes Arbeitsumfeld in einem jungen, motivierten Team mit viel Eigenverantwortung und Raum für neue Ideen. Schicken Sie uns Ihre aussagekräftige Bewerbung bevorzugt per E-Mail oder Post mit einer detaillierten Beschreibung der bisherigen Tätigkeiten und Erfahrungen sowie allen erworbenen Zeugnissen und Ihren Gehaltsvorstellungen an folgende Adresse:

Per E-Mail:

m.hein@clipdealer.de

Per Post:

Clipdealer GmbH
Herr Markus Hein
Lipowskystr. 25
81373 München



PHP, ssh2 und OSX


Als Merkhilfe für mich:

Problem:

pecl ssh2 0.10 lässt sich nicht compilieren.

Wie im PHP Bug Tracker beschrieben, muss ssh2.c editiert und folgendermassen geändert werden:

- #if LIBSSH2_APINO < 200412301450
+ #if LIBSSH2_VERSION_NUM < 0x001000

Der Patch wurde bereits Anfang August letzten Jahres im Bug Tracker veröffentlicht - ich frage mich, warum er es noch immer nicht in ein neues Release der ssh2 extension geschafft hat ...



PHP, syck und SuSE 64bit


Als Merkhilfe für mich:

Problem:

~/syck-0.9.2 # make
[...]
gcc -shared .libs/phpext.o -L/usr/local/lib -lsyck -Wl,-rpath -Wl,/usr/local/lib -Wl,-soname -Wl,syck.so -o .libs/syck.so /usr/lib64/gcc/x86_64-suse-linux/4.2.1/../../../../x86_64-suse-linux/bin/ld: /usr/local/lib/libsyck.a(emitter.o): relocation R_X86_64_32S against `a local symbol' can not be used when making a shared object; recompile with -fPIC
/usr/local/lib/libsyck.a: could not read symbols: Bad value
collect2: ld returned 1 exit status
make: *** [syck.la] Error 1

Wie in der Fehlermeldung ersichtlich sollte syck mit -fPIC neu compiliert werden:

~/syck-0.55 # export CFLAGS=-fPIC; ./configure; make

Danach sollte sich das syck modul ohne weitere Probleme installieren lassen.



PHP5 mit IMAP unter Mac OS X


Als kleine Merkhilfe für mich zum o.g. Thema. Kürzlich wollte ich meiner lokalen PHP Installation unter Mac OS X 10.4.11 IMAP support hinzufügen. Meine Konfiguration dafür sah folgendermassen aus:

'./configure' '--prefix=/opt/php5' '--enable-force-cgi-redirect' '--disable-debug' '--enable-pic' '--disable-rpath' '--enable-inline-optimization' '--with-bz2' '--with-curl' '--without-gdbm' '--with-gettext=/opt' '--with-iconv' '--with-openssl' '--with-regex=system' '--with-xml' '--with-zlib' '--enable-exif' '--enable-sockets' '--enable-track-vars' '--enable-trans-sid' '--with-mysqli=/usr/local/mysql/bin/mysql_config' '--enable-memory-limit' '--enable-fastcgi' '--enable-memcache' '--enable-zip' '--enable-http' '--without-pdo-sqlite' '--without-sqlite' '--enable-mbstring' '--with-imap'

... und resultierte in folgender Fehlermeldung:

...
checking for IMAP support... yes
checking for IMAP Kerberos support... no
checking for IMAP SSL support... no
checking for utf8_mime2text signature... new
checking for U8T_CANONICAL... no
configure: error: utf8_mime2text() has new signature, but U8T_CANONICAL is missing. This should not happen. Check config.log for additional information.

Ein Blick in die config.log zeigt, dass das Include-File c-client.h nicht gefunden wird, und in der tat scheint c-client unter OSX nicht vorhanden zu sein:

conftest.c:240:22: error: c-client.h: No such file or directory

C-client ist Bestandteil des IMAP Paketes der University of Washington. Das komplette IMAP Paket kann über deren FTP Server gezogen werden: ftp://ftp.cac.washington.edu/imap/. Dort habe ich mir das Paket imap-2006k.tar.Z heruntergeladen. Nach dem Entpacken des Paketes wird dieses zunächst per:

$ make oxp

für Mac OS X gebaut. Das IMAP Paket enthält eine komplette IMAP Umgebung und die Installation dafür wäre deshalb auch dementsprechend umfangreich. Für PHP wird jedoch lediglich c-client benötigt, deshalb wird auch nur dieses "installiert", z.b.:

$ mkdir -p /usr/local/imap-2006k/include /usr/local/imap-2006k/lib
$ cp c-client/*.h /usr/local/imap-2006k/include
$ cp c-client/*.c /usr/local/imap-2006k/lib
$ cp c-client/c-client.a /usr/local/imap-2006k/lib/libc-client.a

Anschliessend kann man PHP mit folgenden Parametern neu configurieren und bauen:

'./configure' '--prefix=/opt/php5' '--enable-force-cgi-redirect' '--disable-debug' '--enable-pic' '--disable-rpath' '--enable-inline-optimization' '--with-bz2' '--with-curl' '--without-gdbm' '--with-gettext=/opt' '--with-iconv' '--with-openssl' '--with-regex=system' '--with-xml' '--with-zlib' '--enable-exif' '--enable-sockets' '--enable-track-vars' '--enable-trans-sid' '--with-mysqli=/usr/local/mysql/bin/mysql_config' '--enable-memory-limit' '--enable-fastcgi' '--enable-memcache' '--enable-zip' '--enable-http' '--without-pdo-sqlite' '--without-sqlite' '--enable-mbstring' '--with-imap=/usr/local/imap-2006k' '--with-kerberos'


PHP serialize - PHP::Serialization


Ich muss ein PHP object über 'serialize' serialisieren, an Perl weiterreichen und dort über das Modul PHP::Serialization auspacken. Ich habe Stunden damit zugebracht herauszufinden, warum ich auf meine Objektvariablen nicht zugreifen kann.

class test {
  public $public_key = 'public_val';
  private $private_key = 'private_val';
}

"Eigentlich" ist es logisch, dass PHP "auf irgendeine Art und Weise"" im serialisierten String kenntlich machen muss, welche Bestandteile des Objekts private und welche public sind, da ansonsten PHP selbst das Objekt nicht mehr korrekt deserialisieren könnte. Solchen Fragen muss man sich aber erst stellen, wenn man ein seralisiertes Objekt mit etwas anderem als PHP weiterverarbeiten möchte ... . Der Data::Dumper von Perl jedenfalls gibt für das deserialisierte Objekt folgendes zurück:

$VAR1 = bless( {
  'public_key' => 'public_val',
  'testprivate_key' => 'private_val'
}, 'PHP::Serialization::Object::test' );

Die Ausgabe von ...

print Dumper($obj->{'testprivate_key'});

... liefert jedoch ein "undef" - was ist da los? Ganz einfach: PHP fügt nicht nur den Namen der Klasse in den Namen der Objektvariablen beim Serialisieren des Objektes ein, sondern zusätlich vor und hinter dem Namen der Klasse je ein null-Byte:

0000000: 4f3a 343a 2274 6573 7422 3a32 3a7b 733a O:4:"test":2:{s:
0000010: 3130 3a22 7075 626c 6963 5f6b 6579 223b 10:"public_key";
0000020: 733a 3130 3a22 7075 626c 6963 5f76 616c s:10:"public_val
0000030: 223b 733a 3137 3a22 0074 6573 7400 7072 ";s:17:".test.pr
0000040: 6976 6174 655f 6b65 7922 3b73 3a31 313a ivate_key";s:11:
0000050: 2270 7269 7661 7465 5f76 616c 223b 7d   "private_val";}

Ein Dank an dieser Stelle geht an Plu, der mir beim Debuggen geholfen hat und nicht müde wird all meine Perl-Fragen zu beantworten.



Namespaces in PHP6?


Es geschehen anscheinend doch noch wunder! Für mich positiv überraschend haben sich die PHP Entwickler offenbar dazu durchringen können Namespaces in der kommenden neuen PHP Version zu unterstützen. D.h. Prefixe für Klassennamen sollten damit zukünftig der Vergangenheit angehören.

Alt:

class test_foo {
  function bar() {
  }
}

test_foo::bar();

Neu:

namespace test;

class foo {
  function bar() {
  }
}

test::foo::bar();

Offenbar ist es so, dass keine verschachtelten Namespaces für ein Paket definiert werden können, was ich nur begrüssen kann. Sehr gut finde ich auch die Entscheidung den bereits bekannten "Gültigkeitsbereichsoperator" zur Abtrennung der Namespaces zu verwenden.



(PHP) Entwicklungsumgebung unter Windows


Mein eigenes PHP Framework, das ich für all meine (privaten) Web Anwendungen verwende, benötigt für einige lokale Tools sowie für Build und Deploy Prozesse unter Unix übliche Tools, wie die Bash, GNU Make, awk, sed und wie sie denn alle heissen. Ausserdem mache ich Gebrauch von einigen Eigenheiten des Dateisystems, wie symbolische Links, wie sie so nicht im Windows Dateisystem implementiert sind, bzw. nur umständlich erreicht werden können. Ausserdem setzt mein Framework LightTPD als Web Server voraus, da ich einige spezielle Module dieses hervorragenden Web Servers gerne verwende.

Nun haben aber nicht alle, die dieses Framework verwenden müssen Linux, Mac OS X oder ein anderes Betriebssystem, bei dem von Haus aus all diese schönen Dinge vorhanden sind. Nein, es gibt in der Tat noch Leute, die ein Windows bevorzugen und für die muss eine vernünftige Lösung her.

Nach einigem Überlegen bin ich auf die Idee gekommen, mein Setup unter Cygwin auszuprobieren. Cygwin ist eine Emulation der Linux API für Windows, die es - kurz gesagt - ermöglicht z.b. Linux Anwendungen auf Windows zu portieren. Mehr dazu bei Wikipedia. Cygwin bringt auch eine portierte Bash mit.

Nach Herunterladen der Distribution von der Cygwin Web Seite und der Installation von Cygwin inkl. der Entwicklungstools, war also das Fundament vorhanden. Wer mit einer Debian basierte Linux Distribution vertraut ist, wird das praktische 'apt-get' Tool kennen, mit dem man nachträglich Software installieren kann. Auch für Cygwin gibt es ein derartiges Tool - cyg-apt. Ich kann nur empfehlen, dieses Tool einzusetzen, erleichtert es doch die nachträgliche Installation von z.b. von PHP benötigten Libraries erheblich. cyg-apt ist in Python geschrieben, weshalb bei der Installation von Cygwin auch die Installation von Python berücksichtigt werden sollte.

Als nächstes habe ich mir die Quellen des aktuellen PHP (5.2) besorgt. Wenn man MySQL Support wünscht, wird ausserdem noch der Quellcode von MySQL benötigt.

Zunächst muss man also MySQL kompilieren - da wir unter Cygwin nur die Client Bibliotheken benötigen, werden auch nur diese compiliert:

./configure --prefix=/usr --without-server
make
make install

Als nächstes kann man dann PHP mit fastCGI Unterstützung compilieren. Man beachte hierbei, dass 'fastCGI' unter Windows zu Problemen führen kann. Falls es später tatsächlich zu Problemen kommt, kann man dann aber PHP immernoch als CGI Anwendung unter LightTPD verwenden. Eine PHP Configuration könnte z.b. folgendermassen aussehen:

'./configure' '--prefix=/usr' '--with-config-file-path=/etc' '--with-config-file-scan-dir=/etc/php.d' '--enable-force-cgi-redirect' '--disable-debug' '--enable-pic' '--disable-rpath' '--enable-inline-optimization' '--with-bz2' '--with-curl' '--with-freetype-dir=/usr' '--with-png-dir=/usr' '--with-gd' '--enable-gd-native-ttf' '--without-gdbm' '--with-gettext' '--with-ncurses' '--with-gmp' '--with-iconv' '--with-jpeg-dir=/usr' '--with-openssl' '--with-png' '--with-regex=system' '--with-xml' '--with-expat-dir=/usr' '--with-dom=shared,/usr' '--with-dom-xslt=/usr' '--with-dom-exslt=/usr' '--with-xmlrpc=shared' '--with-zlib' '--with-layout=GNU' '--enable-bcmath' '--enable-exif' '--enable-ftp' '--enable-sockets' '--enable-track-vars' '--enable-trans-sid' '--enable-wddx' '--with-pear=/opt/pear' '--with-mysql' '--with-mysqli=/usr/bin/mysql_config' '--enable-memory-limit' '--enable-bcmath' '--enable-calendar' '--enable-dio' '--enable-mcal' '--enable-mbstring=shared' '--enable-dbx' '--enable-mbstr-enc-trans' '--enable-mbregex' '--enable-fastcgi' '--enable-force-cgi-redirect'

Alle Bibliotheken, die PHP zusätzlich noch benötigt, kann man i.d.R. per 'cyg-apt' nachinstallieren.

Nachdem auch PHP compiliert und installiert wurde kann man sich nun die Quellen von LightTPD besorgen. Ein einfaches './configure --prefix=/usr' und 'make; make install' reicht hier in der Regel aus, um LightTPD mit den wichtigsten Modulen zu Compilieren und zu Installieren. Die Konfiguration für LightTPD findet man anschliessend unter '/etc/lighttpd/lighttpd.conf' und könnte wie folgt aussehen:

# default document-root
server.document-root = "/data/www/htdocs"

accesslog.filename = "/var/log/lighttpd/access.www.log"

server.port = 81

$SERVER["socket"] == "127.0.0.1:80" {
server.username = "nobody"
server.groupname = "nogroup"
server.errorlog = "/var/log/lighttpd/error.www.log"

fastcgi.server = ( ".php" =>
(( "host" => "127.0.0.1",
"port" => 1026,
#"socket" => "/tmp/php-fastcgi.socket",
"bin-path" => "/usr/bin/php",
"bin-environment" => (
"PHP_FCGI_CHILDREN" => "4",
"PHP_FCGI_MAX_REQUESTS" => "100"
),
"bin-copy-environment" => (
"PATH", "SHELL", "USER"
),
"broken-scriptfilename" => "enable"
))
)
# configure server
$HTTP["host"] == "localhost" {
simple-vhost.server-root = "/data/www/"
simple-vhost.default-host = "localhost"
simple-vhost.document-root = "/htdocs/host/"
}

server.document-root = "/data/www/htdocs/host/"
}

# index filenames index-file.names = ( "index.php", "index.html", "index.htm", "default.htm" )

# selecting modules
server.modules = ( "mod_rewrite", "mod_auth", "mod_access", "mod_accesslog", "mod_fastcgi", "mod_redirect", "mod_simple_vhost" )

# disable directory listings
server.dir-listing = "disable"

# mime types
mimetype.assign = (
".pdf" => "application/cation/pdf",
".ps" => "application/postscript",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar"
)

Da unter Windows fastCGI nicht über Sockets funktioniert, muss es über TCP durch Angabe der Parameter Host und Port konfiguriert werden. Testweise kann man nun LightTPD folgendermassen starten:

/usr/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf

Abschliessend soll man LightTPD noch als Windows NT Service installieren können. Hierfür benötigt man das Dienstprogramm 'cygrunsrv', das man ggf. per 'cyg-apt' nachinstallieren muss. Der Befehl zum einrichten von LightTPD als Service sollte nach meinen Recherchen dann wie folgt aussehen:

cygrunsrv --install lighty --path /usr/sbin/lighttpd --args "-D -f /etc/lighttpd/lighttpd.conf" --disp "cygwin - lightTPD"

Leider hat die Installation als Service bei mir noch nicht geklappt.

Nachdem nun also PHP und LightTPD installiert sind, fehlt nur noch die Datenbank - MySQL. Da für PHP schon mit den Client Bibliotheken compiliert wurde, reicht es aus, wenn über die MySQL Web Seite der MySQL Installer für Windows heruntergeladen wird. Darüber kann man dann MySQL komfortabel über den Setup Wizard installieren und mit dem Testen und Entwickeln loslegen.

Das hier vorgestellte Setup funktioniert übrigens nicht nur wunderbar für PHP, sondern z.b. auch für Ruby (on Rails), Python oder Perl.



Interaktive PHP Shell


Ich habe mich schon mal letztes Jahr mit der interaktiven Shell von PHP beschäftigt und war damals zu dem Schluss gekommen, dass sie nicht wirklich brauchbar ist, weil sich z.b. keine 'Fatal Error' abfangen lassen und so jeder Schreibfehler zum Abbruch der Shell führt.

Jan Kneschke, der Entwickler meines bevorzugten WebServers lighttpd, hat sich nun dieser Problematik angenommen und hat eine PHP Shell entwickelt, die den eingegebenen PHP Code vor dem Ausführen prüft. Dies geschieht über die Tokenizer Erweiterung. So lässt sich vor dem Ausführen beispielsweise überprüfen ob bei einem Funktionsaufruf die jeweilige Funktion überhaupt existiert oder dass keine doppelten Funktionsnamen angelegt werden.

Das Hauptaugenmerkt bei der Entwicklung der PHP-Shell liegt also bei der Vermeidung des 'Fatal Error' - welcher sonst zum sofortigen Abbruch des PHP Interpreters führt.

Ich habe bisher nur kurz mit der PHP Shell gespielt, kann aber jetzt schon sagen, dass sie sehr viel brauchbarer ist, als die in PHP eingebaute Shell - eben Dank der Vermeidung des 'Fatal Error'. Wer mit der Shell experimentieren möchte, kann Sier hier herunterladen:

PHP Shell von Jan Kneschke



SDO mit PHP unter Mac OS X


Eigentlich möchte ich mich mal mit den SDOs (Service Data Objects) unter PHP beschäftigen. Die Erweiterung ist in der PHP Welt noch relativ jung und bisher nur über PECL zu bekommen.

Leider habe ich im Moment ein Problem die Erweiterung unter Mac OS X zum Laufen zu bringen. Ich habe bereits eine laufende PHP 5.1.2 Installation mit allen nötigen Erweiterungen. Sobald ich SDO jedoch aktiviere, verweigert PHP seinen Dienst. Über die Console erhalte ich folgende Fehlermeldung, mit der ich wenig anfangen kann:

dyld: NSLinkModule() error
dyld: Symbol not found: __ZTI19SDORuntimeException
Referenced from: /usr/lib/20050922/sdo_das_xml.so
Expected in: flat namespace

Trace/BPT trap

Ich habe bereits das Usenet bemüht und hoffe, dass mir dort jemand helfen kann... mal gespannt.

[Update #1 - 19.02.2006 20:45]

Ich habe eben nochmal die Version von libxml2 bei mir überprüft. In der PHP Dokumentation wird libxml2 ab Version 2.6.19 empfohlen, ich hatte jedoch Version 2.6.16 installiert. Also schnell die aktuelle Version (2.6.23) runtergeladen und alles nochmal compiliert - geht trotzdem nicht :-(.

Wenn ich die Erweiterung für den Data Access Service (DAS) XML deaktiviere, startet immerhin PHP mit der SDO Erweiterung ohne irgendeinen DAS - ich bekomme jedoch ein Warning:

PHP Warning: Interface SDO_PropertyAccess cannot contain non abstract method /abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=[]._#() in Unknown on line 0