Kiwi_gamer01: Fatal error: Uncaught Error: Call to a member function bind_param() on boolean

	<form action="playmobil.php" method="POST">
		<table align="center" border="1">
			<tr>
				<th></th>
				<th>Wert</th>
				<th></th>
				<th>INTO</th>
				<th></th>
				<th>ID</th>
				<th></th>
				<th>Senden</th>
			</tr>
			<t>
				<td><b>UPDATE playmobil SET</td>
				<td><input type="text" name="Wert" /></td>
				<td><b>=</td>
				<td><input type="text" name="into" /></td>
				<td><b>WHERE ID =</td>
				<td><input type="int" name="ID2" /></td>
				<td><b>;</td>
				<td><input type="submit" name="submit2" value="Senden" /></td>
			</t>
		</table>
	</form>	
<?php
	if(isset($_POST["submit2"])):
	
		echo $_POST["Wert"];
		echo $_POST["into"];
		echo $_POST["ID2"];
		
		$Wert = $_POST["Wert"];
		$into = $_POST["into"];
		$ID2 = $_POST["ID2"];
	
		$absenden2 = $mysqli->prepare("UPDATE playmobil SET ? = '?' WHERE ID=?");
		$absenden2->bind_param("ssi", $Wert, $into, $ID2);               <!-- Zeile 150-->
		$absenden2->execute();
		$absenden2->close();
endif;		

Warum erhalte ich diese Fehlermeldung

