PHP Iterator, IteratorAggregate mit current(), next() etc. nutzen

Einige Anmerkungen zu PHP Traversables

Iteration mit Funktionen

Traversables können mit foreach iteriert werden, jedoch nicht im allgemeinen mit Array-Iterations Funktionen wie reset() und next().

Iterator Objekte können auch auch mit folgenden Funktionen verwendet werden:

  • current()
  • next()
  • prev()
  • reset()
  • end()

Achtung: Es ist zwar möglich, allen diesen Funktionen IteratorAggregate Objekte zu übergeben (wie auch jedes andere Objekt), die Iterator-Funktionalität wird dabei aber nicht genutzt, sondern es wird über die Attribute des Objekts iteriert.

Das selbe gilt für Iterator Objekte mit each()! Dieses sollte generell nicht für Objekte benutzt werden, da hilft weder ArrayAccess noch Iterator. Es funktioniert im allgemeinen nie wie erwünscht.

Überprüfung von unbekannter Variable auf Iterierbarkeit mit foreach

$a implements Traversable || is_array($a);
  • Traversable ist vor Iterator zu bevorzugen da auch IteratorAggregate und andere interne Iteratoren damit erkannt werden.
  • Arrays sind keine Traversables, da keine Objekte

Will man allerdings volle Flexibilität genügt:

is_object($a) || is_array($a)
  • PHP kann beliebige Objekte als Iterator benutzen und iteriert dabei über die öffentlichen Attribute!

PHP Typesafe Enum & Propel

Dieser Beitrag beschreibt kurz und knapp, wie meine Typesafe Enum-Klasse zusammen mit dem ORM Propel funktioniert. Mehr über Typesafe Enum und Enumerations in PHP finden Sie hier: Typesafe Enum

Use case:

Typesafe Enum datatypes in propel objects, mapped to SQL ENUM type.

Example in Propel (schema.xml):

<column name="title_casus" sqlType="ENUM('N','M','F')"
    phpType="Casus" required="false" description="Kasus"/>

Example Enum Type:

Enum::define('CasusEnum', 'N','M','F');

Required Data Wrapper:

/**
 * Wrapper class that allows multiple instances, needed by propel
 * 
 * @author Fabian Schmengler <fschmengler@sgh-it.eu>
 * @copyright © 2010 SGH informationstechnologie UG
 */

class Casus
{
	private $casusEnum;

	public function __construct($casusString)
	{
		if (method_exists('CasusEnum', $casusString)) {
			$this->casusEnum = call_user_func(array('CasusEnum', $casusString));
		} else {
			$this->casusEnum = null;
		}
	}
	public function __tostring()
	{
		return (string)$this->casusEnum;
	}
	public static function __set_state(array $properties)
	{
		return (string)$properties['casusEnum'];
	}
}

Note to myself: diese Wrapper-Geschichte könnte noch ausgebaut werden, so etwas wie Enum::getInstance($string) wollte ich eh mal in TypesafeEnum integrieren, warum nicht auch gleich einen mit-generierten Wrapper (der natürlich auch noch eine getEnum() Methode bekommt)

Smarty Method chaining

Ich musste mich mal wieder mit Smarty herumschlagen und habe eine Weile gebraucht um folgendes zu begreifen:

Method chaining in Smarty 2.x ist NICHT MÖGLICH. Genaugenommen bleibt {$object->getFoo()->getBar()} immer leer. Der Smarty-Parser nimmt den Ausdruck komplett auseinander, verarbeitet Objekte jedoch nur eine Ebene tief. In dem Fall ist also ein hässliches {assign var=_foo value=$object->getFoo()}{$_foo->getBar()} nötig. Dies gilt überall, wo das Objekt als Template-Variable genutzt wird.

RESTful Web Applications mit PHP und HTML

Wer sich beim erstellen einer Web-Applikation am REST Prinzip orientieren will und dies nicht nur für Webservices sondern auch fürs User-Interface benutzen will, stößt ganz schnell an die Grenzen von HTML-Formularen, die in den meisten Browsern nämlich nur GET und POST als Methode erlauben – alles andere, also auch PUT und DELETE wird standardmäßig als GET aufgefasst.

Das ist aber kein Grund aufzugeben, Ruby on Rails macht es vor:
How do forms with PUT or DELETE methods work?

CakePHP benutzt den selben Hack (oh Wunder ;-)), wie man hier und hier sieht.

