Szukaj na tym blogu

Postaram się przekazać swoje doświadczenia z PHP, oraz innych dziwnych rzeczach, typu MySQL, DB2. Czyli to co umiem. Blogoczytelników o słabych nerwach proszę o nieczytanie tego blogu. Dlaczego? Jeszcze nie wiem. W razie pytań z dziedziny PHP proszę pytać, jest cień możliwości że akurat to wiem. ;)

poniedziałek, 12 kwietnia 2010

Funkcja mysqli

Oto najprostsza metoda podłączenie się do MySQL'a i zadanie pytania i otrzymanie stosownych odpowiedzi.


<?php
$link = mysql_connect("localhost", "franek", "franek12");
mysql_select_db("franek");
$wynik = mysql_query("SELECT * FROM tabela WHERE id={$_GET['id']}");
$rekord = mysql_fetch_array($wynik, MYSQL_NUM);
echo "Wynik {$wynik[0]}";
mysql_close($link);
?>


Ten wzór pokutuje od zawsze. Co jest w mim źle? Szybciej co jest w nim dobrze. Działać będzie działać, ale czemu można tu się włamać.
Pierwszym najważniejszym błędem jest id={$_GET['id']}.
Zawsze można skazić dane tak, żeby naszego spodziewanego ID od 0 do nieskończoności jest wrzucenie czegoś co wywali nam bazę w kosmos, albo i jeszcze dalej. Druga rzecz, nigdy nie nazywaj zmiennej tak jak nazwa w bazie. Po co ułatwiać innym włamywanie się? Nigdy nie używaj * w zapytaniach. Załóżmy że zmieni Ci się liczba kolumn i co wtedy?

Teraz mnie więcej wiadomo co źle, teraz weźmy się jak to zrobić dobrze. O ile dobrze istnieje. Powiedzmy poprawnie.

Zacznijmy od podłączenia do bazy danych.


<?php
$link = @mysqli_connect('localhost', 'użytkownik', 'hasło', 'nazwa_bazy_danych');

if (!$link) {
die('Błąd połączenia: ' . mysqli_connect_errno());
}
?>



Dodam tu swoje 5 groszy, żeby użytkownik był inny niż nazwa bazy. Pełnią szczęścia był by jeden użytkownik z okrojonymi prawami dla serwisu, a drugi do administrowania bazą.
Szczytem finezji było przekierowanie na inną stronę pod tytułem "Serwis się popsuł", a błąd zapisać w logach Apache, albo gdzie indziej. Byle nie w bazie, która jak wiadomo z jakiś przyczyn jest niedostępna. Można wysyłać błąd na komórkę, albo e-mailem. To już musi być bardziej zaawansowana procedura, zwłaszcza przy serwisie często odwiedzanym. Wtedy przy każdym kliknięciu administrator dostanie SMS, albo e-mail. W efekcie skrzynka zapcha się mu po 20 minutach, a telefon lub jego właściciel zwariuje o minute później.

Przy mysqli_connect jest umieszczony @. Jeżeli w php.ini jest ustawione wyświetlanie błędów w skrypcie, @ zapobiega temu.

Bardziej elegancką wersją tego samego jest zmajstrowanie sobie klasy. Brzmi bardzo groźnie, ale nie taki diabeł straszny...


<?php
$klasa = @new mysqli('localhost', 'użytkownik', 'hasło', 'nazwa_bazy_danych');

if ($klasa->connect_error) {
die('Connect Error: ' . $klasa->connect_error);
}
?>


Na pierwszy rzut oka wygląda tak samo. Nie do końca.
Dla tych co nie wiedzą co to jest klasa szybkie objaśnienie. Klasa jest zbiorem funkcji tworząca obiekt z własnymi zmiennymi, które są niewidoczne na zewnątrz. Nie ma problemu kiedy się użyje tej samej zmiennej w różnych klasach.

To co z tym obiektem dalej? W zasadzie można zrobić jakiegoś SELECT'a w bazie, wyglądać będzie to tak:



<?php
$wynik = FALSE;
$objekt = FALSE;

$baza = new mysqli("localhost", "użytkownik", "hasło", "baza");
if (mysqli_connect_errno()) die("Błąd bazy.");
$baza->set_charset('utf8');
$typ_pracownika = $baza->real_escape_string($_GET['kto']);
$pytanie = "SELECT `imie`, `nazwisko` FROM `pracownicy` WHERE `typ`='{$typ_pracownika}'";

if ($wynik == $baza->query($pytanie)) {
while ($objekt = $wynik->fetch_object()) {
printf ("Imię: %s nazwisko: %s <br>\n", $obj->imie, $obj->nazwisko);
}
$pytanie->close();
}
$pytanie->close();
?>



$wynik i $obiekt ma specjalne na początku wpisane FALSE, w razie jak by ktoś kombinował z przemyceniem danych.
set_charset jest tylko i wyłącznie po to, co by klient z bazą gadał UTF-8, a nie LATIN1. Po kilku insertach z wyrazami "gżegżółka" można ciut zblednąć widząc w PHPMySQLAdmin'ie krzaczki w miejscu polskich liter.
Nie używam w zapytaniu *, żeby panować nad pytaniem. Ktoś zmieni nazwę kolumny i pół roku szukania i narzekania "czemu nie wyświetla się! Działało jeszcze wczoraj!" tak będzie błąd "nieznana nazwa".
Porada dla początkujących w MySQL. Wszystkie nazwy kolumn aliasów tabel baz danych powinny być w ` `, a wartości w ' '. Po pierwsze czytelniejszy SQL, po drugie jest jasność dla interpretera (na posty typu bez tego też działa, nie odpisuje).
real_escape_string jest elegancką funkcją wstawiającą m.i. / przed ', co by nam ktoś nie skaził danych.Zawsze można powiedzieć że w niektórych MySQL ta funkcja ma luki. W sumie to fakt, nigdy nie wiadomo na jakim będzie to serwerze działało i jaką wersją bazy. Może wtedy wykorzystać STR_REPLACE, wpakować tablice co na co ma wymienić... albo użyć coś bardziej pokrętnego, czyli PREPARE STATEMENT. A oto i on!


