PHP und MySQL

ghostadmin

Grand Admiral Special
Mitglied seit
11.11.2001
Beiträge
25.179
Renomée
184
Standort
Dahoam Studios
Mein PHP Script musste ich umschreiben wegen veralteter MySQL Befehle.
Jetzt ist das Problem das nicht die erste Zeile sondern ab der zweiten Zeile ausgegeben wird.

$statement = $pdo->query($Query);
while ($row = $statement->fetch(PDO::FETCH_ASSOC))

http://php.net/manual/de/pdostatement.fetch.php

So ganz schlau werde ich nicht draus, aber da steht ja "Fetches the next row from a result set"
 
Fehlt da nicht ein '$pdo->execute();'?

Ansonsten ist der Fehler eher woanders zu suchen - musste wohl etwas mehr Code posten, denke ich.
 
pdo->query führt den Query aus.

Den SQL Select habe ich auch manuell ausgeführt und der bringt das korrekte Ergebnis

Code:
if (isset($_GET['NextSet'])) {
$NextSet = $_GET['NextSet'];
$NextStartingRow = $NextSet*29;
}


....................
...............
.........
.....
     

        $Query = "SELECT $argnumber, $argname, $argcontact, $argmobile, $arghomenumber ";

        if ($getsort == "company") {
            $Query .= "FROM $mysqltable WHERE ($argnumber IS NOT NULL AND $argnumber <> '' AND $argname IS NOT NULL AND $argname <> '') ";
        } elseif ($getsort == "contact") {
            $Query .= "FROM $mysqltable WHERE ($argnumber IS NOT NULL AND $argnumber <> '' AND $argcontact IS NOT NULL AND $argcontact <> '') ";
        }

        # If we are searching by name (or contact) then add this filter to the query
        if (isset($getname)) {
            $Query .= "and ($argname like '$getname%' or $argcontact like '$getname%') "; #search like entered on phone
        }
        if (isset($getnameconverted) && isset($getname)) {
            $Query .= "or ($argname like '$getnameconverted%' or $argcontact like '$getnameconverted%') "; #search also for umlauts
        }

        # display company names first or contacts?
        if ($getsort == "company") {
            $Query .= "order by $argname,$argcontact ";
        } elseif ($getsort == "contact") {
            $Query .= "order by $argcontact ";
        }

        # If this is the first page of the company directory then we will display the first 30
        if (!isset ($NextSet)) {
                $Query .= "Limit 30 Offset 0";
                $NextSet=0;
        # Now for each subsiquent call we get the next 30 records.
        } else {
                $Query .= "Limit 30 Offset $NextStartingRow";
        }

        # Execute the query
        $statement = $pdo->query($Query);

        # Count the number of rows returned.
        $NumberOfRows = $statement->fetchColumn();


        if ($NumberOfRows >= 30) {
                $NextSetValue = $NextSet+1;
        }

        # Parse through the query and set up the menu items.
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
                $CompanyNameList .= "<MenuItem>\n";
                        $CompanyNameList .= "<Name>";

                        if ($getsort == "company") {
                            if ($row[$argname] > '') {
                                $CompanyNameList .= removeumlauts($row[$argname]);
                            } else {
                                # no company found, nothing to display
                            }
                            if ($row[$argcontact] <> '' and $row[$argname] <> '') {
                                $CompanyNameList .= ", " . removeumlauts($row[$argcontact]);
                            }
                        } elseif ($getsort == "contact") {
                            $CompanyNameList .= removeumlauts($row[$argcontact]);
                            if ($row[$argname] <> '') {
                                $CompanyNameList .= ", " . removeumlauts($row[$argname]);
                            }
                        }

                        $CompanyNameList .= "</Name>\n";
                        $CompanyNameList .= "<URL>";
                                $CompanyNameList .= "$xmldir/companyDirectory.php?";
                                $CompanyNameList .= "displaynumber=";
                                $CompanyNameList .= $row[$argnumber];
                                $CompanyNameList .= "</URL>\n";
                $CompanyNameList .= "</MenuItem>\n";
        }

Am Offset liegts auch nicht, habe auch nur 0 probiert. Auch ohne dem Limit im Query gehts nicht.
 
Zuletzt bearbeitet:
Also Dein Problem sollte sein, dass '$statement->fetchColumn()' schon die erste Zeile fetched. Was Du Abfragen willst ist vermutlich '$statement->rowCount'.

Ansonsten würde ich dringend empfehlen Prepared Statements zu verwenden, gerade wenn man schon PDO nutzt. Vor allem weil die gegen SQL-Injections sicher sind. Deshalb auch meine Verwirrung.
 
Autsch, der hat ja gar nicht richtig die Zeilen gezählt, da habe ich gar nicht hingeschaut beim Fehler suchen.

Warum ist prepare+execute besser als query?
 
Warum ist prepare+execute besser als query?