Konkret werden dabei abhängig von $_POST['method'] die Umgebungsvariablen $_SERVER['REQUEST_METHOD'] bzw. $_ENV['REQUEST_METHOD']überschrieben, auf die Daten wird dann mittels $_REQUEST zugegriffen, genau wie bei echten PUT oder DELETE Requests.

Somit können Web-Browser die selbe API wie andere Clients benutzen und es wird ein einheitlicher Standard für URLs unterstützt.

Git lokal (offline) für Synchronisation nutzen – Teil 2

Erster Teil: Aufsetzen und lokales Klonen eines Git Repositories unter Windows 7

Heute: Synchronisierung des Linux-Rechners mit bestehendem Repository auf Windows 7-Notebook übers Netzwerk.

Unter Debian ist Git schnell installiert:

$ apt-get install git-core

Git benötigt nun Zugriff über SSH, daher wird erstmal unter Windows ein SSH-Server eingerichtet.

Ich hatte erst diesen hier probiert: http://www.freesshd.com/?ctt=download
(falls nach Starten des Dienstes kein Symbol im Systray auftaucht, Kompatibilitätsmodus für Windows XP einstellen! Außerdem ggf. Firewall-Blockierung aufheben und Zugriff auf PC erlauben)

es jedoch nicht hinbekommen, ein anderes Arbeitsverzeichnis als C:\Windows\system32 einzustellen und dass Git das Repository findet.

Die Alternative war dann, OpenSSH über Cygwin laufen zu lassen, nach dieser Anleitung:
http://www.shannoncornish.com/blog/2009/04/git-server-windows-2008/

Den hier angelegten Benutzer “git” benutze ich allerdings nicht, sondern meinen eigenen Account auf dem Windows-Notebook. Dazu mounte ich unter Cygwin wieder im eigenen Account das jeweilige Repository:

$ mkdir /home/git/Kizzes
$ mount C:/Users/fs/Datenpartition/workspace/Kizzes /home/git/Kizzes

Der sshd Dienst sollte noch laufen, also kann ich auf dem Linux-Rechner dann entsprechend zugreifen:

$ git clone ssh://fs@192.168.2.103/home/git/Kizzes workspace/Kizzes

und schon habe ich das geklonte Repo unter workspace/Kizzes

weiter geht es mit
a) Synchronisation zwischen den einzelnen Repositories
b) Branches

Hilfreiche Links:

http://www.cygwin.com/cygwin-ug-net/cygwin-ug-net.html – Cygwin Dokumentation

Netzwerkfreigabe zwischen Windows 7 und Debian Linux

Bevor es mit der Git-Anleitung weitergeht, ein kurzer Exkurs, denn zunächst einmal wollte ich eine Netzwerkverbindung mit Dateifreigabe zwischen Windows 7 und Debian herstellen, dazu wurde Samba auf dem Debian-Rechner installiert (Anm.: nicht notwendig für Git über SSH).

Anleitung (Zugriff von Windows auf Linux): http://www.lug-viersen.de/howtos/samba-unter-debian-mit-einem-share-fuer-windows-xp.html

Für die umgekehrte Richtung, also den Zugriff auf freigegebene Ordner in Windows 7 habe ich auf dem Windows-Notebook einen neuen Benutzer fs-net erstellt (die Heimnetzgruppe von Windows 7 funktioniert nur mit anderen Windows 7 Rechnern) und mein Arbeitsverzeichnis für diesen User freigegeben:
Continue reading “Netzwerkfreigabe zwischen Windows 7 und Debian Linux”

Git lokal (offline) für Synchronisation nutzen

Meine Situation:

verschiedene Datenträger:
1. NTFS-Festplatte aus altem Notebook, auf der sämtliche aktuellen Projekte liegen, an denen ich alleine arbeite.
2. Externe Backup-Festplatte, auf der bisher mit einem einfachen Synchronisations-Tool das Projektverzeichnis abgebilded ist
3. Jungfräuliche NTFS-Datenpartition auf neuem Notebook mit Windows 7
4. Desktop-PC mit Debian Lenny, frisch für die Entwicklung aufgesetzt.
5. 2,5″ Festplatte mit portablem Betriebssystem (muss noch komplett eingerichtet werden)

(1) dient als Original-Quelle; (2) soll weiterhin als Backup genutzt werden, (3), (4) und (5) sollen gleichwertige Arbeitsumgebungen sein, wobei immer nur jeweils eine genutzt wird.

