Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Destruktor wykonuje się za wcześnie.
Forum PHP.pl > Forum > PHP > Object-oriented programming
kazaa9
Witam.

Mam dziwny problem z destruktorami i nie za bardzo wiem jak go rozwiązać. Przeglądałem trochę forum i manuala, ale nic w podobnym stylu nie było.

Oto opis mojego problemu: mam prosty systemik, w którym mam m. in. klasy DB (obsługa bazy danych, śle zapytania i przechowuje informacje o ewentualnych błędach) oraz Session (jak sama nazwa mówi - do obsługi sesji).

W klasie Session w destruktorze wywołuję metodę, która kończy mi daną sesję, czyli przy pomocy klasy DB robi UPDATE danych w bazie. Działa, jednak...no właśnie. Gdybym w klasie DB miał destruktor, w którym kończyłbym połączenie z bazą danych, to to zapytanie już wygeneruje błąd, czyli tak jakbym był rozłączony. Dodam jeszcze, że później pobieram sobie pewną metodą informacje o zapytaniach (klasa DB) i brakuje tego ostatniego zapytania kończącego sesję (teraz oczywiście rozważam wersję bez zakończonego połączenia).

Aha, jeszcze dodam (może to mieć jakiś związek - tak przypuszczam), że obiekty pobieram przy pomocy czegoś w stylu rejestru, tzw. ClassLoader'a.
Załączę odnośniki do kodów klas. Na logikę i angielski proszę na razie nie zwracać uwagi - poprawki wprowadzam na bieżąco winksmiley.jpg
session.class.php
db.class.php
classloader.class.php
-=Peter=-
Jaki jest sens zamykania połączenia z bazą danych w destrukorze klasy Db? Wg mnie nie ma żadnego sensu, bo tak czy siak połączenie z bd jest zamykane na końcu działania aplikacji.
kazaa9
Też racja.

Ale nawet nie o to mi chodziło. Chciałbym uzyskać odpowiedź na temat tego destruktora, dlaczego to nie działa. W sumie jakoś pracy szczególnie nie utrudnia, ale bywa w małym stopniu irytujące, gdyż (moim zdaniem) powinno wszystko działać.
LBO
Przez lata programiści nauczyli się nie ufać destruktorom w PHP. Sprawiały mnóstwo problemów (wystarczy poczytać komentarze w odpowiednim rozdziale manuala). Istnieje obejście tego problemu, dosyć szeroko stosowane w wielu frameworkach - swój własny wirtualny system zarządzania obiektami.


  1. class ApplicationManager
  2. {
  3. protected $managers = array();
  4.  
  5. public function registerManager(IManager $manager)
  6. {
  7. $this->managers[] = $manager;
  8. }
  9.  
  10. public function start()
  11. {
  12. // number of registered managers
  13. $norm= count($this->managers);
  14. for($i = 0; $i < $norm; $i--)
  15. {
  16. $this->managers[$i]->start();
  17. }
  18. }
  19.  
  20. public function stop()
  21. {
  22. // number of registered managers
  23. $norm= count($this->managers);
  24. for($i = $norm - 1; $i >= 0; $i--)
  25. {
  26. $this->managers[$i]->stop();
  27. }
  28. }
  29. }
  30.  
  31. interface IManager
  32. {
  33. public function start();
  34. public function stop();
  35. }
  36.  
  37. class SessionManager implements IManager
  38. {
  39. public function start()
  40. {
  41. // ustawiasz sobie sterownik sesji
  42. }
  43.  
  44. public function stop()
  45. {
  46. //zamykasz sesję
  47. }
  48. }
  49.  
  50.  
  51. class DatabaseManager implements IManager
  52. {
  53. public function __construct($dsn)
  54. {
  55. // łączysz się z bazą
  56. }
  57.  
  58. public function start() {}
  59.  
  60. public function stop()
  61. {
  62. // zamykasz połączenie z bazą
  63. }
  64. }


A wykorzystujesz to mniej więcej tak.

  1. $application = new ApplicationManager();
  2.  
  3. // dalej pobierasz konfig aplikacji i tworzysz na jego podstawie potrzebne obiekty m.in. obiekty odpowiedzialne za sesje i połączenie z bazą danych.
  4.  
  5. $database = new Database($config['database']['dsn']);
  6.  
  7. $application->registerManager($database);
  8.  
  9. $session = new SessionManager();
  10.  
  11. $application->registerManager($session);
  12.  
  13. $application->start(); // wszystkie zarejestrowane obiekty wykonują własne metody start()
  14.  
  15. // etc
  16.  
  17. $application->stop(); // wszystkie zarejestrowane obiekty wykonują własne metody stop(). Zwróć uwagę, że robią to w odwrotnej kolejności niż zostały zarejestrowane. To jest ważne - pozwala zachować zależności.
  18.  


Wszystko pisane z palca, więc jakby co poprawiać lub pytać smile.gif

Pozdrawiam

Cytat(kazaa9 @ 16.08.2009, 18:58:01 ) *
Też racja.

Ale nawet nie o to mi chodziło. Chciałbym uzyskać odpowiedź na temat tego destruktora, dlaczego to nie działa. W sumie jakoś pracy szczególnie nie utrudnia, ale bywa w małym stopniu irytujące, gdyż (moim zdaniem) powinno wszystko działać.


