Avatar billede Mik2000 Professor
20. januar 2013 - 21:37 Der er 30 kommentarer og
1 løsning

bind_param problem

Hej

Er ved at prøve at lave en lille funktion, som kan eksekverer select sætninger og returnerer et array, men jeg er stødt ind i et problem:

Understående kode er en del af en funktion, hvor jeg har medsendt nogle parametre som et array.
Problemet er at de skal sættes ind i bind_param som variabler.

foreach($params as $p_key -> $p_value) {
  // output vil være: Key=0, Value=var1
}

$stmt->bind_param("$typeDef", $Variabler_fra_foreach);

Så med andre ord har jeg medsendt de variabler der skal skiftes ud med ? i min query, og de ligger i arrayet parrams
De skal så sættes ind i bind_param()
TypeDef er "s" for string

Håber du forstår hvad jeg mener og kan hjælpe :)
Avatar billede Mik2000 Professor
20. januar 2013 - 21:41 #1
Ups ved godt det er => og ikke ->

Det er måske vigtigt at tilføje at jeg godt kan få det til at virke så længe der kun er en ting i arrayet, men når der kommer 2 eller flere er der problemer.

Ved 1 kan man jo gøre som følger
foreach($params as $p_key => $p_value) {
  $var = $p_value
}

$stmt->bind_param("$typeDef", $var);

Men hvis der er 2 ? i queryen, og dermed 2 værdier i mit array, så holder det ikke.
Avatar billede olebole Juniormester
20. januar 2013 - 22:28 #2
<ole>

Det kan du ikke. Du skal angive de enkelte variabler - ikke et array. Du kan lave nogle krumspring med referencer og ReflectionClass eller call_user_func_array - men på dette område er PDO faktisk en del lettere at have med at gøre

/mvh
</bole>
Avatar billede arne_v Ekspert
21. januar 2013 - 02:48 #3
mysqli:


<?php
function load($con, $sqlstr, $typs, $vals) {
    if($stmt = $con->prepare($sqlstr)) {
        call_user_func_array(array($stmt, 'bind_param'), array_merge(array($typs), array_map(function(&$v) { return $v; }, $vals)));
        $stmt->execute();
        if($res = $stmt->get_result()) {
            $retval = $res->fetch_all(MYSQLI_ASSOC);
            $res->free();
            $stmt->close();
        } else {
            die($con->error);
        }
    } else {
        die($con->error);
    }
    return $retval;
}

function test($con, $sqlstr, $typs, $vals) {
    $arr = load($con, $sqlstr, $typs, $vals);
    foreach($arr as $row) {
        echo $row['f1'] . ' ' . $row['f2'] . "\r\n";
    }
}

$con = new mysqli('localhost', 'root', '', 'Test');
if(mysqli_connect_errno()) die(mysqli_connect_error());
test($con, 'SELECT f1,f2 FROM t1 WHERE f1=?', 'i', array(2));
test($con, 'SELECT f1,f2 FROM t1 WHERE f1=? OR f1=?', 'ii', array(1,3));
$con->close();
?>


PDO:


<?php
function load($con, $sqlstr, $vals) {
    $stmt = $con->prepare($sqlstr);
    $stmt->execute($vals);
    return $stmt->fetchAll();   
}

function test($con, $sqlstr,$vals) {
    $arr = load($con, $sqlstr, $vals);
    foreach($arr as $row) {
        echo $row['f1'] . ' ' . $row['f2'] . "\r\n";
    }
}

try {
    $con = new PDO('mysql:host=localhost;dbname=Test', 'root', '');
    $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    test($con, 'SELECT f1,f2 FROM t1 WHERE f1=:v1', array(':v1' => 2));
    test($con, 'SELECT f1,f2 FROM t1 WHERE f1=:v1 OR f1=:v2', array(':v1' => 1,':v2' => 3));
} catch (PDOException $ex) {
    die($ex->getMessage());
}
?>
Avatar billede olebole Juniormester
21. januar 2013 - 03:21 #4
@Arne: Der var den combo igen (array_map/anonymous function) - og den gør godt nok koden væsentlig mere elegant. Jeg må se at få indarbejdet (huske), at den mulighed nu også eksisterer i PHP  =)
Avatar billede Mik2000 Professor
22. januar 2013 - 00:43 #5
Hej

