Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET. Zároveň využij akci až 30 % zdarma při nákupu e-learningu - Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
discount 30 + hiring
Avatar
Tomáš Dvořák:15. února 10:57

Mám tento dotaz:

$Podminka = "(nazev LIKE '%ermo%' OR kod LIKE '%ermo%' OR informace LIKE '%ermo%' OR rozsirene_info LIKE '%ermo%') AND (nazev LIKE '%kr%' OR kod LIKE '%kr%' OR informace LIKE '%kr%' OR rozsirene_info LIKE '%kr%')";

$pocet_celkem = $mysqli->prepare('SELECT COUNT(*) FROM table WHERE ? AND zobraz="1"');
$pocet_celkem->bind_param('s', $Podminka);
$pocet_celkem->execute();
$pocet_celkem->store_result();
$pocet_celkem->bind_result($celkem);
$pocet_celkem->fetch();

echo $celkem;

Bohužel mi to vrací 0. Pokud dám příkaz klasicky s dosazením proměnné $Podminka přímo do dotazu klasickou cestou, tak mi to vypíše to co má, tedy cca 600 záznamů.

Je problém ve znacích jako ()""& ? Nebo je problém někde jinde?

Tento kód je v pořádku a funguje:

$podm = "+kab* +kr*";
$pocet_celkem = $mysqli->prepare('SELECT COUNT(*) FROM table WHERE MATCH (`nazev`, `kod`, `informace`, `rozsirene_info`) AGAINST (? IN BOOLEAN MODE) AND zobraz=1');

$pocet_celkem->bind_param('s', $podm);
$pocet_celkem->execute();
$pocet_celkem->store_result();
$pocet_celkem->bind_result($celkem);
$pocet_celkem->fetch();

echo $celkem;

Dotazy jsou dva, v podstatě jde o to, že pokud najde něco fulltextem, tak ať hledá fultextem, pokud není, tak nastupuje ten první kód. Ošetřuji SQL injection a u toho prvního dotazu mi to nechodí.

Mohl by mě prosím někdo nakopnout proč ten LIKE dotaz vrací 0?

Děkuji

 
Odpovědět
15. února 10:57
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:15. února 13:20

Co jsi vycetl prikladu a komentaru v manualu? https://www.php.net/…nd-param.php
Co jsi vycetl z prikladu nalezenych googlem, jak to pouzivaji?

Vis, otazkou je, zda to nekdo pouziva podobne jako ty nebo je vsechno jinak a prikaz jen prevadi cely string tak, aby jej bylo mozna pouzit jako jednu value pro sql prikaz

