Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [skrypt] Licznik odwiedzin OOP PHP z PDO
Forum PHP.pl > Inne > Oceny
gregherb
Na samym początku wypada się przywitać co też czynię, jako że jestem świeżo po rejestracji na forum. Mam też nadzieję, że nie umknął mi żaden punkt regulaminu i na starcie nie popełniłem jakiegoś "wykroczenia" zamieszczając pierwszy post.

A teraz do rzeczy:

Od dzisiaj studiuję rozszerzenie PDO. Napisałem prostą klasę licznika odwiedzin odpornego na odświeżanie strony. Dane licznika przechowywane są w bazie danych. Konstruktor klasy tworzy nową tabelę jeśli takowa nie istnieje. Więcej szczegółów w komentarzach do metod.

Proszę doświadczonych programistów o:
1. ocenę kodu;
2. wskazówki na co zwracać uwagę;
3. ocenę pod kątem bezpieczeństwa (czy takie wykorzystanie PDO utrudnia SQL Injection?)
4. co można zoptymalizować;
5. opinie czy takie wykorzystanie funkcji unset jest korzystne/dobre, czy też zbędne/złe?
6. Co sądzicie o PDO? Czy pod względem wygody/wydajności/bezpieczeństwa jest lepsze od innych metod? Co polecacie?

Każda konstruktywna krytyka jest mile widziana.

Oto kod:
  1. class Counter {
  2. private $db; //obiekt bazy danych
  3. private $counter; //zmienna zawierająca wartość licznika
  4.  
  5. //Funkcja inicjuje połączenie z bazą danych, wrazie potrzeby tworzy tabelę i ustawia wartość
  6. //licznika. Ponadto ustawia cookie o nazwie visited na wartość true i czas życia 3h.
  7. function __construct($dbName, $user, $password, $host="localhost", $dbType="mysql") {
  8. //w tym miejscu powinny znajdować się instrukcję obsługi pozostałych typów baz danych
  9. $dsn = "$dbType:dbname=$dbName;host=$host;";
  10. try {
  11. $this->db = new PDO($dsn, $user, $password);
  12. } catch (PDOException $e) {
  13. echo "Nieudana próba połączenia: " . $e->getMessage();
  14. }
  15.  
  16. $checkIfTableExists = $this->db->prepare("SELECT * FROM licznik");
  17. if(!$checkIfTableExists->execute()) {
  18. $create = $this->db->prepare("CREATE TABLE licznik(Hits INT)");
  19. $create->execute();
  20. unSet($create);
  21.  
  22. $insert = $this->db->prepare("INSERT INTO licznik VALUES(0)");
  23. $insert->execute();
  24. unSet($insert);
  25. }
  26. unSet($checkIfTableExists);
  27.  
  28. $select = $this->db->prepare("SELECT Hits FROM licznik");
  29. $select->execute();
  30.  
  31. $result = $select->fetch(PDO::FETCH_ASSOC);
  32. unSet($select);
  33.  
  34. if (isSet($_COOKIE['visited'])) {
  35. $this->counter = $result['Hits'];
  36. } else {
  37. $this->counter = $result['Hits'] + 1;
  38.  
  39. $update = $this->db->prepare("UPDATE licznik SET Hits=$this->counter");
  40. $update->execute();
  41. unSet($update);
  42.  
  43. setCookie("visited", "true", time()+3600*3);
  44. }
  45. unSet($result);
  46. }
  47.  
  48. //Funkcja umożliwia ustawienie licznia na dowolną wartość.
  49. function setCounter($val) {
  50. $this->counter = $val;
  51. $update = $this->db->prepare("UPDATE licznik SET Hits=$val");
  52. $update->execute();
  53. unSet($update);
  54. }
  55.  
  56. //Funckja zwraca wartość licznika.
  57. function getCounter() {
  58. return $this->counter;
  59. }
  60.  
  61. //Funkcja ustawia wartość licznika na 0.
  62. function resetCounter() {
  63. $reset = $this->db->prepare("UPDATE licznik SET Hits=0");
  64. $reset->execute();
  65. $this->counter = 0;
  66. unSet($reset);
  67. }
  68.  
  69. //Destruktor klasy Counter.
  70. function __destruct() {
  71. unSet($this->db);
  72. unSet($this->counter);
  73. }
  74. }


Sposób użycia:
  1. $licznik = new Counter("nazwa_bazy", "user", "hasło", "host", "typ_bazy");
  2. echo "Liczba odwiedzin: " . $licznik->getCounter();
  3. unset($licznik);
-=Peter=-
Po co korzystasz z unset? Php ma gc, więc jeśli zmienna wyjdzie poza zakres to pamięć zostanie zwolniona - niepotrzebne zaciemnianie kodu.

Korzystasz z prepared statements, lecz w ogóle nie wykorzystujesz ich zalet. Masz na krzyż 4 zapytania, które różnią się tylko parametrem, przygotuj sobie 4 obiekty prepared statement dla każdego z zapytań i te obiekty wykorzystuj do wykonywania zapytań, a nie twórz ich non stop za każdym razem. Będziesz musiał wykorzystać placeholdery (np. w postaci znaku zapytania), a wartosci przekazywać do metody execute - tak też powinno się robić nawet gdy docelowo obiekt PDOStatement ma wykonać tylko jedno zapytanie.

Klasa counter nie powinna nawiązywać połączenia z bazą danych, a dostać już gotowe połączenie.
gregherb
Dzięki za spostrzeżenia!

Cytat(-=Peter=- @ 28.05.2011, 23:12:30 ) *
(..) przygotuj sobie 4 obiekty prepared statement dla każdego z zapytań i te obiekty wykorzystuj do wykonywania zapytań


Czyli w tym konkretnym przypadku powinienem przygotować po jednym obiekcie dla zapytań select, update, insert, create? Czy tak?

Cytat(-=Peter=- @ 28.05.2011, 23:12:30 ) *
Klasa counter nie powinna nawiązywać połączenia z bazą danych, a dostać już gotowe połączenie.


W tym miejscu nie byłem pewien, czy Counter powinien sam łączyć się z bazą. Dzięki za wyjaśnienie! Czy powinienem przekazywać do niego gotowe połączenie w postaci obiektu PDO?


Jeszcze raz dzięki za odpowiedź i miłej niedzieli.
Zyx
Chodzi o zapytania UPDATE:

Kod
$update = $this->db->prepare("UPDATE licznik SET Hits=$val");
$update->execute();


Po co Ci tutaj prepare(), kiedy dane wklejasz bezpośrednio w zapytanie?
cepa
blad na bledzie tongue.gif

1 - dlaczego tworzysz nowe polaczenie w konstruktorze? nie lepiej wystawic set/get connection aby mozna bylo to zlaczyc z innym kodem?
2 - calkowity brak tranzakcji
3 - tworzenie tabeli w locie!?
4 - duplikacje kodu (brak DRY)
5 - nie robisz lockow na tabelach

ogolnie, licznik nadaje sie jedynie do prostych stronek o niklej ogladalnosci, przy wiekszym ruchu bedzie waskim gardlem a przy duzej konkurencyjnosci bedzie dawal zle wyniki
Fifi209
Nie wykorzystujesz tego co oferuje PDO, a mam tutaj na myśli dokładnie to co pisał Zyx.
Destruktor możesz wywalić.
resetCounter mógłby najwyżej wywoływać $this->setCounter(0), nie widzę sensu powielania kodu.
getCounter nie zwróci odpowiedniej wartości, jeżeli w czasie wykonywania skryptu kilku innych w tym samym czasie wejdzie na stronie i każdy z nich przesunie licznik o +1 ;]
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.