1. Keine SQL-Injections möglich, wenn man nur mit Parametern arbeitet
2. Lesbarkeit (ist jedenfalls meine Meinung)
3. Effizienter wenn man das Statement mehrfach aufruft

Edit:

Code:
$stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':value', $value);

Damit hat man die Variablen $name und $value angebunden, und kann die zum Beispiel in einer Schleife füllen und das Statement wieder benutzten:

Code:
foreach($foo as $bar)
{
  $name = $bar['name'];
  $value = $bar['value'];
  $stmt->execute();
}
 
Zuletzt bearbeitet:
$statement = $pdo->query($Query);

$statement = $pdo->prepare($Query);
$statement->execute();

In beiden Fällen ist doch das Ergebnis abhängig davon was in $Query steht.

Wenn der Code ist:
$NextStartingRow = $NextSet*29;
$Query .= "Limit 29 Offset $NextStartingRow";

Müsste ja eigentlich der Query einen Fehler ausgeben wenn versucht wird statt Nummern irgendwas anderes mitzugeben als Parameter.
 
Ja, außer Du übergibst z.B. den Wert "0; INSERT INTO users (name, password) VALUES('admin', 'none');" in $NextStartingRow ...

SQL-Injections sind nicht immer einfach herauszufinden, und an dieser Stelle würde vermutlich vorher ein PHP-Syntaxfehler kommen, aber ich habe die schon effektiv ausgenutzt um Webseiten zu übernehmen (bei Penetrationstests). Auch für Blind-SQL-Injections gibt es Tools, so dass man selbst indirekt einen kompletten Dump der Datenbank machen kann, wenn man nur an einer Stelle in ein SQL-Statement rein kommt.

Edit: Zum Beispiel "AND $argname LIKE '$getname%'" wenn man etwas wie "%'; INSERT INTO users (name, password) VALUES('admin', 'none'); --' in $getname bekommt, wird daraus:

AND $argname LIKE '%'; INSERT INTO users (name, password) VALUES('admin', 'none'); --%'

Das Statement ist gültig und wird beendet, ein weiteres Statement wird eingefügt, und der Rest danach einfach auskommentiert.
 
Zuletzt bearbeitet:
Ok dann macht es wohl Sinn die Parameter vor Verarbeitung zu prüfen mit is_numeric bei Nummern oder strlen bei Text
 
Ja, oder Prepared Statements mit Parametern. Da braucht man sich halt um nix weiter zu kümmern mit Eingabeprüfung ... :)

Aus den vordefinierten gebundenen Parametern sollte man per Definition nicht ausbrechen können (sofern da niemand einen Bug eingebaut hat), außerdem kann man die noch typisieren. Zum Beispiel:

Code:
$sth->bindParam(':calories', $calories, PDO::PARAM_INT);
$sth->bindParam(':colour', $colour, PDO::PARAM_STR, 12);

Und das sollte dann auch einen Fehler geben, wenn man versucht was falsches da rein zu stopfen.

Edit: Sorry, sehe gerade, dass das wohl nicht stimmt im Moment. Eine Typprüfung wird nicht gemacht, sollte man daher selbst vorher noch machen. Aber trotzdem werden SQL-Injections verhindert.
 
Zuletzt bearbeitet:
Ok, mit dem bindParam macht das Prepare natürlich Sinn

Bringt es eigentlich was die User,Passwörter für die DB in ein eigenes File zu stecken?
 
Zuletzt bearbeitet:
Bringt es eigentlich was die User,Passwörter für die DB in ein eigenes File zu stecken?

Naja, ich mache das aus Bequemlichkeit immer in einem PHP-Include, wenn ich mehrere PHP-Seiten habe. Was die Sicherheit angeht bringt das aber eher nichts, außer vielleicht noch, wenn das Include außerhalb des normalen Scopes des Webservers liegt. Da muss man halt nach Möglichkeit sehen, dass der DB-Benutzer der Webanwendung (jedenfalls auf dem Produktivsystem) möglichst wenig Rechte hat, also praktisch nur das kann, was die Anwendung braucht.
 
Mit dem bindParam gibts noch Probleme

Code:
$Query .= "Limit 29 Offset :NextStartingRow";
$statement = $pdo->prepare("SELECT ".$argnumber.",".$argname.",".$argcontact.",".$argmobile.",".$arghomenumber." FROM ".$mysqltable." WHERE ".$Query);
$statement->bindParam(':NextStartingRow', $NextStartingRow, PDO::PARAM_INT);

Weil ich double quotes verwendet habe?
 
Wie lautetet denn die Fehlermeldung?
 
Ich schaffe es nicht das Script an der Kommandozeile mit Parameter aufzurufen

php script.php NextSet=1

so irgendwie?

Apache Log sagt:

SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in /var/www/directory/companyDirectory.php on line 205

Zeile 205 ist der execute

