Ankündigung

Einklappen
Keine Ankündigung bisher.

[Erledigt] call_user_func_array und bind_param Problem

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Ballamann
    hat ein Thema erstellt [Erledigt] call_user_func_array und bind_param Problem.

    [Erledigt] call_user_func_array und bind_param Problem

    Hey
    Ich möchte sämtliche MySQLi-Anweisungen in eine einzige Klasse packen. Das klappt soweit ganz gut, bei den Prepared-Statements allerdings stoße ich auf ein Problem. Folgende Methode soll die Anweisung bind_param() übernehmen. Dafür generiere ich ein Array mit allen übergebenen Parametern, welche ich dann an die bind_param() weiterleite, so dass man dann später bequem mit $objekt->set_parameter($bind, $value) die "Variablen" befüllen kann.
    Der Code dazu sieht so aus:
    PHP-Code:
    function bind()
        {
            
    //Minimum 2 Argumente werden benötigt
            
    $arg_num func_num_args();
            if (
    $arg_num 1) return false;
            
            
    $datastring func_get_arg(0);
            
    $i 1//0 wird übersprungen da im $datastring
            
    while ($i $arg_num)
            {
                
    $this->params[func_get_arg($i)] = "test"//Das Array mit den "gebindeten" Variablen
                
    $i++;
            }
            
            
    //$datastring an das array anfügen, zwecks funktionsaufruf
            
    array_unshift($this->params, &$datastring);
            
    //bind_param aufrufen und Parameter übergeben 
                    
    if (!call_user_func_array(array($this->prep_obj"bind_param"), &$this->params))
                
    $this->exc("Parameter konnten nicht gebunden werden!");
                    } 
    Die Variablen werden dann so befüllt:
    PHP-Code:
    function set_parameter($parameter$value)
        {
            
    $this->params[$parameter] = &$value;
            
        } 
    Das Array wird auf diese Art richtig angepasst (das ergab ein die() ), wenn ich dann allerdings ein execute() aufrufe wird bei einem INSERT-Kommando nicht das eingetragen was ich über set_parameter() festgelegt habe, sondern das was beim initialisieren des arrays eingetragen wurde in die datenbank geschrieben (in dem fall hier also immer "test").

    Beim Nachforschen bin ich auf einen Hinweis auf php.net gestoßen:
    Hinweis: Care must be taken when using mysqli_stmt_bind_param() in conjunction with call_user_func_array(). Note that mysqli_stmt_bind_param() requires parameters to be passed by reference, whereas call_user_func_array() can accept as a parameter a list of variables that can represent references or values.
    Aber das hilft mir nicht weiter, meiner Meinung nach werden alle Werte korrekt by reference übergeben?!

    Danke für Hilfe

  • SinnlosS
    antwortet
    Ich weiß, das der Thread älter ist. Da aber scheinbar häufiger Leute vor diesem Problem stehen (Aufruf von bind_param mit unbekannter Anzahl Parameter), und ich kürzlich ebenfalls, hier noch eine Alternative zu dem Workaround für PHP 5.3+ (setzen der Referenzen für die Array-Elemente in einer eigenen Funktion).
    Durch Nutzung von Reflection spart man sich die Zusatzfunktion und wird in der Performance etwas schneller, allerdings hat man für bind_param zwei Zeilen Code mehr:
    PHP-Code:
    $db     = new mysqli("localhost","root","","tests"); 
    $res    $db->prepare("INSERT INTO test SET test2=?,test3=?"); 
    $refArr = array("si","hallo",1); 
    $ref    = new ReflectionClass('mysqli_stmt'); 
    $method $ref->getMethod("bind_param"); 
    $method->invokeArgs($res,$refArr); 
    $res->execute(); 
    Und noch ein Hinweis der zwar generell bind_param() betrifft, bei der hier angesprochenen Problemstellung aber immer auftritt: Vorsicht beim binden von Array-Elementen wenn das Statement mehrmals mit wechselnden Values durchgeführt werden soll!
    Die Array-Values müssen EXPLIZIT ihrem jeweiligen Key zugewiesen werden. Eine Neugenerierung des kompletten Arrays zerstört die Referenzen, auch bei identischen Keys.

    Beispiele:
    PHP-Code:
    $db     = new mysqli("localhost","root","","tests"); 
    $res    $db->prepare("INSERT INTO test SET test2=?,test3=?"); 
    $refArr = array("si","hallo",1); 
    $ref    = new ReflectionClass('mysqli_stmt'); 
    $method $ref->getMethod("bind_param"); 
    $method->invokeArgs($res,$refArr); 
    $res->execute(); 
    $refArr = array("si","ciao",2);
    $res->execute();
    // Trägt zweimal die Values "hallo" und 1 ein


    $db     = new mysqli("localhost","root","","tests"); 
    $res    $db->prepare("INSERT INTO test SET test2=?,test3=?"); 
    $refArr = array(0=>"si",1=>"hallo",2=>1); 
    $ref    = new ReflectionClass('mysqli_stmt'); 
    $method $ref->getMethod("bind_param"); 
    $method->invokeArgs($res,$refArr); 
    $res->execute(); 
    $refArr = array(0=>"si",1=>"ciao",2=>2);
    $res->execute();
    //  Trägt zweimal die Values "hallo" und 1 ein


    $db     = new mysqli("localhost","root","","tests"); 
    $res    $db->prepare("INSERT INTO test SET test2=?,test3=?"); 
    $refArr = array(0=>"si",1=>"hallo",2=>1); 
    $ref    = new ReflectionClass('mysqli_stmt'); 
    $method $ref->getMethod("bind_param"); 
    $method->invokeArgs($res,$refArr); 
    $res->execute(); 
    $refArr[1] = "ciao";
    $refArr[2] = 2;
    $res->execute();
    // Trägt wie gewünscht beim ersten execute() "hallo" und 1 ein, beim zweiten execute() "ciao" und 2 

    Einen Kommentar schreiben:


  • Ballamann
    antwortet
    Danke mermshaus!

    Durch deinen Beitrag hab ich wieder ein wenig google gequält und bin schließlich auf diese Seite hier gestoßen: klick

    Des Rätsels Lösung besteht darin von meinem Array $params[] eine Kopie mit Referenzen auf dieses Array zu erstellen. Danach kann ich wie gewünscht $params verändern und es funktioniert. Dickes Danke!

    Einen Kommentar schreiben:


  • mermshaus
    antwortet
    Sorry, mir erschließt sich das aktuelle Probleme nicht so richtig und ich bin gerade nicht in der Stimmung, den gesamten Thread nachvollziehen zu wollen. Aber ich habe neulich in einem anderen Kontext mal das hier gebastelt:

    PHP-Code:
    <?php

    class Test
    {
        protected 
    $_mysqli_stmt;
        protected 
    $_mysqli;

        public function 
    __construct($server$user$password$db)
        {
            
    $this->_mysqli = new mysqli($server$user$password);
            
    $this->_mysqli->select_db($db);
            
    $this->_mysqli_stmt $this->_mysqli->stmt_init();
        }

        
    /**
         *
         * @see http://www.php.net/manual/en/mysqli-stmt.bind-param.php#96770
         */
        
    protected function _refValues($arr){
            if (
    strnatcmp(phpversion(),'5.3') >= 0//Reference is required for PHP 5.3+
            
    {
                
    $refs = array();
                foreach(
    $arr as $key => $value)
                    
    $refs[$key] = &$arr[$key];
                return 
    $refs;
            }
            return 
    $arr;
        } 

        public function 
    insert($table$data)
        {
            
    $sql 'INSERT INTO `' $this->_mysqli->escape_string($table) . '`';

            
    // Feldnamen hinzufügen
            
    $escapedKeys array_map(array($this->_mysqli'escape_string'), array_keys($data));
            
    $sql .= ' (`' implode('`, `'$escapedKeys) . '`)';
         
            
    // Platzhalter für Werte hinzufügen
            
    $sql .= ' VALUES (' implode(', 'array_fill(0count($data), '?')) . ')';

            
    // Feldtypen ermitteln
            
    $type '';
            
    $values array_values($data);

            foreach (
    $values as $value) {
                if (
    is_int($value)) {
                    
    $type .= 'i';
                } else if (
    is_double($value)) {
                    
    $type .= 'd';
                } else {
                    
    $type .= 's';
                }
            }

            if (!(
    $this->_mysqli_stmt->prepare($sql))) {
                return 
    FALSE;
            }

            if (!
    call_user_func_array(array($this->_mysqli_stmt"bind_param"),
                
    array_merge(array($type), $this->_refValues($data)))
            ) {            
                return 
    FALSE;
            }

            if (!(
    $this->_mysqli_stmt->execute())) {
                return 
    FALSE;
            }
            
            if ((
    $this->_mysqli_stmt->affected_rows) <= 0) {
                return 
    FALSE;
            }
            
            return 
    TRUE;
        }
    }

    $t = new Test('localhost''user''pass''dbname');

    $data = array(
        
    'headline'  => 'db-test',
        
    'text'      => 'eins, zwei, drei, vier',
        
    'author_id' => 12
    );

    $f $t->insert('news'$data);

    if (
    $f == false) {
        echo 
    'Fehler';
    } else {
        echo 
    'Erfolg';
    }
    Vielleicht hilft es ja. (Bitte keine Kommentare über den Sinngehalt des Codes. Ich weiß, dass der fast völlig zweckfrei ist. )

    Edit: Zusammengefasst: Es gibt da an einer Stelle ein abweichendes Verhalten ab PHP 5.3 (siehe verlinkter Post). Benutzt du PHP 5.3?

    Einen Kommentar schreiben:


  • Ballamann
    antwortet
    Das leuchtet ein. Nur wie löse ich dann das ganze Problem, dass die übergebenen Werte so in das Array übernommen werden das auch Funktionen wie bind_param() richtig funktionieren?

    Fällt dir spontan ne Möglichkeit ein set_parameter() umzuschreiben?

    Einen Kommentar schreiben:


  • Paul.Schramenko
    antwortet
    Eine Referenz ist ein Zeiger auf eine Variable im Speicher.
    Wohin willst du zeigen, wenn du keine Variable hast sondern diesen String einfach übergibst?

    Einen Kommentar schreiben:


  • Ballamann
    antwortet
    und? gibts ne Möglichkeit das zu lösen?

    Einen Kommentar schreiben:


  • Paul.Schramenko
    antwortet
    Zitat von Ballamann Beitrag anzeigen
    Liegt das vielleicht daran das beim Aufruf ja keine Variable sondern direkt ein String übergeben wird?
    Ja.

    Einen Kommentar schreiben:


  • Ballamann
    antwortet
    Zitat von Paul.Schramenko Beitrag anzeigen
    Ziemlich dreist für jemanden, der Hilfe sucht...
    Wie man in den Wald ruft so schallt es heraus, ich muss mich ja nich von ihm wie nen Idiot hinstellen lassen nur weil ich Anfänger in der Materie bin oder?
    Des weiteren ist der Aufruf an der Stelle falsch.
    PHP-Code:
    function set_parameter($parameter, &$value) {
        
    $this->params[$parameter] = $value;

    Das & muss in den Funktionskopf, wo der Parameter übergeben wird!
    Ja das hab ich mal ausprobiert, wenn ich das & allerdings in den Funktionskopf schreibe bekomm ich folgende Fehlermeldung:
    Fatal error: Cannot pass parameter 2 by reference
    Liegt das vielleicht daran das beim Aufruf ja keine Variable sondern direkt ein String übergeben wird?
    Nachtrag:
    Außerdem finde ich diese Klasse auch recht unnötig... Ich würde dir eher empfehlen dir eine DB-Klasse zu schreiben, mit der du einfacher SQL-Queries ausführen kannst... Würde denke ich mehr Sinn machen. Und wenn du dafür keine Lust hast, such dir eine im Netz oder benutz PEAR
    Doch, ich denke wenn das dort oben läuft werd ich auch mal versuchen nen praktischen Nutzen aus der Klasse zu ziehen, aber im Moment gehts halt hauptsächlich ums Lernen. Es gibt nicht mal ein Projekt zu der Klasse, sie soll einfach nur funktionieren.

    @Woskamp: Prepared Statements funktionieren bei PDO und MySQLi von der Handhabung her doch gleich? Was in den Funktionen passiert weiß ich zwar nicht, aber ich denke dass das Problem eher bei dem verwendeten Array und dessen Aufruf liegt.

    Einen Kommentar schreiben:


  • woskamp
    antwortet
    Du solltest Dich mal näher mit PHP PDO auseinandersetzen:

    Einführung PHP PDO

    speziell prepared statement

    PHP-Code:
        /*** prepare the SQL statement ***/
        
    $stmt $dbh->prepare("SELECT * FROM animals WHERE animal_id = :animal_id AND animal_name = :animal_name");

        
    /*** bind the paramaters ***/
        
    $stmt->bindParam(':animal_id'$animal_idPDO::PARAM_INT);
        
    $stmt->bindParam(':animal_name'$animal_namePDO::PARAM_STR5);

        
    /*** execute the prepared statement ***/
        
    $stmt->execute(); 

    Einen Kommentar schreiben:


  • Paul.Schramenko
    antwortet
    Zitat von Ballamann Beitrag anzeigen
    Wahnsinnig schlauer Spruch. Bist du mit der eigentlichen Frage überfordert oder warum kommt nichts in die Richtung?
    Ziemlich dreist für jemanden, der Hilfe sucht...

    Des weiteren ist der Aufruf an der Stelle falsch.
    PHP-Code:
    function set_parameter($parameter, &$value) {
        
    $this->params[$parameter] = $value;

    Das & muss in den Funktionskopf, wo der Parameter übergeben wird!

    Nachtrag:
    Außerdem finde ich diese Klasse auch recht unnötig... Ich würde dir eher empfehlen dir eine DB-Klasse zu schreiben, mit der du einfacher SQL-Queries ausführen kannst... Würde denke ich mehr Sinn machen. Und wenn du dafür keine Lust hast, such dir eine im Netz oder benutz PEAR

    Einen Kommentar schreiben:


  • Ballamann
    antwortet
    Zitat von makko Beitrag anzeigen
    bind_param() macht nur Sinn, wenn du Daten an DB übergeben willst, die durch Besuchereingaben (Z.Bsp. per POST aus Formular) reinkommen.

    Nein, bind_param() macht bei jeder Abfrage Sinn die ich öfter als ein mal ausführen muss.

    Jetzt mal im Ernst Leute, über den Sinn der ganzen Klasse lässt sich nach Meinung von Nikosch streiten - das seh ich ein. Trotzdem wäre es nett wenn ihr euch der eigentlichen Frage widmen würdet.

    Total sinnlos ist die Klasse jedenfalls nicht - auch wenn sie wohl nicht gut genug für größere Projekte ist, aber dafür ist man eben Anfänger.

    Einen Kommentar schreiben:


  • makko
    antwortet
    bind_param() macht nur Sinn, wenn du Daten an DB übergeben willst, die durch Besuchereingaben (Z.Bsp. per POST aus Formular) reinkommen.

    PHP-Code:
    $db = new db;

    db->prepare_query("INSERT INTO user (Feld1, Feld2, Feld3, Feld4) VALUES (?, ?, ?, ?)");
    //Parameter einbinden
    $db->bind_param("ssss"$_POST['var1'], $_POST['var2'], $_POST['var3'], $_POST['var4']);
    //Ausführen
    $db->execute();

    $ergebnis $db->fetch() ; 

    Einen Kommentar schreiben:


  • Ballamann
    antwortet
    Zitat von ChrisB Beitrag anzeigen
    Gewinn, und damit Sinn des ganzen Unterfangens - absolut keiner.
    Neben einer Menge praktischer Erfahrung sind in meinen Funktionen auch Überprüfung auf Fehler, deren Weiterleitung und die Möglichkeit für mehr vorhanden.
    Warum ich sowas schreibe kann dir doch letztenendes egal sein oder? Ich fange in 3 Monaten eine Ausbildung zum FIAE an und jeden Fehler den ich jetzt mache kann ich mir dann sparen.
    Ausser du betrachtest es als Gewinn, dass dein Code nicht funktioniert. Nicht lauffähige Scripte kann man sich aber auch mit weniger Aufwand basteln.
    Wahnsinnig schlauer Spruch. Bist du mit der eigentlichen Frage überfordert oder warum kommt nichts in die Richtung? Ich bin mir sicher das zumindest der Teil der im Moment nicht funktioniert für andere (eigene) Systeme durchaus zu gebrauchen wäre, es wäre also durchaus interessant zu erfahren wo der Fehler liegt.

    Einen Kommentar schreiben:


  • ChrisB
    antwortet
    Zitat von Ballamann Beitrag anzeigen
    So funktioniert der Aufruf meines neuen Objekts.
    Das Erzeugen des neuen DB-Objektes, Vorbereiten der Query, Binden der Variablen und Ausführen - all das läuft ganz genau so, wenn du wirklich das native MySQLi nimmst.

    Im Grunde hast du nur die Funktionen „umbenannt“ - ausführen musst du sie nach wie vor alle (und bei deinem Code noch ein paar zusätzliche).

    Gewinn, und damit Sinn des ganzen Unterfangens - absolut keiner.
    Ausser du betrachtest es als Gewinn, dass dein Code nicht funktioniert. Nicht lauffähige Scripte kann man sich aber auch mit weniger Aufwand basteln.

    Einen Kommentar schreiben:

Lädt...
X