Forstod ikke funktionen helt, men har læst og læst, set videoer osv, og har for øvelsens skyld prøvet at lave min egen funktion, som returnerer et array med resultaterne fra en select

Det jeg håber I vil gøre er at komme med kommentarer til min funktion så jeg kan lære af det :)

Så med andre ord komme med kommentarer til:
1: Forbedringer
2: Sikkerhedsproblemer
3: Hastighedsproblemer
4: Fejl/problemer der kan komme som der ikke er taget højde for
5: Ting man bør gøre anderledes

Jeg takker mange mange gange for jeres store hjælp - takket være jer, og jeres eksempler mv. er jeg ved at begynde at forstå PDO lidt :)

Koden er herunder

// ---------------------------------------------
// Connection
// ---------------------------------------------
$host = "localhost";
$username = "bruger";
$password = "pass";
$dbname = "databasenavn";
$db = new PDO('mysql:host='.$host.';dbname='.$dbname, $username, $password);


// ---------------------------------------------
// Funktion
// ---------------------------------------------
function db_select($db, $sql, $params) {
    $query = $db->prepare($sql);

    foreach ($params as $p_key => $p_value) {
        $query->bindValue($p_key, $p_value);
    }
   
    $query->execute() or die(print_r($query->errorInfo(), true));
   
    $posts = 0;
    while($row = $query->fetch(PDO::FETCH_ASSOC)) {
        foreach($row as $row_key => $row_value) {
            $result[$posts][$row_key] = $row_value;
        }
        $posts = $posts + 1;
    }
    return $result;
}



// ---------------------------------------------
// Kører funktionen som returnerer et array
// ---------------------------------------------
$sql = "SELECT * FROM slet WHERE navn LIKE :name AND telefon LIKE :phone";
$params = array(':name' => '%is%',':phone' => '%9%');
$resultarray = db_select($db, $sql, $params);

// ---------------------------------------------
// Udskriver hvis mange resultater
// ---------------------------------------------
//foreach ($resultarray as $posts) {
//    foreach ($posts as $r_key => $r_value) {
//        echo $r_key."-".$r_value."<br />";
//    }
//    echo "<br />";
//}

// ---------------------------------------------
// Udskriver hvis der kun er 1 resultat
// ---------------------------------------------
//foreach ($posts as $r_key => $r_value) {
//    echo $r_key."-".$r_value."<br />";
//}
Avatar billede arne_v Ekspert
22. januar 2013 - 00:53 #6
Det er ikke klart for mig hvad du opnaar ved at:
- loope over params i.s.f. bare at sende dem til execute
- loope over raekker i.s.f. at bruge fetchAll
Avatar billede olebole Juniormester
22. januar 2013 - 01:05 #7
@Arne: Zend er lidt forbeholdne overfor fetch_all:

Further, if you need to iterate over the result set, you will need a looping construct that will further impact performance. For these reasons mysqli_fetch_all() should only be used in those situations where the fetched result set will be sent to another layer for processing.

Måske, det er årsagen(?) Det kommer lidt an på, hvordan koden skal bruges, og til hvad
Avatar billede arne_v Ekspert
22. januar 2013 - 02:18 #8
ja

men som jeg laeser det saa siger det at:
- foerste have en loekke inden i fetchAll som henter alle raekker og skovler data over i et array
- derefter have en ny loekke som gaar igennem array og dermed alle raekker
har noget overhead

og det har det jo ogsaa sammenlignet med:
- en loekke som henter alle raekker og processer dem