O ile sam nie usuwasz obiektów robi to samo PHP przy zakończeniu skryptu - a wtedy nie możesz być 100%-owo pewny kolejności. Rozwiązanie powyżej.
sowiq
Cytat(-=Peter=- @ 16.08.2009, 18:39:47 ) *
Jaki jest sens zamykania połączenia z bazą danych w destrukorze klasy Db? Wg mnie nie ma żadnego sensu, bo tak czy siak połączenie z bd jest zamykane na końcu działania aplikacji.
Mylisz się. Połączenie jest zamykane w chwili jego wygaśnięcia, czyli wcale niekoniecznie po zakończeniu działania aplikacji. Timeout jest ustawiany w opcjach MySQL.

Jeśli odwiedzalność Twojej strony jest na poziomie 1/godzinę, to nie ma to znaczenia. Ale jeśli chcesz stworzyć optymalny skrypt, który wytrzyma obciążenie rzędu kilkuset - kilka tys. odsłon / minutę, to oczekiwanie na wygaśnięcie każdego połączenia nie jest dobrym pomysłem.
kazaa9
Cytat
O ile sam nie usuwasz obiektów robi to samo PHP przy zakończeniu skryptu - a wtedy nie możesz być 100%-owo pewny kolejności. Rozwiązanie powyżej.


Więc chyba przebuduję swoją aplikacyjkę, wywalę destruktory i zastąpię to tym (nieco zmienionym - mam chyba pomysł) rozwiązaniem. Czytałem sporo o tych destruktorach i faktycznie pojawiło się trochę informacji o "wałkach" jakie robią. Myślałem, że dotyczyły one wersji PHP <=5.1, a tu proszę.

Dziękuję za rzeczowe uwagi.
LBO
dodałem kilk małych zmian w kodzie.

Cytat(kazaa9 @ 16.08.2009, 19:42:42 ) *
Więc chyba przebuduję swoją aplikacyjkę, wywalę destruktory i zastąpię to tym (nieco zmienionym - mam chyba pomysł) rozwiązaniem. Czytałem sporo o tych destruktorach i faktycznie pojawiło się trochę informacji o "wałkach" jakie robią. Myślałem, że dotyczyły one wersji PHP <=5.1, a tu proszę.

Dziękuję za rzeczowe uwagi.


Oczywiście, musisz to zmienić, żeby było używalne. To co przedstawiłem to jedynie zalążek smile.gif
-=Peter=-
Cytat(sowiq @ 16.08.2009, 17:35:38 ) *
Mylisz się. Połączenie jest zamykane w chwili jego wygaśnięcia, czyli wcale niekoniecznie po zakończeniu działania aplikacji. Timeout jest ustawiany w opcjach MySQL.

Jeśli odwiedzalność Twojej strony jest na poziomie 1/godzinę, to nie ma to znaczenia. Ale jeśli chcesz stworzyć optymalny skrypt, który wytrzyma obciążenie rzędu kilkuset - kilka tys. odsłon / minutę, to oczekiwanie na wygaśnięcie każdego połączenia nie jest dobrym pomysłem.

Nie wiem czy masz rację, ale moją wiedzę na ten temat zaczerpnąłem z manuala, być może jest tam błąd lub nowsze biblioteki do obsługi baz danych różnią się pod tym względem.

http://pl.php.net/mysql_close
http://pl.php.net/pg_close

Jednak nie znalazłem informacji jak to wygląda np. w mysqli czy PDO, być może jest tak jak mówisz. Oczywiście mówimy tutaj o połączeniach tymczasowych, a nie stałych, które to na pewno nie są zamykane wraz z końcem działania aplikacji.
erix
Cytat
Mylisz się. Połączenie jest zamykane w chwili jego wygaśnięcia, czyli wcale niekoniecznie po zakończeniu działania aplikacji. Timeout jest ustawiany w opcjach MySQL.

Wygaśnięcia, to znaczy? Bo pierwsze słyszę o wygasaniu połączeń...

Cytat
Jednak nie znalazłem informacji jak to wygląda np. w mysqli czy PDO, być może jest tak jak mówisz. Oczywiście mówimy tutaj o połączeniach tymczasowych, a nie stałych, które to na pewno nie są zamykane wraz z końcem działania aplikacji.

W sterownikach stricte obiektowych jest to realizowane właśnie w destruktorach.
sowiq
@-=Peter=-, @erix,
oczywiście macie rację. Strasznie nabredziłem.
Cytat
Using mysql_close() isn't usually necessary, as non-persistent open links are automatically closed at the end of the script's execution.

Jednak dalej jestem przekonany do zamykania połączenia w destruktorze.

@erix,
usuń proszę mojego poprzedniego posta, albo przekreśl jego treść - niech nie miesza ludziom w głowie. Ja już nie mogę edytować.
erix
Cytat
usuń proszę mojego poprzedniego posta, albo przekreśl jego treść - niech nie miesza ludziom w głowie.

Zrobione. Na przyszłość raportuj.

Cytat
Jednak dalej jestem przekonany do zamykania połączenia w destruktorze.

Wiesz, kiedyś działy mi się dziwne rzeczy, jeśli używałem mysql_free_result w destruktorze. tongue.gif Nie mam zielonego pojęcia, czemu to się tak działo; może w ostatnich wersjach zostało to poprawione, trudno mi cokolwiek na ten temat powiedzieć.
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-2025 Invision Power Services, Inc.