codehunter
Goto Top

In PHP-Klassen Eventhandler verwenden wie in Delphi

Diese Anleitung richtet sich an Programmierer welche sich sowohl in Borland Delphi als auch in PHP auskennen. Ich erläutere in einem einfachen Beispiel die Verwendung von Eventhandlern (Ereignissen) in PHP-Klassen.

Hallo zusammen!

Als langjähriger Delphi-Programmierer habe ich in PHP immer eines sehr vermisst: Den Komfort von Eventhandlern (auch bekannt als Ereignisse). Die Delphi-Dokumentation definiert Ereignisse einfach als Methodenzeiger welche wiederum als Eigenschaftsvariablen einer Klasse angegeben werden. Zwar kennt PHP Callback-Funktionen doch ist deren Handhabung mit call_user_func innerhalb einer Klasse umständlich, der Zugriff auf Funktionen außerhalb der Klassendeklaration IMHO unmöglich.

PHP kennt im Gegensatz zu Delphi keine Methodenzeiger. Man kann sich aber eine andere nützliche Eigenschaft des PHP-Interpreters zu Nutze machen. PHP behandelt Funktionsnamen wie Strings und kann Stringvariablen als Funktionsaufrufe nutzen.

Da es für diesen speziellen Anwendungsfall natürlich keinen Objektinspektor wie in Delphi gibt ähnelt die weitere Vorgehensweise stark der Entwicklung von visuellen Komponenten in Delphi sowie deren Test durch Erzeugung zur Laufzeit.

Ich habe dazu folgenden Beispielcode entwickelt. Er orientiert sich stark an den Delphi-Konventionen:
<?php

  /* Eine neue Klasse deklarieren */
  class TObject {

    /* Sichtbare Klassenvariable welche den Eventhandler aufnimmt */
    public $OnStart;

    /* Eine neue Klassenmethode deklarieren */
    public function Start() {

      /* Die Klassenvariable mit dem Eventhandler in eine lokale Variable
         kopieren */
      $func = $this->OnStart;

      /* Den Eventhandler aufrufen und ihm einen Parameter übergeben
         (beispielsweise einen Fortschrittszähler) */
      if(function_exists($func)) {
        $func(1234);
      }
    }

  }
  
  /* Die Funktion des Eventhandlers definieren */
  function ObjectStart($number) {
    echo $number;
  }

  /* Oben deklarierte Klasse instantiieren */
  $obj = new TObject;

  /* Eventhandler der Klasseninstanz zuweisen */
  $obj->OnStart = ObjectStart;
  
  /* Klassenmethode aufrufen welche ihrerseits den Eventhandler aufruft */
  $obj->Start();

?>
Einige Erläuterungen zum Quelltext:

Zeile 7: Hier wird eine Klassen-Membervariable OnStart als public deklariert. Diese nimmt später den Namen der Eventhandler-Routine als String auf.

Zeile 10: Hier wird eine Klassenmethode als public deklariert. Darin können z.B. Schleifen durchlaufen werden wobei innerhalb der Schleife z.B. der Eventhandler mit dem aktuellen Schleifenzählerstand aufgerufen wird.

Zeile 14: Hier wird der Inhalt der Klassen-Membervariable OnStart in eine lokale Variable der Klassenmethode kopiert. Funktionsaufrufe aus Stringvariablen heraus funktionieren nur mit lokalen Variablen.

Zeile 18: Mittels function_exists wird überprüft, ob der angegebene Funktionsname als Funktion exisitiert und im Codebaum erreichbar ist. Dies kann man entfernt mit dem Aufruf von if Assigned(OnStart) in Delphi vergleichen.

Zeile 19: Hier wird nun der Eventhandler aufgerufen. Ich habe hier beispielhaft nur eine numerische Konstante als Parameter mitgegeben. Dies kann natürlich jede Art von Variable sein.

Zeile 26: Hier wird eine einfache Funktion als Eventhandler deklariert. Ihre Parameterliste muss natürlich mit der kompatibel sein welche innerhalb der Klasse beim Eventhandler-Aufruf verwendet wird.

Zeile 27: Der Inhalt des Eventhandler-Parameters wird beispielhaft per echo ausgegeben.

Zeile 34: Der Membervariablen OnStart unserer Klasse wird nun der Name unserer Eventhandler-Funktion übergeben. Um mich der Delphi-Syntax etwas anzunähern habe ich mir die Eigenart von PHP zunutze gemacht, nicht in einfache oder doppelte Anführungszeichen gesetzte Zeichenfolgen als String zu behandeln. Allerdings erzeugt dies einen PHP-Hinweis dass dies keine definierte Konstante ist - mir ging es um Ähnlichkeit zur Delphi-Syntax. Besser man schreibt
$obj->OnStart = 'ObjectStart';  
oder
$obj->OnStart = "ObjectStart"';  