men det har ikke mere overhead end:
- foerste have en loekke i egen PHP kode som henter alle raekker og skovler data over i et array
- derefter have en ny loekke som gaar igennem array og dermed alle raekker
Avatar billede arne_v Ekspert
22. januar 2013 - 02:21 #9
Ioevrigt tror jeg at overhead ved at iterere gennem et array er ret lille sammenlignet med det at laese fra database og outputte HTML.
Avatar billede Mik2000 Professor
22. januar 2013 - 14:12 #10
Kan godt være jeg får det lavet for tungt på den måde, men i så fald hvad hvordan vil du så ændre den, så man stadig får et array tilbage og dermed ikke er begrænset i brugen på siden, så den både virker hvis der er et eller mange resultater?
Avatar billede arne_v Ekspert
22. januar 2013 - 14:16 #11
Jeg ville ikke bekymre mig spor over at konstruere et array og iterere gennem det.
Avatar billede Mik2000 Professor
22. januar 2013 - 15:39 #12
Okay, så burde godt kunne bruge min funktion, og den er også beskyttet i henhold til injection min. ligeså godt som med real_escape_string før i tiden ik?

Mange tak for den store hjælp - hvis der ligges et svar så er der selvfølgelig point :)
Avatar billede olebole Juniormester
22. januar 2013 - 16:32 #13
@Arne: Jeg mener heller ikke, der er noget problem ved alm. forespørgsler (men nok ved meget store result set). Jeg pointerede blot, at det kunne være dét, der var årsagen til fremgangsmåden  =)

#12: Ja og Nej. Nu er du beskyttet ... det var du ikke før i tiden med mysql_real_escape_string  *o)
Avatar billede Mik2000 Professor
22. januar 2013 - 22:02 #14
Super mange tak for hjælpen - har efterhånden ikke tal på hvor mange gange I har hjulpet mig herinde :)
Det er jeg superglad for :)

Troede det var sikkert så længe man bare brugte den, men det er da kun plus at det er bedre nu :)
Avatar billede Mik2000 Professor
22. januar 2013 - 22:24 #15
Lige en allersidste ting

Jeg tænker jeg lige skal close() og evt free() resultaterne

Jeg har i understående sat
$query->close();
ind inden den laver return result

Men skal jeg også bruge free()
Fangede ikke helt hvornår man bruger free og hvornår man bruger close()


function db_select($db, $sql, $params) {
    $query = $db->prepare($sql);

    foreach ($params as $p_key => $p_value) {
        $query->bindValue($p_key, $p_value);
    }
   
    $query->execute() or die(print_r($query->errorInfo(), true));
   
    $posts = 0;
    while($row = $query->fetch(PDO::FETCH_ASSOC)) {
        foreach($row as $row_key => $row_value) {
            $result[$posts][$row_key] = $row_value;
        }
        $posts = $posts + 1;
    }
    $query->close();
   
    return $result;
}
Avatar billede Mik2000 Professor
22. januar 2013 - 22:29 #16
LOL nu roder jeg vist sammen med mysqli - sorry
Skal man intet gøre med pdo for at "rydde op efter sig"
Avatar billede olebole Juniormester
22. januar 2013 - 23:05 #17
Selve forbindelsen kan du NULL'e (Arnes eksempel):

$con = null;

Derudover kan du bruge closeCursor
Avatar billede arne_v Ekspert
23. januar 2013 - 02:11 #18
Hvis man bruger mysql_real_escape_string og husker:
- at bruge det paa alle strenge
- at bruge noget andet paa alle tal
- at altid sende connection med
saa burde det vaere sikkert.

Jeg har ihvertfald aldrig hoert om sikkerheds problemer med det.

Men med konsekvent brug af prepared statement er der ikke saa meget at huske.
Avatar billede Mik2000 Professor
23. januar 2013 - 11:54 #19
Hej

Mange tak for alle svar.
Hvis I ligger svar, så er der selvfølgelig point :)
Avatar billede arne_v Ekspert
23. januar 2013 - 15:04 #20
svar
Avatar billede Mik2000 Professor
27. januar 2013 - 20:04 #21
Hej Arne og Ole

Lige et tillægsspørgsmål til det med bind_value og bind_param - stiller det her da vi har snakket om det, men opretter gerne et nyt spørgsmål, så I kan få point for svar

Nu har jeg en sql sætning

$sql = "SELECT * FROM personer ORDER BY :columnorder ASC";

Men
$query->bindValue(":columnorder", "navn");
ser kun ud til at virke på værdier og ikke på kollonne navne.

