Autor Zpráva
Jcas
Profil *
Žádost na moderátory fóra. Šlo by udělat vlákno, něco jako "Některé časteji řešené dotazy pro MySQL - FAQ", kde nebude spousta povídání, ale budou pouze jasná stručná doporučení, či příklady, či ukázky. + samozřejmě drobný komentář.
V podstě jde o to, že povídání se vymaže a případné chyby se upravý a bude to jak jednoduchý návod, který upozorní na chyby, které by mohl uživatel dělat.

Přechod z mysql na mysqli - OOP styl.

V první fázi musím podotknout, že základ jsem čerpal z Fisirova článku a doporučuji si napřed tento článek přečíst.
-----------------------------------------------------------

Připojení k Databázi



     define('DBHOST', 'localhost');            
     define('DBNAME', 'databáze');                
     define('DBUSER', 'uživatel');          
     define('DBPASS', 'heslo');      
      @mysql_connect(DBHOST, DBUSER, DBPASS) or die ( my_mysql_error() );
    mysql_select_db(DBNAME);      
    
    //nahradíme za:
     $mysqli = new mysqli("localhost", "uživatel", "heslo", "databáze"); 
-----------------------------------------------------------

Kontrola Úspěšného připojení k databázi



// Dříve jsem používal konstrukci or die ( my_mysql_error() ); Nyní ji nahradím přímo funkcí pro kontrolu připojení mysqli_connect_errno()

     /* check connection */
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\n", mysqli_connect_error());
    exit();
}
-----------------------------------------------------------

Nastavení kódování



    mysql_query("SET NAMES UTF-8");
    // nahradíme
    /* change character set to utf8 */
if (!$mysqli->set_charset("utf8")) {
    printf("Error loading character set utf8: %s\n", $mysqli->error);
}
-----------------------------------------------------------

Konstrukce if() {}



Jak píše Joker - Mysqli Result
Lepší je vždy ošetřit chyby.
Lze říct, že platí: Jakékoliv kontaktování databází skončí buď vrácením výsledku (result), nebo vrácením FALSE. Toto FALSE podmínka if() odchytí a můžu odkázat na dannou chybu, která vznikla. Je to tedy svým způsobem náhrada metody or die().

Ale pozor, může to být zavádějící. Jak napíši hned v následujícím odstavci.
-----------------------------------------------------------

Dotaz na DB


jednoduché mysql_query() nahradíme za mysqli->query()

SELECT
    $query = "SELECT * FROM `USER` WHERE `nick` = 'Jcas'"; 
    if ($mysqli->query($query)) {
        .....výpis chybové hlášky......    // prosím doplnit    (printf nerozumím-zatím jsem jen kopíroval)                        
        }
- Jestliže existuje záznam vyhovující podmínce WHERE `nick` = 'Jcas', tak bude vybrán.
- Jestliže neexistuje, tak budou vráceny prázdné hodnoty.

Jak jsem psal, že buď vrátí výsledek, nebo FALSE a že to může být zavádějící, tak toto je ten případ.
Pokud si někdo myslí, že když v DB neexistuje záznam vyhovující podmínce WHERE `nick` = 'Jcas', že dotaz vrátí FALSE, tak se mýlí. Může za to tato věta z php manuálu:

Returns FALSE on failure. For successful SELECT, SHOW, DESCRIBE or EXPLAIN queries mysqli_query() will return a mysqli_result object. For other successful queries mysqli_query() will return TRUE.

Podmínka if() ošetří chyby, které by mohli vzniknout při výběr dat z DB, ale pokud záznam neexistuje, tak to není chyba při výběru dat. data se vyberou, ale budou mít hodnotu NULL.

    $query = "SELECT * FROM `USER` WHERE `nick` = 'Jcas'"; 
    if ($mysqli->query($query)) {
        var_dump($mysqli->query($query));                    
        }
nám vrátí object:
object(mysqli_result)#3 (5) { ["current_field"]=> int(0) ["field_count"]=> int(6) ["lengths"]=> NULL ["num_rows"]=> int(0) ["type"]=> int(0) }

Nyní opět poprosím o dopnění informací.
#3 (5) - nemám tušní co znamená
current_field - také nevím
field_count - by měl být počet sloupců v tabulce
lengths - nevím, když při úspšném vybrání jednoho řádku je stále NULL
num_rows - počet vybraných řádků - záznamů v tabulce
type - nevím, asi bude naplněno, pokud vyberu jedinou hodnotu z jediné buňky - vyplní typ (string, int atd.)

Nyní poprosím zkušené, aby doplnili, co nám bylo vráceno.
-----------------------------------------------------------

Kontrola existence záznamu



