Thema: Variabler Funktionsaufruf

Hi.

Habt ihr ne schnelle Lösung, wie man einen Funktionsaufruf dynamisch generienen kann. Ich habe eine Funktion, die möchte ich gerne Aufrufen und die Parameter sind in einem indizierten Array mit variabler Länge gespeichert. Wie kann ich nun daraus den Aufruf zusammenschustern, ohne für jede Möglichkeit der Parameterzahl von 1 bis, sagen wir 5 einen eigenen Aufruf zu starten.

Also quasi was sinnigeres, als sowas:

$args = array('test1', array(1,2,3), 42);

switch (count($args)) {

    case 0:
    my_function();
    break;

    case 1:
    my_function($args[0]);
    break;

    case 2:
    my_function($args[0], $args[1]);
    break;

    case 3:
    my_function($args[0], $args[1], $args[2]);
    break;

    case 4:
    my_function($args[0], $args[1], $args[2], $args[3]);
    break;

    case 5:
    my_function($args[0], $args[1], $args[2], $args[3], $args[4]);
    break;
}

Basti

Re: Variabler Funktionsaufruf

Hi!

Arbeite doch mit "foreach" und "eval".
Also, vielleicht (ungetestet):

$cmd = 'my_function(';
foreach ($args as $arg)
  $cmd .= $arg.',';
$cmd = substr($cmd, 0, -1);
$cmd .= ');';
eval($cmd);

Ich hoffe, das hilft...

Mamphil

The laws of physics are the canvas God laid down on which to paint his masterpiece. “Leonardo Vetra” in Dan Brown’s “Angels & Demons”

3

Re: Variabler Funktionsaufruf

@Mamphil:

foreach bei so einer leichten Aufgabe?
Da würde ich eher join() verwenden.
http://de.php.net/join

$param = join(",",$args);
$cmd = 'my_function(' . $param . ')';
eval($cmd);

tink

Beleidigungen sind die Argumente derer, die keine Argumente haben

4

Re: Variabler Funktionsaufruf

...naja, bei den Werten meines Beispielcodes würde in der Funktion als zweiter Parameter der String 'Array' übrig bleiben. Das ist ja eben genau das Problem.

Und, dazu kommt noch, dass diese ganze Prozedur in einer eigenen Funktion (sei mod_call() ) steht und das Array mit den Parametern als Argumente von außen eingegeben werden und auch berücksichtigt werden muss, dass diese Parameter ev. als Referenz übergeben werden (mod_call($module, $arg1, &$arg2, $argn); ), wobei das galaub ich leicht zu lösen ist, indem man bei dem Funktionsaufruf aus dieser Funktion heraus einfach grundsäzlich die Argumente als Referenz übergibt, insofern sie hinterher nicht wieder so oder verändert zurückgegeben werden müssen. Muss ich wohl nochmal durchspielen, muss jetzt aber los.

Für weitere Ideen (und auch die bisherigen) bin ich dankbar.

Basti

Re: Variabler Funktionsaufruf

Hi!

... warum einfach, wenn es auch kompliziert geht wink

Du könntest auch einfach nur einen Parameter verwenden: Ein Array, in welches du alles anderen Parameter setzt.
Der Aufruf ist dann völlig unabhängig von der Anzahl der im Array übergebenen Parameter.

Mamphil

The laws of physics are the canvas God laid down on which to paint his masterpiece. “Leonardo Vetra” in Dan Brown’s “Angels & Demons”

6

Re: Variabler Funktionsaufruf

Hi Phil.

Das Ganze gibt eine Aufruffunktion für sowas, wie ein Simpleton-Pattern, wobei es weniger um die einmalige Initialisierung von Klassen geht, als darum, den Zugriff auf Objekte bzw. deren Methoden ganz generell zu steuern.

Es gibt also verschiedene Module in Form von Klassen und nicht jeder Benutzer darf alle Methoden eines Moduls ausführen und manche Module werden als ein anderer Benutzer ausgehführt. Andere Methoden-Aufrufe müssen immer die selbe Instanz einer Klasse 'treffen', also eben die Simpleton-Geschichte.

Diese Funktionalität stellt eine Klasse bereit (ModuleHandler oder so) und jeder Aufruf einer Methode irgendeines Moduls, also auch des 'eigenen' ($this->methode) läuft dann über diese Klasse. Es ist dann aber wiederum umständlich, sich zunächst die Instanz der Klasse in eine Varible zu holen und über die dann die zentrale Methode aufzurufen, die die ganzen Aufrufe managed. Einfacher ist es, den Aufruf über eine Funktion zu steuern, die die Instanz des Modulhandlers als statische Variable immer parat hält.