Mit einer zentralen Versionsverwaltung wie SVN hätte ich mich für einen Ort als Server für das Repository entscheiden müssen, der von (3), (4) und (5) gleichermaßen erreichbar ist, die Wahl wäre dafür wohl auf unser Firmen-Intranet gefallen, eine Internet-Verbindung ist ja i.d.R. überall vorhanden.

Der große Unterschied bei GIT ist nun, dass jede Kopie als vollwertiges Repository fungiert und Commits erst einmal lokal erfolgen. Dies geht wiederum so schnell (auch wenn mal keine Netz-Verbindung besteht) dass es den Workflow nicht stört und man die Faustregel “Wenn sich die Änderung nicht in einem Satz zusammenfassen lassen, ist es zu viel für einen Commit” leichter beherzigen kann.

Ich arbeite also auf dem einem System, kann darauf isoliert arbeiten und committen, bis ich den Arbeitsplatz wechsle, dann müssen nur die beiden betroffenen Repositorys gemerged werden (schrecklich dieses Denglisch, aber das bringt unser Job so mit sich…) und ich kann auf dem anderen System genauso weitermachen. Da GIT das Arbeiten mit Branches sehr einfach macht, ist es dabei sinnvoll, jedes Mal einen Branch abzuzweigen und schlussendlich wieder zusammenzuführen. Habe ich doch einmal an unterschiedlichen Repositorys parallel gearbeitet, sollte es auch kein Problem sein, diese Änderungen zusammenzuführen.

In diesem Artikel beschäftige ich mich nun mit der Installation von GIT auf Windows und der initialen Einrichtung der Repositories.
Continue reading “Git lokal (offline) für Synchronisation nutzen”

PEAR Installer updaten

Ich habe mir kürzlich Zend Server CE eingerichtet, die mitgelieferte go-pear.bat installierte mir darin PEAR in der Version 1.7.2 – dumm nur dass z.B. PHPUnit die Version 1.8 erfordert.

Merke: Es bringt nichts, sich go-pear.php von pear.php.net neu herunterzuladen und PEAR damit neu zu installieren, ein einfaches


pear upgrade-all

dagegen tut Wunder 🙂

PHP 5.2 Abstract Singletons: “abstract public static function” issue

Ein Problem über das ich schon häufiger gestolpert bin ist folgendes: Man will aus irgeneinem Grund eine abstrakte Singleton-Klasse definieren, sprich alle abgeleiteten Klassen sollen nur jeweils einmal instanziert werden.

In PHP 5.3 dank late static binding sehr einfach lösbar: Beispiel

In vorherigen Versionen ist es auf diese Weise nicht lösbar, die naive Herangehensweise wäre daher so etwas:

abstract class AbstractSingleton
{
	protected function __construct() { }
	private function __clone() { }
	abstract public static function getInstance();
}
class ConcreteSingleton extends AbstractSingleton
{
	private static $instance;
	public static function getInstance()
	{
		if (empty(self::$instance)) {
			self::$instance = new ConcreteSingleton();
		}
		return self::$instance;
	}
}

was jedoch seit PHP 5.2 berechtigerweise einen E_STRICT Fehler wegen “abstract static” wirft.
Continue reading “PHP 5.2 Abstract Singletons: “abstract public static function” issue”

PHP Fatal Error Handler

Codeschnipsel inspiriert von diesem Artikel:

fatalerrorhandler.php

<?php
// report all but fatal errors 
error_reporting((E_ALL | E_STRICT) ^ (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR));

// fatal error handler as shutdown function
register_shutdown_function('fatalErrorHandler'); 

function fatalErrorHandler() {
	$error = error_get_last();
	if ($error['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
		echo '<h1>Fatal Error, shutting down...</h1>';
		echo '<pre>' . var_export($error,true) . '</pre>';
	} else {
		echo 'Regular Shutdown, no fatal errors.';
	}
}

test1.php

<?php
require 'fatalerrorhandler.php';

// Fatal Error (E_ERROR)
unknown_function_call();

test2.php

<?php
require 'fatalerrorhandler.php';

// E_USER_ERROR
trigger_error('...', E_USER_ERROR);

test3.php

<?php
require 'fatalerrorhandler.php';

// Notice (E_NOTICE)
echo $unknown_var;