Fatal error: Uncaught Error: Call to a member function bind_param() on boolean in D:\Programme\XMAPP\htdocs\playmobil.php:150 Stack trace: #0 {main} thrown in D:\Programme\XMAPP\htdocs\playmobil.php on line 150

  1. Identyfier dürfen nicht mit einem Platzhalter besetzt werden. Das ist der Fehler. MFG

  2. Tach!

    Warum erhalte ich diese Fehlermeldung

    Fatal error: Uncaught Error: Call to a member function bind_param() on boolean

    Du bekommst die Fehlermeldung, weil du nicht berücksichtigt hast, dass prepare() nicht nur ein Objekt, sondern auch einen booleschen Wert liefern kann.

    dedlfix.

  3. Hallo,

    	if(isset($_POST["submit2"])):
    	
    		echo $_POST["Wert"];
    		echo $_POST["into"];
    		echo $_POST["ID2"];
    		
    		$Wert = $_POST["Wert"];
    		$into = $_POST["into"];
    		$ID2 = $_POST["ID2"];
    	
    		$absenden2 = $mysqli->prepare("UPDATE playmobil SET ? = '?' WHERE ID=?");
    		$absenden2->bind_param("ssi", $Wert, $into, $ID2);               <!-- Zeile 150-->
    		$absenden2->execute();
    		$absenden2->close();
    endif;		
    

    Warum erhalte ich diese Fehlermeldung

    Fatal error: Uncaught Error: Call to a member function bind_param() on boolean in D:\Programme\XMAPP\htdocs\playmobil.php:150 Stack trace: #0 {main} thrown in D:\Programme\XMAPP\htdocs\playmobil.php on line 150
    

    steht doch (fast) alles in der Fehlermeldung: Du versuchst, eine Methode bind_param() von einem boolean aufzurufen, anstatt einem mysqli-Objekt.

    Und wie kommt das? Weil du jegliche Fehlerüberprüfung weglässt und dich blind darauf verlässt, dass alle Methodenaufrufe erfolgreich sind.

    Die mysql- und mysqli-Funktionen sind bekannt dafür, dass sie im Fehlerfall ein stumpfes boolsches false zurückgeben anstatt einer mysql(i)-Ressource. Genau das muss beim Aufruf von prepare() passiert sein, $absenden2 ist also false.

    Abgesehen davon: Warum kopierst du die Werte aus $_POST[] erst um, bevor du sie verwendest? Das ist nicht nur unnötig, sondern manchmal auch tückisch, weil man damit die Herkunft verschleiert.

    So long,
     Martin

    --
    Ich stamme aus Ironien, einem Land am sarkastischen Ozean.
    1. Und wie kommt das? Weil du jegliche Fehlerüberprüfung weglässt und dich blind darauf verlässt, dass alle Methodenaufrufe erfolgreich sind.

      Oder weil unser OP erwartet hat, dass PHP Fehlermeldungen an sinnvollen Stellen produziert. Der Fehler beim bind_param ist ja nur ein Folgefehler, weil das prepare zuvor nicht geklappt hat. Da haben wir den Silent Failure von letzer Woche wieder. Und wieder eine Buchung auf das technische Schuldenkonto von PHP.

      Abgesehen davon: Warum kopierst du die Werte aus $_POST[] erst um, bevor du sie verwendest? Das ist nicht nur unnötig, sondern manchmal auch tückisch, weil man damit die Herkunft verschleiert.

      Das mit der Herkunft der Daten spielt selten eine Rolle. Ungefilterte Nutzereingaben können zwar zu SQL-Injections führen, aber das löst man indem man die Daten entsprechend ihres Zielkontextes behandelt. Das sollte man immer machen, nicht nur bei Benutzereingaben.

      Das Umkopieren ist reine Geschmackssache. Spätestens wenn ich ein zweites mal auf den selben Eintrag eines Arrays zugreifen muss, lege ich mir dafür gewöhnlich eine Variable an, so vermeide ich es mich selbst zu widerholen. Speichertechnisch spricht auch wenig dagegen, da PHP die Daten nicht wirklich kopiert, bis man sie verändert.

  4. Hallo Kiwi_gamer01,

    dass man einen Spaltennamen nicht per Parameter binden kann, hast Du schon gelesen.

    Die Frage ist: Wie macht man es?

    Du kommst hier mit PREPARE nicht weiter, du bist gezwungen, das SQL selbst zusammen zu bauen.

    Man ist dann verleitet, es so zu machen:

    		$absenden2 = $mysqli->prepare("UPDATE playmobil SET $_POST[Wert] = '?' WHERE ID=?");
    		$absenden2->bind_param("si", $into, $ID2);               <!-- Zeile 150-->
    		$absenden2->execute();
    		$absenden2->close();
    

    Was dann bösen Menschen das Schreddern der DB erlaubt.

    Ach übrigens: Hast Du $Wert und $into vertauscht? Man sollte doch meinen, dass der Feldname in INTO steht.

    Es stellt sich die Frage, ob dein Datenmodell taugt. Oder deine Eingabemaske. Weil - es ist leichtfertig, einem Web-Anwender blindlings einen Update auf eine beliebige Spalte zu erlauben. Du wirst eine gewisse Anzahl von Spalten in der Playmobiltabelle haben. Bevor Du das rote Statement einsetzt, musst Du also prüfen, ob in $_POST['Wert'] einer der zum Überschreiben erlaubten Column-Namen steht. Das kann man mit in_array tun (vorher sicherstellen, dass alles klein oder groß geschrieben ist). Dann ist es okay und Du musst auch nicht mit $mysqli->escape_string arbeiten. ABER AUCH NUR DANN.

    Die Alternative wäre, statt einzelnen Spalten eine Attribute-Tabelle zu machen, in der die Spalten ID, NAME und WERT stehen. Dann musst Du bei neuen Attributen keine Tabellenänderung machen. Das Verarbeiten ist dann nur etwas mühsamer, weil Du nicht alle Attribute in einer Zeile findet. Ist nur eine Idee.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Man ist dann verleitet, es so zu machen:

      		$absenden2 = $mysqli->prepare("UPDATE playmobil SET $_POST[Wert] = '?' WHERE ID=?");
      

      Was dann bösen Menschen das Schreddern der DB erlaubt.

      Lösung: Vergleichen von $_POST['Wert'] mit den Spalten der Tabelle und nur im positiven Fall an die Datenbank senden.

      Wichtig In ($_POST[Wert]) ist die Angabe des Strings ohne Quotas (hier: Wert) bzw. die Verwandlung der nicht besetzten Konstante in einen String depraceted und wird in zukünftigen PHP-Versionen keine Notiz sondern einen Error abliefern!

      1. Hallo Schreddern,

        wenn du mich korrigieren willst, dann recherchiere in Zukunft besser und lese genauer.

        Zur "Lösung": Was schrieb ich denn?

        musst Du also prüfen, ob in $_POST['Wert'] einer der zum Überschreiben erlaubten Column-Namen steht. Das kann man mit in_array tun.

        D.h. man verwende ein Array mit den erlaubten Spaltennamen. Für eine komplexere Lösung kann man eine Schemaabfrage auf die DB machen und prüfen ob ein bekannter Spaltenname angegeben wird, aber

        es ist leichtfertig, einem Web-Anwender blindlings einen Update auf eine beliebige Spalte zu erlauben

        Zum Hinweis auf String-Interpolation: Es ist richtig, dass der Zugriff auf einen nichtnumerischen Array-Key ohne String-Delimiter deprecated ist. In PHP 5 eine Notice, in PHP 7 eine Warnung, angekündigt als Error. ABER: "SET $_POST['Wert'] = '?'" ist ein Syntaxerror. Die simple syntax für string parsing gibt die Notation ohne Hochkomma ausdrücklich vor. Das war schon immer so, und auf der entsprechenden PHP.NET Seite steht nichts von Deprecation. Grund für die Deprecation ist, dass ohne Quotes ein define hineingrätschen kann. Aber string parsing ignoriert in der simple syntax defines. Die complex syntax verhält sich anders: "SET {$_POST[Wert]} = '?'" wäre falsch, hier würde ein define("Wert", "Blubb") beachtet werden, darum müssen hier Quotes hin: "SET {$_POST['Wert']} = '?'" ist richtig.

        Ich will nicht sagen, dass PHP das gut macht. Die simple syntax enthält hier einen sehr unnötigen Quirk aus der Zeit, wo der PHP Parser zu dumm war für sowas. Aber es gibt zu viel Alt-Code, der nicht mehr laufen würde wenn man das ändert.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. wenn du mich korrigieren willst, dann recherchiere in Zukunft besser und lese genauer.

          Oh nein, da hast Du mich (sicherlich ist das meine Schuld) missverstanden. Auch wenn es umm 02:30 war, ich wollte Dich keinesfalls korrigieren, sondern nur Deine Ausführungen ergänzen.

          Das Du sogar selbst den Vergleich des übermittelten Spaltennamen mit einem Array tatsächlicher bzw. erlaubter Spaltennamen nahegelegt hattest hatte ich aus zwei Gründen nicht (mehr) gelesen:

          • 02:30, ich war ja nicht mal in der Lage den Namen und das Subject in das richtige Formularfeld einzutragen.
          • Es war nicht unterstrichen. (Das ist ein Witz bezogen auf ein gewisses Erlebnis mit insgesamt 4 Richtern)

          Ich will nicht sagen, dass PHP das gut macht.

          Deine Zurückhaltung ist „fast unangemessen“. Ich hätte mich wegen dieser „tollen“ Idee „bis auf Messer“ gestritten. Solcher Mist gehört nicht in eine Programmiersprache.

          Die simple syntax enthält hier einen sehr unnötigen Quirk aus der Zeit, wo der PHP Parser zu dumm war für sowas. Aber es gibt zu viel Alt-Code, der nicht mehr laufen würde wenn man das ändert.

          Und dieses Mal reicht ein einfacher Regex für das Anpassen der Skripte leider nicht. Ich wollte hier tatsächlich warnen - denn wir stimmen ja in der Ansicht, dass da nicht noch mehr „Quirks“ hinzukommen sollte, überein.

          1. Hallo Raketenjörg,

            danke für die Klarstellung.

            Ob man sowas per Regex nicht finden kann - hm. Wäre eine Challenge 😉.

            Aber vielleicht wäre ein Durchlauf mit PHP -l einfacher. Wenn's ein Error ist, sollte der Linter das funden.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Aber vielleicht wäre ein Durchlauf mit PHP -l einfacher. Wenn's ein Error ist, sollte der Linter das funden.

              Naja. Jedenfalls bei relativ einfachen Skripten und solchen, die man einfach mal starten kann …

              Beispiel:

              <?php
              error_reporting(E_ALL);
              echo $_POST[wert]; # Ein Fehler ist ein Fehler ist ein Fehler…
              ?>
              

              … könnte man die Fehlerausgabe …

              PHP Warning:  Use of undefined constant wert - assumed 'wert' (this will throw an Error in a future version of PHP) in /tmp/test.php on line 3
              

              … auswerten und Patch-Dateien erzeugt, welche dann ein weiteres Skript sucht und die betroffenen Datei(en) patcht. Dann wäre auch der Rückweg einfach.

              Sind die Skripte komplizierter (z.B. durch dynamische, datenabhängige Einbindung von Libs, welche die oben gezeigte Konstante namens „wert“ definieren) wird eine heftige Testumgebung notwendig, was die Frage aufwirft, ob man nicht einfach eine Entwicklungsumgebung anwirft und nach dem verquirksten Zeug sucht.

              Ich bin froh, dass ich es mir nie erlaubt habe…

              1. Hallo Raketenfuzzi,

                dynamische, datenabhängige Einbindung von Libs

                brrrr 😉 - Scherz, ich weiß dass sowas passiert. Ein Autoloader für Klassen in einer Anwendung mit Controller/Action Pattern erzeugt die Situation auch.

                Ein Linter startet das Programm nicht. Sollte man den Linter nicht über die Libs bzw. Klasendateien laufen lassen können?

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Ein Linter startet das Programm nicht. Sollte man den Linter nicht über die Libs bzw. Klassendateien laufen lassen können?

                  Die Jerewanische Antwort: „Im Prinzip schon, aber …“

                  Der Linter kann den Zusammenhang nicht (in jedem Fall) herstellen. Wird die Konstante von einem dynamisch eingebundenen Skript gesetzt, dann wird der Linter deren Verwendung (wohl) als ungequoteten String monieren.

                  Und da es ja um ein Programm geht, welches den Fehler korrigieren soll, wird der Problemstack dadurch voller und voller…

        2. Ich will nicht sagen, dass PHP das gut macht. Die simple syntax enthält hier einen sehr unnötigen Quirk aus der Zeit, wo der PHP Parser zu dumm war für sowas. Aber es gibt zu viel Alt-Code, der nicht mehr laufen würde wenn man das ändert.

          Wenn man den Parser um neue Content-Types erweitern will kommt man um einen Wrapper nicht herum. Und bei der Gelegenheit kann man auch Methoden definieren die den Zugriff auf $_POST//$_GET abstrahieren.

          Nurmalso als Idee.

          1. Ich will nicht sagen, dass PHP das gut macht. Die simple syntax enthält hier einen sehr unnötigen Quirk aus der Zeit, wo der PHP Parser zu dumm war für sowas. Aber es gibt zu viel Alt-Code, der nicht mehr laufen würde wenn man das ändert.

            Wenn man den Parser um neue Content-Types erweitern will kommt man um einen Wrapper nicht herum. Und bei der Gelegenheit kann man auch Methoden definieren die den Zugriff auf $_POST//$_GET abstrahieren.

            Nurmalso als Idee.

            Und vor allem die Requestmethode aus der Anwendung raushalten. Das ist HTTP und damit ein anderer Layer.

            MFFG

          2. Dieser Beitrag wurde gelöscht: Der Beitrag ist unkonstruktiv oder provokativ und trägt zu einer Verschlechterung der Stimmung bei.