Beispiel:

class MyModule
{
    function do_something ()
    {
        // hier soll jetzt auf das Session-Modul zugegriffen werden
        // sagen wir, um die der Session-Var $foo den Wert 'bar' zu
        // uebergeben

        // Instanz des Modul-Handlers holen
        global $module_handler;
        $session_module = &$module_handler->get_module('session');
        $session_module->register(array('foo'=>'bar'));
    }
}

Das ist ziemlich beschissen, vor allem, wenn du die gleiche Geschichte für jedes $this durchziehen musst.

Mit einer solchen Aufruffunktion sieht es dann z.B. so aus:

class MyModule
{
    function do_something ()
    {
        mod_call('session.register', array('foo'=>'bar'));
    }
}

...hinter dem session.register, oder auch session::register (ist in einer PHP-Umgebung wahrscheinlich vertrauter) sollen dann noch parameter übergeben werden können, wie '-x' für exclusiv, wenn keine bestehende Instanz des angesprochenen Moduls verwendet werden soll, sondern eine neue erzwungen wird ... sowas halt.

Hier siehst du schon, dass das dein Array-Vorschlag eine ganz üble Syntax produzieren würde:

mod_call('session.register', array(array('foo'=>'bar')));

...bei diesem einfachen Beispiel, aber das könnte, wie in meinem Eingangsbeispiel dann ja auch so aussehen:

mod_call('session.register', array('test1', array('foo'=>'bar'), 42));

...und das will ich eigentlich nicht. außerdem, und das ist viel wichtiger, kriegst du so ja das Problem mit der Übergabe als Referenzen nicht gelöst. Du kanst letztlich nur alle oder garkeinen als Referenz übergeben oder noch eine weitere Array-Ebene einführen, in der diese Info dann mitgeschickt wird...!

Ich mein, ich hab aber schon die Löung und die ist ganz ähnlich, wie dein Code oben. Irgendwie so:

$args = array('test1', array(1,2,3), 42);
$cmd = 'my_function('
for ($i=0; $i<func_num_args(); $i++) {

    $cmd .= '&$args[' . $i . ']';
    if (func_num_args() != $i) $cmd .= ', '; 
}
$cmd .= ');'
eval ($cmd);

...müsste doch gehen, oder. Gleich mal testen...

Danke.

Basti

7

Re: Variabler Funktionsaufruf

Ja, es funktioniert (so ähnlich):

function mod_call ($x = false)
{
    static $ModuleHandler;
    
    // Falls noch nicht geschehen, Modulhandler instanzieren
    if (!isset($ModuleHandler) AND !is_object($ModuleHandler)) {
        $ModuleHandler = new ModuleHandler;
    }
    
    $args = func_get_args();
    $cmd = '$ModuleHandler->call(';
    for ($i=0; $i<func_num_args(); $i++) {

        $cmd .= '$args[' . $i . ']';
        if (func_num_args()-1 != $i) $cmd .= ', '; 
    }
    $cmd .= ');';
    eval ($cmd);
}

class ModuleHandler
{
    function call ()
    {
        $arguments = func_get_args();
        echo '<pre>';
        var_dump($arguments);
        echo '</pre>';
    }
}

mod_call ('test1', array(1, 2, 3), 42);
mod_call ('test2');

Allerdings kann ich die Argumente nicht als Referenzen übergeben. Habt ihr Ahnung, wie ich das anstelle? Wenn ich ein Kaufmanns-Und vor das '$args[...' setze, dann spuckt mir der der Parser (PHP5 RC3) folgende Warnung aus:

Warning: Call-time pass-by-reference has been deprecated - argument passed by value; If you would like to pass it by reference, modify the declaration of [runtime function name](). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file. However, future versions may not support this any longer. in /home/basti/.../func_get_args_test.php(20) : eval()'d code on line 1

Und selbst, wenn die nicht käme, ich kann die Referenz via func_get_args() ja eh nicht entgegennehmen ... bzw. das meint die Warnung ja auch wahrscheinlich.

Hmmm ... Grübel.

Basti

8

Re: Variabler Funktionsaufruf

Ich gebs auf.

Das mit den Referenzen krieg ich so nicht hin und außerdem ist es eigentlich eine Schnapsidee, die Modulprogrammierer einzeln auf Modulmethoden loszulassen, denn so hat man ja keine Möglichkeit sicherzustellen, dass ein weiterer Aufruf die gleiche Instanz des Moduls trifft oder eben eine andere.