<?php
$baza = new mysqli("localhost", "użytkownik", "hasło", "baza");
if (mysqli_connect_errno()) die("Błąd bazy.");
$baza->set_charset('utf8');
$baza = $db->prepare("SELECT `imie`, `nazwisko` FROM `ludzie` WHERE `ksywa` = ? LIMIT 1");
$baza->bind_param('s', $_GET['nick']);
$baza->execute();
$baza->bind_result($f_name, $s_name);
$baza->fetch();
$baza->close();
?>



To jest przykład wyrwany z jakiegoś dawnego wiekopomnego dzieła, jak mnie jeszcze źli ludzie nie terroryzowali Java.
W SQL tam gdzie jest pierwszy znak zapytania (w tym przypadku i ostatni) będziemy wstawiali zmienną. Czemu nie w ''? Nie potrzeba. bind_param podstawia elegancko coś wysłane do skryptu metodą GET, a s sygnalizuje że to jest STRING. Jak wstawimy I będzie INTEGER. jeszcze jest d jak double i bjak BLOB. Duży binarny obiekt (np. kompromitujące kogoś zdjęcie). Jak ktoś ma dwa znaki zapytania w zapytaniu lub więcej musi w pierwszym parametrze nawstawiać literki co to za zwierz, a dalej po przecinku w odpowiedniej kolejności powpisywać wartości. Na przykład:



$baza->bind_param('iss', $id, $imie, $nazwisko);



I jest szczęśliwy że działa.

Execute wiadomo.

bind_result wrzuca w poszczególne zmienne wartości z zapytania.

fetch przechwytuje. W tym przypadku spodziewamy się jednego osobnika. Po pierwsze NICK który teoretycznie powinien być unikalny, po drugie żeby baza za nas nie musiała kombinować jak nie ma klauzuli unikalności z jakiś przyczyn, jak znajdzie jednego delikwenta, niech dalej się nie męczy.
Jeżeli jednak chcemy mieć więcej rekordów niż jeden umieszczamy fetch w pętli.

Teraz można już coś z bazą robić. Jest tam parę zgrzytów i można by było zrobić lepiej, ale ktoś może mnie tez poprawiać, w napadzie szału wyśmiać gdzieś na blogu.

PS. Ci co szukają w wyszukiwarce przykładów do CV, albo są po rozmowie kwalifikacyjnej i muszą teraz błysnąć intelektem w jakimś przykładzie przez siebie stworzonym mam ostrzeżenie. Zmieniajcie choć nazwy zmiennych. Przyszły pracodawca na pewno to znajdzie w w sieci, albo przypadkowo zna te przykłady od dawna. ;)

poniedziałek, 5 kwietnia 2010

Funkcja ECHO

Ostatnio odkryłem funkcje ECHO. Działa rewelacyjnie. Piszę:

echo "Ala ma psa";
echo 'Ala ma kota';

I tu się zaczynają schody, wyższość świąt Bożego Narodzenia, nad świętami Wielkiej nocy. W " " działa wolniej i nie można używać, za to w ' ' nie można używać zmiennych takich jak

echo "Ala ma {$ilosc} kotów";

Za to używając pojedynczych trzeba używać składni:

echo 'Ala ma '.$ilosc.' kotów';

Oczywiście wiadoma sprawa, w cudzysłowach jest zaledwie miliard wolniejszy niż w apostrofach.

Dobra, dość tych mitów.
W cudzysłowach faktycznie działa wolniej.  Problem w tym, że różnica jest w błędzie statystycznym i nie ma czym se głowy zawracać. Jedna ważna rzecz, trzeba zmienne pisać w klamrach. Wiem że działa bez, ale czasem zdarzają się dziwne rzeczy, zwłaszcza jak używa się tablic do wyświetlenia.

Jest jeszcze jedna zdobycz techniki:

echo <<<EOF
Ala ma {$ilosc} kotów.
Ola "miała" {$psy['ola']} psów.
echo "to mój przykład z PHP";
EOF;

Działa w sposób banalny, widzi <<<, po tym jest jakiś ciąg znaczków. W tym przypadku EOF. Jeżeli ktoś mi zada pytanie dlaczego EOF (End of File), to powiem że nie wiem. Zamiast EOF można użyć dowolny ciąg znaków. Chodzi o to, żeby interpreter wiedział kiedy zakończyć wyświetlanie. Dobrze może być:

echo <<<NIENAWIDZE_KACZEK
Ala ma {$ilosc} kotów.
Ola "miała" {$psy['ola']} psów.
echo "to mój przykład z PHP";
NIENAWIDZE_KACZEK;

Ważne żeby na początku i na końcu będzie ten sam ciąg. W środku może być dokładnie wszystko. No może z wyjątkiem równań różniczkowych.

Kiedy robi się aplikację, w której liczy się każdy takt zegara (coś, co jest znane tylko na demo scenie), można wydziwiać która metoda jest lepsza.
W tej chwili jest ważne co jest wygodniejsze w tej chwili w danym miejscu.