// string, number
$stmt = mysqli_prepare($link, "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param($stmt, 'sssd', $code, $language, $official, $percent);

// array
$stmt = $mysqli->prepare("SELECT Language FROM CountryLanguage WHERE CountryCode IN (?, ?)");
$stmt->bind_param('ss', ...['DEU', 'POL']);

// kousek sql prikaz
// takova moznost tam neni
// --- takze mas 2 moznosti

$Podminka = "(nazev LIKE '%ermo%' OR kod LIKE '%ermo%' OR informace LIKE '%ermo%' OR rozsirene_info LIKE '%ermo%') AND (nazev LIKE '%kr%' OR kod LIKE '%kr%' OR informace LIKE '%kr%' OR rozsirene_info LIKE '%kr%')";

$query = 'SELECT COUNT(*) FROM table WHERE %s AND zobraz="1"';
$query = sprintf($query, $Podminka );
//$pocet_celkem->bind_param('s', $Podminka);

$query = 'SELECT COUNT(*) FROM table WHERE $Podminka AND zobraz="1"';
//$pocet_celkem->bind_param('s', $Podminka);

Chces docilit obycejne spojeni retezcu, ne? Nebo se ma pri to stat jeste neco jineho?
To je prave otazka, co jineho by se mohlo stat, co tam delas? Jestli se tam da propasovat nejaky nezadouci string.
Vis, ty funkce nedelaji parsovani sql dotazu, pouze doplnuji znaky tak, aby text nenarusil sql dotaz. Proste, doplni znaky k urcitym znakum.
Na parsovani dotazu bys asi potreboval nejakou umelou inteligenci, aby si dokazala tipnout, co tam chces proti tomu, co tam je napsane.

$stmt = mysqli_prepare($link, "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
// ulozi sql dotaz do promene
$stmt->bind_param('sssd', $code, $language, $official, $percent);
// prevede data z $code, $language, $official, $percent na string, string, string, digit (sssd)
// escapuje hodnoty pro stringy, tj, prida uzovozku pred a za a escapuje uvozovky uprostred stringu
// $language -> (string) $language -> escape($language) -> "escape($language)"
// jaz"yk -> (string) jaz"yk -> escape(jaz\"yk) -> "jaz\"yk" // escape se trochu lisi u ruznych sql, obvykle se znak zdvojuje
// a jeste ta funkce escapuje vuci stringu v php (ale, to prave delat nemusi, to se deje pri ukladani)
// "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)"
// "INSERT INTO CountryLanguage VALUES (?, \"jaz\\\"yk\", ?, ?)" // takhle bys to musel zapsat do sql prikazu primo, pokud php string zacnes dvojitymi uvozovkami
Editováno 15. února 13:22
 
Nahoru Odpovědět
15. února 13:20
Avatar
Odpovídá na Peter Mlich
Tomáš Dvořák:15. února 13:42

Právě, že jsem nic nevyčetl. Ten příkaz ještě vypadá nad tím takto. Doplňuje LIKE podle toho kolik je slov. Je to input pro hledání, takže se tam teoreticky dá zavléct škodlivý kód.

$nazev = $_GET["vstupni_input"];
$array_like = Explode(" ", $nazev);
$Podminka = null;
$pole_like = [];

for($i = 0; $i <= Count($array_like) - 1; $i++){
                $pole_like[] = "(nazev LIKE '%".$array_like[$i]."%' OR kod LIKE '%".$array_like[$i]."%' OR informace LIKE '%".$array_like[$i]."%' OR rozsirene_info LIKE '%".$array_like[$i]."%')";
}

$Podminka .= '  '.Implode(' AND ',$pole_like);

Takže těch prvků v poli může být hned několik podle toho kolik je tam slov. Jestliže to doplňuje znaky, tedy nějak escapuje, tak proč ten příkaz poté nefunguje skrz ten bind_param? Kdyby šlo jen o jen řetezěc nazev, kod, informace a rozsirene_info, tak to by šlo doplnit, ale vzhledem k tomu, že nevím kolik jich bude, tak mě nenapadá jak by to šlo...

Je nějaká bezpečná cesta jak odstranit z $Podminka školivý kód nebo jak jinak to provést?

 
Nahoru Odpovědět
15. února 13:42
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:15. února 16:03
$pole_like = [];

for($i = 0; $i <= Count($array_like) - 1; $i++){
                $pole_like[] = "(nazev LIKE ? OR kod LIKE ? OR informace LIKE ? OR rozsirene_info LIKE ?)";
}
$where = implode(' AND ', $pole_like);

$query = 'SELECT COUNT(*) FROM table WHERE $where AND zobraz="1"';
$stmt = mysqli_prepare($link, $query);
$i_end = Count($array_like);
for($i = 0; $i < $i_end ; $i++) // zasadne nepouzivej funkce do podminky pro cykly, pokud se hodnota nemeni
// to "<=" zase ma svuj vyznam, kdyz nechces prekrocit rozsah nejake promene, tak to si tam klidne nech
    {
    $value = $array_like[$i];
    // musis se zbavit vseho, co muze narusit vyraz pro like
    // ted s nejsem jisty, bud \znak (escapovat) nebo znakznak (zdvojit)
    $value = str_replace(array('\\', '_', '%'), array('\\\\', '\\_', '\\%'), $value);
    $value = '%'.$value.'%';
    $stmt->bind_param('ssss', $value, $value, $value, $value);
    }
Editováno 15. února 16:04
 
Nahoru Odpovědět
15. února 16:03
Avatar
Odpovídá na Peter Mlich
Tomáš Dvořák:17. února 11:21

To mi nefunguje, protože bind_param nemá stejný počet prvků jako ten dotaz.

Udělal jsem to následovně:

$array_like = Explode(" ", $Nazev);
$Podminka = null;
$nic_hledej_order = "";
$pole_like = [];
$i_end = Count($array_like);
$qArray = array();
$refs = array();
$type = array();
$string = null;

function arrayToRef(&$rawArray)
{
    $refArray = array();
    foreach($rawArray as $key => $value)
    {
        $refArray[$key] = &$rawArray[$key];
    }
    return $refArray;
}

for($i = 0; $i <= $i_end - 1; $i++){
    $pole_like[] = "(nazev LIKE ? OR kod LIKE ? OR informace LIKE ? OR rozsirene_info LIKE ?)";

    $value = $array_like[$i];
    $value = str_replace(array('\\', '_', '%'), array('\\\\', '\\\_', '\\\%'), $value);
    $value = '%'.$value.'%';
    $qArray[] = $pole_like[0];
    $b = $i * 4;

    for($a = $b; $a < 4+$b ; $a++){
        $string .= "s";
        $refs[$a] = $value;
    }
}

$query = 'SELECT COUNT(*) FROM table WHERE ';

$data = arrayToRef($refs);

$type[] = $string;
$result_params = array_merge($type,$data);
$query .= implode(' AND ', $qArray).' AND zobraz_eshop="1"';
$stmt = $mysqli->prepare($query);
call_user_func_array(array($stmt, 'bind_param'), $result_params);
$stmt->execute();
$stmt->bind_result($celkem);
$stmt->store_result();
$stmt->fetch();

musel jsem ted použít funkci call_user_fun­c_array. Je to ok? Výsledky mi to dává správné.

 
Nahoru Odpovědět
17. února 11:21
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17. února 13:55

Urcite to musi jit i nejak sikovneji. Ale mysqli nepouzivam, tak nevim. Dneska se pouziva pdo driver.

Toto bych resil jinak

//$query .= implode(' AND ', $qArray).' AND zobraz_eshop="1"';

$qArray[] = 'zobraz_eshop="1"';
$query .= implode(' AND ', $qArray);

Muze se totiz stat, ze qArray bude prazdny, implode tam da "" a pak mas sql dotaz

SELECT COUNT(*) FROM table WHERE    AND zobraz_eshop="1"

coz je nesmysl a vypise error :)

 
Nahoru Odpovědět
17. února 13:55
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17. února 14:03

google = mysqli bind dynamic params bind_​param
https://stackoverflow.com/…cally-in-php

if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+
    {
        $refs = array();
        foreach($arr as $key => $value)
            $array_of_param[$key] = &$arr[$key];
       call_user_func_array(array(&$stmt, 'bind_params'), $array_of_params);
     }

---
public function get_result($sql,$types = null,$params = null) {
    $stmt = $this->mysqli->prepare($sql);
    $stmt->bind_param($types, ...$params);
    if(!$stmt->execute()) return false;
    return $stmt->get_result();
}
$res = $output->get_result($sql, 'ss',array('1','Tk'));
-----
$stmt->bind_param(str_repeat("s", count($data)), ...$data);
-----
call_user_func_array(
    array($stmt, "bind_param"),
    array_merge(array(str_repeat("s", count($data))), $data));
----
$references_to_data = array();
foreach ($data as &$reference) { $references_to_data[] = &$reference; }
unset($reference);
call_user_func_array(
    array($stmt, "bind_param"),
    array_merge(array(str_repeat("s", count($data))), $references_to_data));
 
Nahoru Odpovědět
17. února 14:03
Avatar
Odpovídá na Peter Mlich
Tomáš Dvořák:17. února 14:04

Jasně, to se stát může, ale to je ošetřeno ještě před dotazem, takže se do této části hledání vůbec nedostane. Pokud je $_GET['input'] prázdný, tak to hodí hlášku.

 
Nahoru Odpovědět
17. února 14:04
Avatar
Tomáš Dvořák:17. února 14:07

K tomu PDO. Kdybych to přepsal, tak by se asi dalo to tam opakovat a lépe "pojmenovat" jednotlivé vstupy, dle příkladu.

// prepare sql and bind parameters
 $stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email)
 VALUES (:firstname, :lastname, :email)");
 $stmt->bindParam(':firstname', $firstname);
 $stmt->bindParam(':lastname', $lastname);
 $stmt->bindParam(':email', $email);

Mrknu se na to. PDO má výrazné výhody oproti klasice? má smysl, abych do toho hrabal?

 
Nahoru Odpovědět
17. února 14:07
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17. února 17:39

No, php7 myslim vypisuje, ze mysqli je deprecated, nepodporovane. Jinak o tom nic moc nevim. Jen mi bylo doporuceno pouzivat pdo pred X lety a nemam s tim problem. Ja mam napsane nejake vlastni sql class, ve ktere to pdo pouzivam. Vyuzivam escapovani pres vlastni funkce, odkazuji na pdo. S Bindem jsem se nikdy nezkamaradil, protoze jsem zvykly skladat sql dotaz jako text.

 
Nahoru Odpovědět
17. února 17:39
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 10.