Muss also ein standart Singleton mit etwa folgendem Aufruf her:

$session = get_module ('session');
$session->register('foo', 'bar');
$session->unset('foo');

Der Aufruf für eine Zugriffsrechteprrüfung muss dann halt in den Modul-Methoden selbst erfolgen, etwa so:

class ModuleA
{
    function foo ($x, &$y)
    {
        method_check($func_get_args);
        // ...hier dann die eigentlichen Aktionen... 
    }
}

In method_check() (schöner Name, ich weiß *g) kann man dann via debug_backtrace() auslesen, welche Methode welcher Klasse zu prüfen ist und hat ja auch eine Kopie der übergebenen Argumente erhalten.

Führ ich Selbstgespräche (...ich meine, das interessiert wahrscheinlich 'kai Sau', oder?)

Basti

9

Re: Variabler Funktionsaufruf

Hi Basti,

zum Thema Selbstgespräche:
Interessieren tut´s mich schon, aber das ist mir viel zu hoch muss ich zugeben, denn so tief habe ich mich noch nie mit PHP-Programmierung beschäftigt, als dass ich das nachvollziehen könnte, was Du da treibst/treiben willst smile

MfG, tink

Beleidigungen sind die Argumente derer, die keine Argumente haben

10

Re: Variabler Funktionsaufruf

Ich will einfach ein System, in das du Module einbauen kannst. Diese Module sind Klassen. Und die Instanzen der Klassen/Module werden einfach zentral vom System verwaltet, so, dass z.B. eben alle Module nur auf eine einzige Instanz des Session-Moduls zugreifen und nicht jedes Modul sich seine eigene Instanz des Moduls/der Klasse Session instanziert.

Das heißt, dass alle Aufrufe von einem Modul aus auf ein anderes Modul eben über eine zentrale Funktion läuft, die prüft, ob das gefragte Modul bereits instanziert wurde und ob das aufrufende Modul überhaupt auf eine bestehende Instanz zugreifen möchte, oder ob es womöglich eine neue Instanz eines Moduls verlangt. Usw.

Und ich wollte eigentlich hier noch Zugriffsrechte einbauen, so dass im System zentral eingestellt werden kann, dass z.B. eine bestimmte Methode eines Administartionsmoduls eben nur von Administaratoren ausgeführt werden darf. Anders herum muss z.B. der garbage_collector des Session-Moduls von jedem benutzer aus ausführbar sein, aber eben mit root-Rechten, damit der Müllschlucker dann auch Zugriff auf die Sitzungsdaten anderer erhält (die er auch braucht, da die Datenspeicherung, auch von Temporären bzw. Sitzungsdaten nochmal gekapselt ist und eben jeder Benutzer nur auf seine Daten/die seiner Gruppe zugreifen darf).

Das Problem ist nun eben, dass ich Aufrufe aus einem Modul auf eine bestimmte Methode eines anderen (sowie des selben) Moduls nicht über eine zentrale Steuerungsinstanz laufen lassen kann, weil ich eben a) dem aufrufenden Modul nicht gleichzeitig die Rückgabewerte des Aufrufs zurückgeben kann, sowie eine Referenz der angesprochenen Modulinstanz (außer eben mit unhandlichen Workarounds). Aber natürlich brauchst du ja eine Referenz auf das Modul, dass du ansprichst, um bei einem weiterem Aufruf auch genau dieses Modul wieder ansprechen zu können und nicht womöglich eine ganz neue Instanz davon zu erhalten. b) scheint es eben nicht möglich, auf einfachem Wege einer Modul-Methode auf diese Weie eine Referenz als Argument zu übergeben, womit das Vorhaben natürlich gestorben ist.

Ergo:
Das aufrufende Modul muss von der zentralen Kontroll-Instanz eine Referenz auf das Modul erhalten, das es ansprechen will und dann über diese Referenz direkt auf die Methoden des Moduls zugreifen. Das ist dann letztlich ein banaler Simpleton-Pattern gepaart mit der Option, bei Wunsch, dem aufrufendem Modul auch eine neue Instanz des aufgerufenen Moduls zurückzugeben und eben zumindest noch ganze Module für bestimmte Benutzer zu sperren. Aber natürlich hat man so keine Möglichkeit mehr, den Zugriff auf einzelne Modul-Methoden zu kontrollieren, wozu dann eben in jedes Modul ein Funktionsaufruf an eine Kontrollinstanz gesetzt werden muss (und auf die Modulvariablen kommt man dann völlig unbegrenzt ran...).

War das etwas verständlicher?

Basti