Mike: Funktions Bibliothek aufbauen und bei Bedarf einzeln abrufen

Hallo,

ich versuche gerade eine Bibliothek aus Funktionen aufzubauen und diese nach Bedarf einzufügen. Es gibt hier im Forum zwar den Hinweis auf __autoload(), der aber Klassen betrifft. Also habe ich mir gedacht es könnte wie folgt gehen, doch dem ist nicht so, warum?

function masterfunc($funcname,$arg='')  
{  
if(function_exists($funcname) )  
{  
$str = $funcname.'('.$arg.');'; // Funktion mit Argumenten zusammensetzen  
eval($str); // Funktionsaufruf  
  
}else{  
  
// hier beginnt die Suche in einem vorgegeben Verzeichnis, in diesem Test nicht wichtig  
}  
  
  
} // end func.####  
  
// Aufruf Beipiel:  
echo masterfunc('substr',"'abcdefghijklmnop',3,5");  
  

Ich benutze hier absichtlich eine bestehende PHPFunktion zur Verdeutlichung. Aber mir geht es natürlich vorrangig um eigene. Warum funktioniert mein Beispiel nicht? Ist das überhaupt machbar?

Gruss
Mike

  1. Mahlzeit,

    Warum funktioniert mein Beispiel nicht?

    Wie macht sich das bemerkbar? Ich behaupte, es funktioniert.

    --
    42
  2. Moin,

    ich versuche gerade eine Bibliothek aus Funktionen aufzubauen und diese nach Bedarf einzufügen. Es gibt hier im Forum zwar den Hinweis auf __autoload(), der aber Klassen betrifft.

    was willst du denn eigentlich bezwecken? Wenn die Funktionen „nach Bedarf“ eingefügt werden sollen, kannst du die Bibliothek doch auch modularisieren und dann nur das entsprechende Modul nach Bedarf einbinden. Oder verstehe ich das Anliegen falsch?

    Also habe ich mir gedacht es könnte wie folgt gehen, doch dem ist nicht so, warum?

    function masterfunc($funcname,$arg='')

    {
    if(function_exists($funcname) )
    {
    $str = $funcname.'('.$arg.');'; // Funktion mit Argumenten zusammensetzen
    eval($str); // Funktionsaufruf

      
    Hast du schon [call_user_func](http://de1.php.net/manual/de/function.call-user-func.php) ausprobiert? Das ist deutlich sicherer als evil eval.  
      
    
    > ~~~php
    
    }else{  
    
    >   
    > // hier beginnt die Suche in einem vorgegeben Verzeichnis, in diesem Test nicht wichtig  
    > }  
    >   
    > } // end func.####  
    >   
    > // Aufruf Beipiel:  
    > echo masterfunc('substr',"'abcdefghijklmnop',3,5");  
    >   
    > 
    
    

    Ich benutze hier absichtlich eine bestehende PHPFunktion zur Verdeutlichung. Aber mir geht es natürlich vorrangig um eigene. Warum funktioniert mein Beispiel nicht?

    Das kommt darauf an, was du erwartest. Dein eval oben führt substr schon aus, speichert das Ergebnis allerdings nirgends oder gibt es zurück. Wenn du $str mit 'return ' anfangen lässt, hast eval einen Rückgabewert, den du mit einem weiteren return aus masterfunc zurückgeben kannst.

    Viele Grüße,
    Robert

    1. Hallo,

      was willst du denn eigentlich bezwecken? Wenn die Funktionen „nach Bedarf“ eingefügt werden sollen, kannst du die Bibliothek doch auch modularisieren und dann nur das entsprechende Modul nach Bedarf einbinden. Oder verstehe ich das Anliegen falsch?

      Hast du schon call_user_func ausprobiert? Das ist deutlich sicherer als evil eval.

      Nein, dachte bisher die wären nur für eigene Funktionen(aus dem Namen geschlossen). Aber nach deinem Hinweis hab ich das probiert und geht tatsächlich ebenso mit Standardfunktionen.

      Ich würde call_user_func() auch gerne nutzen, tue mich da aber im Moment schwer, weil mein Funktionsaufruf keine unbegrenzte Anzahl Argumente zulässt und wenn ich das als einen String übergebe, bleibt wohl doch wieder nur eval. Oder hast Du dafür auch eine Lösung?

      return call_user_func ('substr',$arg); erzeugt somit einen Fehler beim Aufruf, weil $arg natürlich nur als ein Argument zählt.
      echo masterfunc('substr',"'abcdefghijklmnop',3,5");

      Das kommt darauf an, was du erwartest. Dein eval oben führt substr schon aus, speichert das Ergebnis allerdings nirgends oder gibt es zurück. Wenn du $str mit 'return ' anfangen lässt, hast eval einen Rückgabewert, den du mit einem weiteren return aus masterfunc zurückgeben kannst.

      So einfach kanns manchmal sein und ich komme doch nicht drauf. Habe zwar mit returnwerten experimentiert nur leider nicht in dieser Kombi, vielen Dank klappt super.

      Gruss
      Mike

      1. Moin,

        Ich würde call_user_func() auch gerne nutzen, tue mich da aber im Moment schwer, weil mein Funktionsaufruf keine unbegrenzte Anzahl Argumente zulässt und wenn ich das als einen String übergebe, bleibt wohl doch wieder nur eval. Oder hast Du dafür auch eine Lösung?

        Dann definiere doch die Funktion so, dass sie eine variable Anzahl an Argumenten akzeptiert und nutzt.

        Viele Grüße,
        Robert

        1. Hallo,

          ich habe das nun nach den Vorschlägen hier entsprechend gelöst und, was viel wichtiger ist, ausreichend getestet. Bisher ohne Komplikationen.

          Der wesentliche Aspekt dabei war die Parameter/Argumente klar zu identifizieren:

          if(function_exists($funcname) ){return call_user_func_array($funcname,$args);}else{return '<h3>FUNCTION NOT EXIST</h3>';}

          Somit ist es nun tatsächlich so einfach aufzurufen wie normale PHP-Funktionen:

          masterfunc('myfunc_math_versatile',$var,$Array,$xy);

          Sogar modular habe ich mir was einfallen lassen. Normalerweise eine Funktion je Datei, doch komplexe Module, die selbst eigentlich schon Gesamtpaket sind, baue ich über meine FunktionLib ein indem ich sie durch den Namen als Modul deklariere und ein Parameter/Argument hinzufüge, was mir dann die jeweilige Funktion innerhalb dieser Moduldatei aufruft:

          // prüfen ob ein Modul vorliegt dann ändert sich ab hier der Funktionsname  
          // Module können mehrere Funktionen in einer Datei haben, und dann brauchts den Namen dazu  
          if(strpos($funcname,'_modul')){$funcname = $funcname.'_'.$args[1];unset($args[1]);}
          

          Ich habe das jetzt ohne Ende getestet und es übertrifft meine Erwartungen, vor allem auch in der Dokumentation. Denn nun habe ich alle Funktionen in einem Verzeichnis, die ich mit einem Script durchlaufen lasse, die widerum jeweils die Kommentare der einzelnen Dateien auswertet, so habe ich eine Mini-PHP.net-like Doku zu meinen Funktionen.

          Gruss und Dank an alle
          Mike

    2. Hallo,

      was willst du denn eigentlich bezwecken? Wenn die Funktionen „nach Bedarf“ eingefügt werden sollen, kannst du die Bibliothek doch auch modularisieren und dann nur das entsprechende Modul nach Bedarf einbinden. Oder verstehe ich das Anliegen falsch?

      da hatte ich wohl zuviel gelöscht, hier nochmal zu diesem Punkt:

      Ich stelle mir ein Verzeichnis oder DB vor mit vielleicht Hunderten von Funktionen, die ich so simple aufrufen kann wie hauseigene PHP-Funktionen, also permanent präsent bei Bedarf. Doch Du scheinst da schon eine andere Lösung im Auge zu haben, doch wie meinst Du das genau mit dem modularisieren?

      Gruss
      Mike

      1. Moin,

        Ich stelle mir ein Verzeichnis oder DB vor mit vielleicht Hunderten von Funktionen, die ich so simple aufrufen kann wie hauseigene PHP-Funktionen, also permanent präsent bei Bedarf. Doch Du scheinst da schon eine andere Lösung im Auge zu haben, doch wie meinst Du das genau mit dem modularisieren?

        Die eingebauten PHP-Funktionen sind – wie der Name sagt – eingebaut, d.h. sie sind immer präsent und nicht „nur bei Bedarf“. Dieser Code ist also (zumindest beim direkten interpretieren bis PHP 5.4) immer im Arbeitsspeicher vorhanden.

        Dein Vorschlag erfordert bei jedem Aufruf einer vorher nicht definierten Funktion eine (möglicherweise sehr kostspielige) Suche danach, bevor sie geladen und ausgeführt wird. Du willst dir damit wahrscheinlich das „Wissen“ ersparen, in welcher Datei die Funktion zu finden ist. Das kann problematisch werden, wenn es in deinem Datenbestand mehrere Funktionen mit dem gleichen Namen geben sollte, denn dann hängt es vom Suchergebnis ab, welche Funktion zur Verfügung steht und letztlich aufgerufen wird.

        Sinnvoller ist meiner Meinung nach, Funktionsbibliotheken anzulegen, d.h. Dateien mit Funktionen gleicher „Kategorie“, z.B. Dateisystem, Datenbanken, Login, … Jede Bibliothek ist dann bspw. eine Datei mit den Funktionen, die du per include(_once) oder require(_once) einbindest. Sobald du mehrere Funktionen in deinem Skript benötigst, ist dieser Ansatz wohl deutlich performanter, da möglicherweise (sehr wahrscheinlich) sehr viel weniger Zugriffe aufs Dateisystem oder Datenbanken nötig sind.

        Viele Grüße,
        Robert

  3. Moins,

    eine Funktionsbibliothek klingt für mich nach einer Klasse.
    Anstatt Methoden die aufeinander aufbauen bzw. einen Sinn miteinander verknüpft, sind das allgemeine Methoden die static sind. Und falls doch mal mehrere Methoden aufeinander aufbauen kann man in der Klasse variablen definieren - das wäre auch der Vorteil die Funktionen in einer Klasse zu bündeln.

    Gruß
    Libmaker
    T-Rex

    1. Hi,

      eine Funktionsbibliothek [..] das wäre auch der Vorteil die Funktionen in einer Klasse zu bündeln.

      Nochmal die Aufgabe:
      Führe einfach nur eine Funktion aus, so als wäre sie eine Built-in-Funktion.

      Klar, ich könnte eine Lib zusammenbauen, wo die alle drin sind. Denke jedoch ans Kompilieren: Da werden stets ALLE Funktionen kompiliert, womöglich brauche ich aber nur eine von denen. Also keine Lib?

      Lösung: Die Funktion wird aus dem FS oder aus einer DB oder von sonstwo nachgeladen. In Perl steht der Name der Funktion in $AUTOLOAD. Hier nun das Perl-Beispiel für die Klasse main, die main gibt es in Perl immer (Beispiel ohne Fehlerbehandlung):

        
      use strict;  
      use warnings;  
        
      foo(1,2,3,4); # einfach mal ne Funktion aufrufen  
        
      sub AUTOLOAD{  
          # main::foo steht in $AUTOLOAD, wir brauchen nur foo  
          my $meth = [split "::", our $AUTOLOAD];  
          require $meth->[1].'.pm';         # eine foo.pm liegt im @INC Pfad  
          my $code = main->can($meth->[1]); # can() liefert die Code-Referenz  
          &$code(@_); # Code ausführen  
      }  
        
      
      

      Jetzt schauen wir mal in die Datei foo.pm da steht drin:

        
      use strict;  
      use warnings;  
        
      sub foo{  
          print "@_";  
      }  
        
      1;  
      
      

      Und hier ist die Ausgabe: 1 2 3 4

      Mit einer zweckmäßigen Fehlerbehandlung versehen, wird das zu einer feinen Sache. Geht auch mit PHP zu machen.

      Horst

  4. Hallo,

    ich versuche gerade eine Bibliothek aus Funktionen aufzubauen und diese nach Bedarf einzufügen. Es gibt hier im Forum zwar den Hinweis auf __autoload(), der aber Klassen betrifft.

    Du suchst __call(); damit kannst Du Methoden bei Bedarf nachladen.
    Binde den Code, soweit der Pfad zur Source in Deiner __call() definiert ist, mit require ein.

    Horst

    1. Hallo,

      Du suchst __call(); damit kannst Du Methoden bei Bedarf nachladen.
      Binde den Code, soweit der Pfad zur Source in Deiner __call() definiert ist, mit require ein.

      keine Ahnung ob mir das weiterhilft, scheint nur im Bereich OOP und somit Klassen zu arbeiten. Weiß mittlerweile nicht mal mehr ob meine Vorstellung überhaupt problemlos umzusetzen ist. Je mehr ich experimentiere umso mehr Schwierigkeiten muss ich aus dem Weg räumen. Versuche ich zb. ein Array zu übergeben, gibt's nen eval Fehler. Habe ich zwar hinbekommen durch var_export, bsp. masterfunc('db_show_html',var_export($ar_show,TRUE));, doch dauernd tun sich neue Probleme im Umgang mit den Parametern auf. Ist halt nicht so einfach wie call_user_func(), doch das geht ja nicht, wie bereits erklärt.

      Gruss
      Mike

      1. Mahlzeit,

        keine Ahnung ob mir das weiterhilft, scheint nur im Bereich OOP und somit Klassen zu arbeiten.

        Spricht da irgendwas dagegen?

        --
        42
      2. hi,

        keine Ahnung ob mir das weiterhilft, scheint nur im Bereich OOP und somit Klassen zu arbeiten.

        Tja, die Schulmedizin präsentiert OOP als die Lehre von Säugetieren, Lurchen und Fischen. Wie wärs mit OOP als Lehre der Fortbewegungsarten, Schwimmen, Laufen, FLiegen... da kämen wir eher zur Sache, nämlich zu den Methoden.

        Also, Du hast eine Factory und brauchst eine Methode, z.B. um an die Daten eines Benutzers zu kommen:

          
        use User;  
        my $user = User->new;  
        $user->getData;  
        
        

        Drei Zeilen, wo Du doch nur eine brauchst (Getter). Wie wärs damit, Du hast einen Singleton und der ruft die Methode einfach auf:

          
        $self->getUserData->{vorname};  
        
        

        Wäre eine Überlegung wert ;)

        Horst

  5. Tach!

    Warum funktioniert mein Beispiel nicht? Ist das überhaupt machbar?

    Es ist machbar, aber es ist nicht wirklich toll. Du verwendest beim Aufrufen den Namen der Funktion als String. Damit kann dir keine IDE eine Autovervollständigung oder Hilfe bei Parametern anbieten. Immer wenn man Strings anstelle von richtige Bezeichnern nimmt, hat man dieses Problem. Wenn man dann nicht immer alles Nötige zum Verwenden (genaue Schreibweisen und Parameterreihenfolge zum Beispiel) im Kopf hat, muss man ständig nachschlagen. Ich verzichte ungern auf diese Hilfen und finde es immer unnötig anstrengend, wenn ich mit solchen Strings (aber in anderen Situationen als deiner) arbeiten muss.

    Wenn du trotzdem bei deinem Prinzip bleiben willst, nimm call_user_func() oder call_user_func_array() statt eval().

    dedlfix.