Zeile 37: Schließlich und endlich wird nun die Klassenmethode Start() aufgerufen welche unser Konstrukt in Gang setzt.

Jetzt könnte man das Ganze noch ein wenig auf die Spitze treiben in dem man dem Eventhandler ein Handle auf die Klasseninstanz mitgibt. Dies entpricht dann den in Delphi bekannten Sender-Variablen. Dadurch kann man mehreren Klasseninstanzen den selben Eventhandler zuweisen. Die einzelnen Klasseninstanzen lassen sich anschließend innerhalb des Eventhandlers über die Sender-Variable identifizieren. Hier ein entsprechend modifiziertes Codebeispiel:
<?php

  class TObject {

    public $Name;
    public $OnStart;
    
    public function __construct() {
      $this->Name = 'Object1';  
    }

    public function Start() {

      $func = $this->OnStart;

      if(function_exists($func)) {

        /* Den Eventhandler mit einem Handle auf die aktuelle Klasseninstanz
           und einem Zählparameter aufrufen */
        $func($this, 1234);
      }
    }

  }
  
  function ObjectStart($sender, $number) {
    echo $number;
    echo '<br />';  

    /* Hier wird über das Handle der Klasseninstanz auf eine Membervariable
       zugegriffen. Genausogut könnte man auf Klassenmethoden zugreifen */
    echo $sender->Name;
  }

  $obj = new TObject;

  $obj->OnStart = ObjectStart;
  
  $obj->Start();

?>
Ich hoffe, mein kleines PHP-Delphi-Crossover findet ein paar Anwender. Schließlich sind beide Programmiersprachen sehr komfortabel. Warum sollen sie dann nicht voneinander profitieren.

Grüssle
Cody

Content-Key: 146483

Url: https://administrator.de/contentid/146483

Printed on: April 16, 2024 at 18:04 o'clock

Member: dog
dog Jul 08, 2010 at 17:46:51 (UTC)
Goto Top
PHP behandelt Funktionsnamen wie Strings und kann Stringvariablen als Funktionsaufrufe nutzen.

Und das kommt davon wenn man PHP programmiert ohne mal die Fehleranzeige einzuschalten...

<?php

  error_reporting(E_ALL|E_STRICT);
  ini_set('display_erros','On');  
  
  function keks() {
    echo "Hallo";  
  }
  
  $call = keks;
  
  $call();

?>

PHP Notice: Use of undefined constant keks - assumed 'keks' in /private/var/folders/rk/rkpxTSHLGeKxWDx3wCuQbk+++TI/-Tmp-/untitled_1b9..php on line 10
PHP Stack trace:
PHP 1. {main}() /private/var/folders/rk/rkpxTSHLGeKxWDx3wCuQbk+++TI/-Tmp-/untitled_1b9..php:0

Hallo

Wenn schon, dann gib bitte auch einen String an und nicht eine Konstante!
Das ist eine Unart, die manche PHP-Programmier-Anfänger entwickeln und dafür sollten Sie gleich im Hinterhof an die Wand gestellt werden...

Zudem kann 5.3 Closures, weshalb man dann ohnehin folgendes schreibt:

<?php 
  $call = function() {
    echo "Hallo";  
  };
  
  $call();
?>
Member: Codehunter
Codehunter Jul 09, 2010 at 06:15:21 (UTC)
Goto Top
Zitat von @dog:

Und das kommt davon wenn man PHP programmiert ohne mal die Fehleranzeige einzuschalten...
Das ist eine Unart, die manche PHP-Programmier-Anfänger entwickeln und dafür sollten Sie gleich im Hinterhof an die Wand
gestellt werden...

Na das lass mal lieber face-wink Ich hatte dies ja bewusst gemacht und auch als solches hier in der Anleitung kenntlich gemacht. Ich hätte nur mehr betonen sollen was die richtige Notation gewesen wäre.

Zudem kann 5.3 Closures, weshalb man dann ohnehin folgendes schreibt:

Zu den Closures muss ich sagen dass ich bis heute keine Zeit gefunden habe mich in 5.3 einzuarbeiten, leider. Anders herum betrachtet ist so mein Beispiel aber auch zu älteren Versionen kompatibel. Die 5.3 ist längst noch nicht auf allen Webhosting-Paketen angekommen (manche bieten tatsächlich noch 4.x an)

Danke für die Tips, ich ändere mal die Anleitung entsprechend.

Grüssle
Cody