Code:
$Query .= "and ($argname like :getname or $argcontact like :getname) "; 
$getname = $getname."%";
$Query .= "or ($argname like :getnameconverted or $argcontact like :getnameconverted) "; 
$getnameconverted = $getnameconverted."%";
$Query .= "Limit 29 Offset :NextStartingRow";
$statement = $pdo->prepare("SELECT ".$argnumber.",".$argname.",".$argcontact.",".$argmobile.",".$arghomenumber." FROM ".$mysqltable." WHERE ".$Query);
$statement->bindParam(':NextStartingRow', $NextStartingRow, PDO::PARAM_INT);
$statement->bindParam(':getnameconverted', $getnameconverted, PDO::PARAM_STR, 10);
$statement->bindParam(':getname', $getname, PDO::PARAM_STR, 10);
$statement->execute();
 
Ich glaube Du darfst die Parameter vermutlich nicht zweimal angeben. Du benutzt zwei mal ":getnameconverted", versuch einfach mal ":getnameconverted1" und ":getnameconverted2".
 
stimmt, hab ich sogar noch irgendwo gelesen.
Allerdings schlägt es bereits fehl wenn man nur einmal :getname verwendet.
 
Code:
$pdo = new PDO("mysql:host=$mysqlhost;dbname=$mysqldb",$mysqluser,$mysqlpass);
 
	
		$Query = "SELECT model,erd,weight,erdadd3,comment,height FROM :rimtype ORDER BY model";
		$statement = $pdo->prepare($Query);
		$statement->bindValue(':rimtype', $rimtype, PDO::PARAM_STR);
		$statement->execute();
		$rows=$statement->rowCount();
		echo $rows;

Ist da irgendwas falsch? Bekomme immer 0 Zeilen als Output. Wenn ich den Select manuell mache, gehts aber.

mysql> select model,erd,weight,erdadd3,comment,height FROM rims406 ORDER BY model;
+------------------------+-------+--------+---------+---------+--------+
| model | erd | weight | erdadd3 | comment | height |
+------------------------+-------+--------+---------+---------+--------+
| Alex DA16 | 383.4 | 340 | 0 | 16C | 0 |
| KinLin Nb-R | 390 | 245 | 0 | 13C | 18.5 |
| Remerx Dragon 719 | 383 | 399 | 0 | 19C | 0 |
| Rigida Andra 40 | 381.9 | 558 | 0 | 25C | 0 |
| Rigida Big Bull | 380.5 | 495 | 0 | 25C | 0 |
| Rigida Big Bull eyelet | 381.3 | 495 | 0 | 25C | 0 |
| Velocity Aerohead | 390 | 275 | 0 | 14C | 0 |
+------------------------+-------+--------+---------+---------+--------+
7 rows in set (0.00 sec)
 
Zuletzt bearbeitet:
Lass dir mal den Fehler ausgeben:

Nach execute(); ein echo $pdo->error();
 
Mit dem Befehl lädt die Seite nicht.

Mit print_r($pdo->errorInfo());

Array ( [0] => 00000 [1] => [2] => )

Auch damit erhalte ich keinerlei Output ausser das HTML Select Feld
Code:
echo "<select name=\"rimmodel\">"; 
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
echo "<option value=\".$row[model].\">.$row[model].</option>";
}
echo "</select>";

Der User hat nur Select Rechte auf der Table.

Edit:
Sobald ich mit bindParam oder bindValue anfange funktioniert es nicht, ansonsten schon
 
Zuletzt bearbeitet:
Ne Frage zur Logik: müsste der SELECT nicht in etwa so aufgebaut werden?
Code:
    $query = "SELECT model,erd,weight,erdadd3,comment,height FROM `meine_tabelle` WHERE `rimtype`= :rimtype ORDER BY model";

Bei deinem Select ist der rimtype ja der tabellenname, das hieße, dass jeder typ seine eigene tabelle hat in der db.
Ist nicht eine Tabelle für die typen logischer?

Code:
$query = "SELECT model,erd,weight,erdadd3,comment,height FROM :rimtype ORDER BY model"

Jedenfalls habe ich auch einen Lösungsvorschlag und eine weitere schöne Vorführung des praktischen Nutzens der PDO.

Code:
$query = "SELECT model,erd,weight,erdadd3,comment,height FROM ? ORDER BY model";
$query= $pdo->prepare($query);
$statement = $pdo->execute(array($rimtype));
$row = $pdo->fetch(PDO::FETCH_ASSOC); // Oder PDO::FETCH_ARRAY bzw. PDO::FETCH_ROW oder PDO::FETCH_OBJ

Die Fragezeichen im Query sind Platzhalter, wobei du deren Inhalte per array bei $pdo->execute() übergibst, wobei die Reihenfolge
von Platzhalter im Query und der Parameter im Array identisch sind.

Bsp:
Code:
$query = $pdo->prepare("SELECT * FROM `my_table` WHERE `foo`= ? AND `bar`= ? AND `baz`= ? ORDER BY `baz` ASC");
$query->execute(array($foo, $bar, $baz));
 
Zurück
Oben Unten