jak vydíte, pouze kontrola SELECT nestačí, protože nám query vrátí object, i když žádný záznam v tabulce neexistuje.
Způsobů jak zkontrolovat, zda záznam existuje, nebo ne je více.

Zde Juriad Mysqli Result doporučuje vytáhnout všechna data o uživateli a pak je podrobit kontrole. (Protože stejně většinou potřebujeme víc, než jen ověřit existenci záznamu).

    $query = "SELECT * FROM `USER` WHERE `nick` = 'Jcas'"; 
    if ($result = $mysqli->query($query)) {
 
        /* fetch associative array */
            if ($row = $result->fetch_assoc()) {
                if($row['nick']=='Jcas') {echo 'zaznam existuje';}
                }

Já zvolil metodu ověření počtu řádků.
$result = mysql_query("SELECT COUNT(*) FROM `uzivatele`...........
           // jsem nahradil za:
           $query = "SELECT * FROM `USER` WHERE `nick` = '" . $mysqli->real_escape_string($_POST['nick']) . "'";
        if ($result = $mysqli->query($query)) {     
            if($row = $result->num_rows) {
                echo 'záznam existuje';
                }
            }

Nyní už skutčně if($row = $result->num_rows) vrátí 1, či více při existenci jednoho či více záznamů a nebo vrátí 0, pokud záznam neexistuje.

-----------------------------------------------------------

Zrádné bind_param()



Objevil jsem Bug mezi židlí a klávesnicí, které způsobí vyhazování chyby "Fatal error: Cannot pass parameter 2 by reference in...."
Může za to nedefinovaná proměnná. bind_param() nesnese předávání samotných hodnot, ale potřebuje proměnnou. Tedy:

mysql_query("INSERT into `uzivatele` VALUES('', '{$_POST['user']}', 0)") ;

Dřívější metoda použití prázdných apostrofů pro `id`, které se v DB vyplní automaticky nyní skončí fatální chybou.
Stejně tak skončí i snaha předat 0 do slupce typu int.

Máme dvě možnosti. buď definujeme proměnné dopředu a předáme pouze proměnné, nebo definujeme proměnné přímo v dotazu.

    $statement = $mysqli->prepare("INSERT INTO `USER` VALUES (?,?,?,?,?,?)");
    if(!$statement) { echo("Příprava SQL dotazu selhala: ".$mysqli->error); }
    $statement->bind_param("isssis", $a=0, $_POST['nick'], $_POST['email'], crypt($_POST['heslo']), $e=0, vrat_nahodny_kod());    
    $statement->execute();
    $statement->close(); 

$a=0 jsem použil pro `id`, které má v DB unikátní hodnotu a nesmí být NULL a proto se uloží jiná hodnota např.4.
$e=0 jsem použil pro `sloupec`, který je typu boolean a má hodnoty pouze 1/0

ps. Co se týče toho `id`, tak nevím jak se to dělá a nevím, jestli toto je správný postup. Nicméně toto mne pustilo přes vyskakující chybu.
-----------------------------------------------------------
Pokračování příště, až narazím na další nejasnosti a chyby.
Ta žádost na moderátory webu je, aby z tohoto vlákna vzali to co stojí za to, udělali vlákno návodu-příkladů a ukázek.
Z toho vymazali ty nesmyslné kecy a doplnili, čemu já nerozumím a potom toto vlákno smazali, aby nezabíralo místo.

Vlákna "nejčastější dotazy, problémy atd jsou super. Kdybych to psal pro sebe, tak bych uvítal diskusi. Ale když se chce člověk něco dozvědět, tak je hrůza pročítat 20 diskusí, a v každé 20 příspěvků aby se dozvěděl jednu jedinou drobnost.
Tento text jsem nepsal pro sebe a pro odpovědi, které sám neznám. Tento text jsem psal pro ostatní, aby neopakovali moje chyby. Proto prosím moderátory fóry, aby vytáhli to důležité, použili to pro rady ostatním a vlákno smazali. Děkuji
Medvídek
Profil
Nevím, jestli je nutné kvůli tomu dělat speciální vlákno. Kdo zvládl napsat kód pomocí mysql, nebude pro něj mysqli žádný problém. Oficiální průvodce je dobře zpracovaný.

Jcas:
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

Raději bych používal exceptiony, "echování" jakýchkoliv chyb přímo bych nedoporučoval.

if ($mysqli->connect_errno) {
    throw new CustomException($mysqli->connect_error);
}
Jcas
Profil *
Medvídek:
Vždy jsem si cenil tvých rad, ovšem řekl bych, že ty mluvíš jazykem odborníků. (viz dříve).
Já mluvím jazykem obyčejných a totálně hloupých amatérů (kteří většinou mají problém s angl. jazykem), kteří většinou mají omezené znalosti. Vždy jim nějaká souvislost uniká. Věřím, že takových jako já je více a přesto, že jsem mysql už několikrát použll, tak jsem opět narazil na chyby. A proto bych rád na tyto chyby upozornil ostatní. Třeba tu chybu bind_param() jsem řešil asi 2 hod. A stejně jsem ji musel vyřešit přes zahraniční weby.


Každopádně děkuji za tvou radu exeptionu - jdu se pokusit o tom zjistit více.


osobně nesnáším vlákna stylu - mám někde chybu- nefunguje to
A hned na to další přízpěvěk - vyřešeno.
Neukáže se kde je chyba, co jak nefunguje a jak to řešit atd. Já dnes odhalil několik chyb a pral jsem se s nimi. Když tu chybu dokážu udělat já, tak ji dokáže udělat i někdo jiný. Proto věnuji čas tomu abych to napsal a abych třeba jinému ušetřil trápení.

O mě nejde. Mám problém - zeptám se a vy mi odpovíte. Ale co ostatní? Je dost vystvětlení na webu? Je dost článků srozumitelnej? Je dost návodů použitelnejch?
A zvlášť když nyní samotní uživatelé fora upozorňují na budoucí zánik mysql?
Jcas
Profil *
Oprava:


Zde Juriad Mysqli Result doporučuje vytáhnout všechna data o uživateli. (Protože stejně většinou potřebujeme víc, než jen ověřit existenci záznamu).

 $query = "SELECT * FROM `USER` WHERE `nick` = 'Jcas'"; 
 if ($result = $mysqli->query($query)) {
 
  /* fetch associative array */
  if ($row = $result->fetch_assoc()) {  //pokud záznam neexistuje, vrátí FALSE
       echo 'zaznam existuje';
        }
Jan Tvrdík
Profil
Jcas:
Čas na korekce =) Máš špatně zvýrazňování kódu, použij místo [pre] [prephp]

Připojení k Databázi
Není důvod u mysql používat konstanty a u mysqli nikoliv. Lépe všude bez konstant, protože dávat přihlašovací údaje do konstant je blbost. Navíc používáš nějakou vlastní funkci my_mysql_error

@mysql_connect('localhost', 'uživatel', 'heslo') or die mysql_error();
mysql_select_db('databáze');

// nahradíme za
$mysqli = new mysqli('localhost', 'uživatel', 'heslo', 'databáze');

Kontrola Úspěšného připojení k databázi
Nahradil jsi špatné řešení u mysql za špatné řešení u mysqli. Chybová hláška není určena pro koncového uživatele aplikace a tedy ji nesmíš jen tak vypsat. Jako možné řešení je použít např. trigger_error, který vypíše chybu jen, když je zapnuté display_errors. Alternativně si můžeš tu podmínku implementovat ručně. Ještě jsem si všiml, že používáš printf, což je funkce, kterou znají velmi dobře všichni, co dělali někdy něco v C, ale PHPčkaři si většinou vystačí s echo. Navíc používáš procedurální API, místo objektového.

if ($mysqli->connect_error) {
    if (ini_get('display_errors') === '1') {
        echo "Connect failed: $mysqli->connect_error\n";
    }
    exit();
}

Nastavení kódování
To samé, chybová hláška nemá být viditelné pro běžné uživatele.

Dotaz na DB
Musíš si nějak uložit výsledek, jinak ses ptal zbytečně.
$result = $mysqli->query("SELECT * FROM `USER` WHERE `nick` = 'Jcas'");
if ($result === false) {
    if (ini_get('display_errors') === '1') {
        echo "DB query failed: $mysqli->error\n";
    }
    exit();
}

Další chyby nemám sílu opravovat.

Kontrola existence záznamu
Podmínka if($row = $result->num_rows) je nesmysl, použij normálně if($result->num_rows > 0).

Zrádné bind_param()
Špatně ošetřená chyba, při selhání prepare pořád pokračuje ve zpracování.
Špatně použitá funkce crypt. Použít ji správně vůbec není intuitivní, proto vznikly funkce jako password_hash. Viz také např. Šifrování/hashování hesel: algoritmus blowfish, nové funkce
Jcas
Profil *
Jan Tvrdík:
Děkuji - a to je přesně to, o čem mluvím(t žádost na začátku). Moje bláboly smazat a nahradit tím správným.
MikeO
Profil
Tak po instalaci LAMP serveru (Debian Stretch) na nový hw a přesunu několika databází mysql došlo i na tehnle pro mne sw oříšek v podobě PHP7.0. Jde pouze více méně o lokální server s databázemi pro určitý okruh uživatelů. Vše běželo léta na PHP5. Do mysql bohužel moc nevidím. Nicméně jsem nějaké postupy přechodu z mysql na mysqli vyzkoušel, ale neúspěšně v tomhle případě. Vložím úplně nejjednodušší skript pouze k výpisu hodnot z db se kterým si nevím rady a nejde mi na něj úspěšně aplikovat přechod na mysqli. Budu rád za jakékoliv upřesnění a nasměrování, které by mi pomohly skripty oživit a přepsat do funkčního stavu.

<?php
$PZS = 100; // pocet záznamů na stranu
$str = 1; // aktuální strana (počítáno od 1)
$message = "Error"; // chybová zpráva

if (isset($_GET['str']) &&
$_GET['str']> 0) { // z parametrů URL vybere aktuální stránku
$str = $_GET['str'];
}

// připojení k databázi
$sql = mysql_connect("localhost", "user", "pwd") or die($message);
@mysql_select_db(databaze);
mysql_query ("SET NAMES 'utf8_czech_ci'");
// výběr počtu prvků v tabulce
$resultCount = mysql_query("SELECT COUNT(0) as COUNT_TABLE FROM tabulka");
// uložení počtu záznamů
$pocetZaznamu = mysql_result($resultCount, 0);

// SQL funkce LIMIT(START, COUNTS)
// výpočet STARTU
$start = ($str - 1) * $PZS;
// výběr položek
$result = "SELECT column1, column2, column3, column4 FROM tabulka ORDER BY column1 LIMIT ".$start.",".$PZS;
// výpis výsledků
$navrat=mysql_db_query("databaze", $result, $sql);
echo("<table width=\"100%\" cellspacing=\"1\" cellspadding=\"1\" border=\"0\" bordercolor=\"red\">");
for ($i=0;$i<mysql_num_fields($navrat); $i++){
}
while (list($column1, $column2, $column3, $column4) = mysql_fetch_row($navrat)){
echo("<tr bgcolor=\"lightblue\" onmouseover=\"this.style.backgroundColor='orange'\" onmouseout=\"this.style.backgroundColor='lightblue'\">
<td style=\"color: red; font-size: 12px; font-weight: bold\">$column1</td>
<td style=\"color: green; font-size:11px\">$column2</td>
<td style=\"color: green; font-size:11px\">$column3</td>
<td style=\"color: green; font-size:11px\">$column4</td>
</tr>");
}
echo("</table>");
//
// Jednoduché listování mezi stranami
//
echo "<div style='float: right; clear: both; font-size:12px;'>".
"<a href='?str=" .($str> 1 ? $str - 1 : "1"). "'>&laquo;</a>&nbsp";

// počet stránek
$pocet = (($pocetZaznamu % $PZS)> 0) ? (int)($pocetZaznamu / $PZS) + 1 :
$pocetZaznamu / $PZS;

// výpis odkazů na stránky
for ($i = 1; $i <= $pocet; $i++) {
if ($i != $str) {
echo "<a href='?str=".$i."'>".$i."</a>&nbsp;";
} else {  // aktuální strana
echo "<a href='?str=".$i."'><b>".$i."</b></a>&nbsp;";
}
}
echo "<a href='?str=" .(($str <$pocet) ? $str + 1 : $pocet) ."'>&raquo;</a>"
.
"</div>";
$num_rows = mysql_num_rows ($navrat);
echo "<div style='color: blue; font-size:11px; font-weight: bold;'>". "Zobrazuji <font style='color: green; font-size: 14px;'>$num_rows</font> zaznamu na\n" ;
echo $akt="<font style='color: green;'>$str</font> strance ze <font style='color: green;'>$pocet</font>";
echo "<div style='color: blue; font-size:11px; font-weight: bold;'>". "Bylo nalezeno celkem <font style='color: red; font-size:14px;'>$pocetZaznamu</font> zaznamu\n" ;
?>

Možná zbytečně jsem sem dal celý skipt. Ale budu rád za každou radu a tip, jak provést úpravu a nahrazení. Když jsem provedl úpravu, kdy jsem se nakonec k db připojil, tak výpis i tak nebyl žádný, proto raději celý skript.
Kajman
Profil
MikeO:
Pokud Vás to zajímá a chcete to prozkoumat, tak ve svém příspěvku vždy klikněte na funkci mysql_... a v manuálu je v informaci o odstranění její doporučená náhrada v mysqli knihovně. Zda použiteje procedurální nebo objektové volání je na Vás.

Pokud to nechcete řešit, můžete si přidat náhradu odstraněných funkcí. Něco jako tento odkazovaný projekt.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0