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.

wtorek, 30 marca 2010

Pierwszy tekst

Jak domyślam się iż

<?php
echo "Witaj świecie";
?>

każdy zna. Jeżeli nie, ten blog nie jest dla niego.
Zawsze chciałem zadać zadanie, żeby tę jedną linie napisać tak, żeby maksymalnie rozbudować kod.

Wiem, szaleństwo. Niestety jak patrze w różne kody czasem mam wrażenie że tę tendencje ma większość programistów.  Może dlatego postanowiłem wziąć brzytwę Ockhama i wycinać co jest zbędne i niepotrzebne.

Często popełnianą herezją jest

$zmienna = $_POST['zmienna'];
echo $zmienna;

jak by nie można od razu

echo $_POST['zmienna'];

Obie metody są niebezpieczne, zawsze znajdzie się jakiś hakier i wyśle postem:

<div style="position: absolute; background-color: red; color: white; border: 1px black solid;">
shakwał tą strone<br>
pytek frytek
</div>

Po pierwsze zcrackował, po drugie drugie takich spławia się punkcją

echo htmlentities($_POST['zmienna']);

Amen.

Nie jest to eleganckie rozwiązanie, ale zapobiega wrzucaniom jakiś tagów, złośliwych JS, oraz innego dziadostwa.
Istotą rzeczy jest, żeby nie wierzyć poprawności danych przychodzących na serwer. Zawsze znajdzie się jakiś gieniuś, albo inna tragedia i będzie próbowało przemycić skażone dane, albo "świadomy użytkownik" dorwał się do komputera.

Oczywiście przykłady były banalne, w rzeczywistości były bardziej zawiłe, ale szkielet był mniej więcej taki jak w tych prościutkich przykładach. Nie echo z post, tylko z bazy, do której wrzucił inny skrypt nie sprawdzając kontentu.

To na tyle w dziedzinie mądrzenia się. Jak spodoba się, to będę pisał dalej, jeżeli nie, to zobaczę. ;)