Problemet er at :columnorder hentes gennem en $_GET, så det er vel farligt at sætte den direkte ind.
Fandt en der hed bind_Column, men den virker ikke, og ser heller ikke ud til at være til formålet, når jeg læser om den.

Så hvordan får jeg et kollonne navn sikkert ind i SQL sætningen når det kommer fra en $_GET?
Avatar billede arne_v Ekspert
27. januar 2013 - 20:20 #22
bindValue og bindParam er kun til vaerdier ikke til tabel og felt navne.

Det du vil skal laves paa gammeldags vis.
Avatar billede olebole Juniormester
27. januar 2013 - 20:30 #23
- og det er derfor nok ikke hensigtsmæssigt at bruge kolonnenavne fra brugeren. Så vil du være nødt til at strikke SQL sammen af udefrakommende input - og så ryger sikkerheden ved prepared statements
Avatar billede arne_v Ekspert
27. januar 2013 - 21:16 #24
Hvis det er et lille antal mulige felter ville jeg vaelge en af:

* have en SQL streng per order by og saa bare tage den rigtige

* hvis database specifik (ikke PDO) saa bruge en database specifik funktion, eksempel for MySQL md 2 felter:

... ORDER BY IF(?,feltA,feltB)
Avatar billede Mik2000 Professor
27. januar 2013 - 22:09 #25
Hej

Tak for svarene. Det er fordi man skal kunne trykke på en kollonne overskrift i en tabel, og så sorterer den efter det, og kollonnerne er dynamisk genereret.

Så det bliver lidt svært at at generer en sql pr stk. da der kan være forskelligt antal kollonner mv.

Men det er mysql så det andet kan måske bruges.......
Hvordan fungerer ORDER BY IF(?,feltA,feltB) og hvorfor er det sikkert?
Avatar billede arne_v Ekspert
27. januar 2013 - 22:14 #26
Men behoever den sortering blive lavet i databasen?

Masske kan den laves i applikationen.
Avatar billede arne_v Ekspert
27. januar 2013 - 22:15 #27
ORDER BY IF(?,feltA,feltB)

og saette ? til enten true eller false giver ingen mulighed for SQL injection.
Avatar billede Mik2000 Professor
27. januar 2013 - 22:27 #28
Som sådan ikke, og så alligevel

For der kan jo være mange der skal hentes via en query, og dermed en stor mængde data.

Så derfor hentes det jo således at man får f.eks. 100 resultater, og så står der side 2, 3, 4 osv nede i bunden. Og skifter man her, så henter den igen, men hvis man vælger at sorterer, så skal den jo vises de første 100 efter alle er sorteret.

Den med feltA, feltB osv bliver også svær så, da det skal genereres dynamisk, og man skal kunne klikke på alle overskrifterne, men det kunne lade sig gøre.

Men det er da egentlig underligt der ikke findes en måde at benytte et felt i en sql sætning, uden at det giver problemer :(
Avatar billede Mik2000 Professor
27. januar 2013 - 22:35 #29
Fandt en på en side der skrev det kunne gøres således:

$orders=array("name","price","qty");
$key=array_search($_GET['sort'],$orders));
$order=$orders[$key];
$query="SELECT * from table WHERE is_live = :is_live ORDER BY $order";

På den måde skal det være en af dem i arrayet, så det burde jo være sikkert.
Ikke nemmeste løsning, men enddog nok den eneste lige umiddelbart ik?
Avatar billede arne_v Ekspert
28. januar 2013 - 00:05 #30
Det er en maade at checke input paa.

Ikke nogen daarlig maade.
Avatar billede arne_v Ekspert
28. januar 2013 - 00:09 #31
Med hensyn til hvorfor det er saadan, saa haenger det sammen med hvad der sker bag scenen.

Det er faktisk prepare som sender SQL saetningen til MySQL og execute kalder den kun med nogle vaerdier.

Saa allerede ved prepare skal MySQL "compile" SQL saetningen.

Det er svaert at compile hvis tabel og felt navne er input.

Det er nemmere at compile hvis det kun er vaerdier som er input.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Vi tilbyder markedets bedste kurser inden for webudvikling

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester