Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [PHP] Dziwny sposób obejścia metody __SET. Proszę o objaśnienie., czyli zapis pola z pozycji metody statycznej dokonujący się poza __SET
Ziuk
post
Post #1





Grupa: Zarejestrowani
Postów: 3
Pomógł: 0
Dołączył: 26.07.2011

Ostrzeżenie: (0%)
-----


Witam

Zakupiłem książkę PHP i MySQL. Projekty do wykorzystania:
http://helion.pl/ksiazki/php-i-mysql-proje...inas,phmspr.htm

Znajduje się w niej kod odpowiadający za mechanizm rejestracji i logowania użytkowników, który w kolejnych rozdziałach jest bazą do tworzenia różnych przykładowych aplikacji. Jego zasadnicza częścią jest klasa User, której kod przedstawiam poniżej:


  1. <?php
  2. class User
  3. {
  4. private $uid; // identyfikator użytkownika
  5. private $fields; // inne pola rekordu
  6.  
  7. // inicjalizacja obiektu User
  8. public function __construct()
  9. {
  10. $this->uid = null;
  11. $this->fields = array('username' => '',
  12. 'password' => '',
  13. 'emailAddr' => '',
  14. 'isActive' => false);
  15. }
  16.  
  17. // nadpisanie metody odczytującej właściwości
  18. public function __get($field)
  19. {
  20. if ($field == 'userId')
  21. {
  22. return $this->uid;
  23. }
  24. else
  25. {
  26. return $this->fields[$field];
  27. }
  28. }
  29.  
  30. // nadpisanie metody ustawiającej właściwości
  31. public function __set($field, $value)
  32. {
  33. if (array_key_exists($field, $this->fields))
  34. {
  35. $this->fields[$field] = $value;
  36. }
  37. }
  38.  
  39. // sprawdzenie, czy nazwa użytkownika ma właściwy format
  40. public static function validateUsername($username)
  41. {
  42. return preg_match('/^[A-Z0-9]{2,20}$/i', $username);
  43. }
  44.  
  45. // sprawdzenie, czy adres email ma właściwy format
  46. public static function validateEmailAddr($email)
  47. {
  48. return filter_var($email, FILTER_VALIDATE_EMAIL);
  49. }
  50.  
  51. // zwrócenie obiektu wypełnionego na podstawie identyfikatora użytkownika
  52. public static function getById($uid)
  53. {
  54. $u = new User();
  55.  
  56. $query = sprintf('SELECT USERNAME, PASSWORD, EMAIL_ADDR, IS_ACTIVE ' .
  57. 'FROM %sUSER WHERE USER_ID = %d',
  58. DB_TBL_PREFIX,
  59. $uid);
  60. $result = mysql_query($query, $GLOBALS['DB']);
  61.  
  62. if (mysql_num_rows($result))
  63. {
  64. $row = mysql_fetch_assoc($result);
  65. $u->username = $row['USERNAME'];
  66. $u->password = $row['PASSWORD'];
  67. $u->emailAddr = $row['EMAIL_ADDR'];
  68. $u->isActive = $row['IS_ACTIVE'];
  69. $u->uid = $uid;
  70. }
  71.  
  72. return $u;
  73. }
  74.  
  75. // zwrócenie obiektu wypełnionego na podstawie nazwy użytkownika
  76. public static function getByUsername($username)
  77. {
  78. $u = new User();
  79.  
  80. $query = sprintf('SELECT USER_ID, PASSWORD, EMAIL_ADDR, IS_ACTIVE ' .
  81. 'FROM %sUSER WHERE USERNAME = "%s"',
  82. DB_TBL_PREFIX,
  83. mysql_real_escape_string($username, $GLOBALS['DB']));
  84. $result = mysql_query($query, $GLOBALS['DB']);
  85.  
  86. if (mysql_num_rows($result))
  87. {
  88. $row = mysql_fetch_assoc($result);
  89. $u->username = $username;
  90. $u->password = $row['PASSWORD'];
  91. $u->emailAddr = $row['EMAIL_ADDR'];
  92. $u->isActive = $row['IS_ACTIVE'];
  93. $u->uid = $row['USER_ID'];
  94. }
  95.  
  96. return $u;
  97. }
  98.  
  99. // zapisanie rekordu w bazie danych
  100. public function save()
  101. {
  102. if ($this->uid)
  103. {
  104. $query = sprintf('UPDATE %sUSER SET USERNAME = "%s", ' .
  105. 'PASSWORD = "%s", EMAIL_ADDR = "%s", IS_ACTIVE = %d ' .
  106. 'WHERE USER_ID = %d',
  107. DB_TBL_PREFIX,
  108. mysql_real_escape_string($this->username, $GLOBALS['DB']),
  109. mysql_real_escape_string($this->password, $GLOBALS['DB']),
  110. mysql_real_escape_string($this->emailAddr, $GLOBALS['DB']),
  111. $this->isActive,
  112. $this->userId);
  113. mysql_query($query, $GLOBALS['DB']);
  114. }
  115. else
  116. {
  117. $query = sprintf('INSERT INTO %sUSER (USERNAME, PASSWORD, ' .
  118. 'EMAIL_ADDR, IS_ACTIVE) VALUES ("%s", "%s", "%s", %d)',
  119. DB_TBL_PREFIX,
  120. mysql_real_escape_string($this->username, $GLOBALS['DB']),
  121. mysql_real_escape_string($this->password, $GLOBALS['DB']),
  122. mysql_real_escape_string($this->emailAddr, $GLOBALS['DB']),
  123. $this->isActive);
  124. mysql_query($query, $GLOBALS['DB']);
  125.  
  126. $this->uid = mysql_insert_id($GLOBALS['DB']);
  127. }
  128. }
  129.  
  130. // oznaczenie rekordu jako nieaktywnego i zwrócenie znacznika aktywacji
  131. public function setInactive()
  132. {
  133. $this->isActive = false;
  134. $this->save(); // zapewnienie, że rekord jest zapisany
  135.  
  136. $token = random_text(5);
  137. $query = sprintf('INSERT INTO %sPENDING (USER_ID, TOKEN) ' .
  138. 'VALUES (%d, "%s")',
  139. DB_TBL_PREFIX,
  140. $this->uid,
  141. $token);
  142. mysql_query($query, $GLOBALS['DB']);
  143.  
  144. return $token;
  145. }
  146.  
  147. // wyczyszczenie tymczasowego statusu użytkownika i oznaczenie rekordu jako aktywnego
  148. public function setActive($token)
  149. {
  150. $query = sprintf('SELECT TOKEN FROM %sPENDING WHERE USER_ID = %d ' .
  151. 'AND TOKEN = "%s"',
  152. DB_TBL_PREFIX,
  153. $this->uid,
  154. mysql_real_escape_string($token, $GLOBALS['DB']));
  155. $result = mysql_query($query, $GLOBALS['DB']);
  156.  
  157. if (!mysql_num_rows($result))
  158. {
  159. return false;
  160. }
  161. else
  162. {
  163. $query = sprintf('DELETE FROM %sPENDING WHERE USER_ID = %d ' .
  164. 'AND TOKEN = "%s"', DB_TBL_PREFIX,
  165. $this->uid,
  166. mysql_real_escape_string($token, $GLOBALS['DB']));
  167. mysql_query($query, $GLOBALS['DB']);
  168.  
  169. $this->isActive = true;
  170. $this->save();
  171. return true;
  172. }
  173. }
  174. }
  175. ?>


Zdeklarowana w klasie metoda dostępowa __SET:

Kod
    
    public function __set($field, $value)
    {
        if (array_key_exists($field, $this->fields))
        {
            $this->fields[$field] = $value;
        }
    }


przewiduje jedynie zapis do pola fields, które za sprawą konstruktora rozbudowywane jest do rozmiarów tablicy. Jak zatem możliwy jest zapis do pola $uid występujący np. w metodzie public static function getById($uid)?

Czy ktoś mógłby to wyjaśnić?

Wiem, że mógłbym uniknąć całego problemu zmieniając pola z private na public lub deklarując w __SET obsługę także pola $uid ale mnie interesuje zrozumiane tego jak to się dzieje, że zapis do pola $uid jest w ogóle możliwy mimo braku obsługi tegoż pola w metodzie __SET.

Za pomoc z góry dziękuję.

Cały rozdział(1) w którym umieszczona została przytoczona powyżej klasa jest dostępny na stronie:
http://pdf.helion.pl/phmspr/phmspr-1.pdf
Dostępny jest także kod z tego rozdziału:
ftp://ftp.helion.pl/przyklady/phmspr.zip


Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi (1 - 4)
thek
post
Post #2





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Pamiętaj, że zmienne choć prywatne, są widoczne dla metod tejże klasy. Co z tego, że $obiekt->uid = 5 wywali błąd, skoro masz metodę, która wewnątrz widzi $this->uid i może z nią robić co chce smile.gif Poczytaj ciutkę o obiektówce i zasięgu widoczności zmiennych. __set i __get owszem... mają taką definicję, że działają z private $fields, ale jakakolwiek metoda tej klasy, niezależne o jakim atrybucie dostępu, widzi jej składowe już jako publiczne. Na zasadzie "Co jest moje to zezwalam, a tyś obcy i nie pozwolę."


--------------------
Najpierw był manual... Jeśli tam nie zawarto słów mądrości to zapytaj wszechwiedzącego Google zadając mu własciwe pytania. A jeśli i on milczy to Twój problem nie istnieje :D
Go to the top of the page
+Quote Post
mortus
post
Post #3





Grupa: Zarejestrowani
Postów: 2 178
Pomógł: 596
Dołączył: 25.09.2009
Skąd: Piwniczna-Zdrój

Ostrzeżenie: (0%)
-----


Z manuala PHP:
Cytat
__set() is run when writing data to inaccessible properties.

Oznacza to, że operator -> za pomocą domyślnej metody __set() w pierwszej kolejności próbuje uzyskać dostęp do zadeklarowanych właściwości (property) klasy o określonym zasięgu, a w przypadku, gdy danej właściwości nie ma lub nie ma do niej dostępu następuje przeciążenie wyżej wspomnianej metody __set(), które to realizuje Twoja funkcja.
Go to the top of the page
+Quote Post
Ziuk
post
Post #4





Grupa: Zarejestrowani
Postów: 3
Pomógł: 0
Dołączył: 26.07.2011

Ostrzeżenie: (0%)
-----


Jest więc tak, że metody klasy mogą uzyskiwać dostęp i możliwość zapisu do dowolnego pola klasy (w tym pola prywatnego) o ile tego typu operacja rozgrywa się w ramach tejże klasy nie wychodząc poza jej obręb. Natomiast jeżeli spoza klasy chciałbym ustawić wartość pola $uid to taka operacja nie jest możliwa gdyż metoda dostępowa __set w tym wypadku nie obsługuje zmiany $uid z pozycji (zewnętrznego wobec klasy User) obiektu klasy User.
W kodzie o którym mowa metoda __set właściwe kieruje jedynie tym gdzie mają być zapisywane wartości tzn. wskazuje zapis do konkretnych pozycji tablicy fields, która jest jednocześnie polem. Bez "nawigacji" prowadzonej przez __set dane zapisywane byłyby wprost do pól klasy czyli do pól takich jak $uid i dlatego też $uid, który nie jest ujęty w "nawigacji" realizowanej za pośrednictwem __set jest zapisywalny.

Czy teraz dobrze rozumuje?

Cytat
przeciążenie wyżej wspomnianej metody __set(), które to realizuje Twoja funkcja.


Jeszcze nie wiem co to jest przeciążenie metody smile.gif

Ten post edytował Ziuk 27.07.2011, 14:05:08
Go to the top of the page
+Quote Post
thek
post
Post #5





Grupa: Moderatorzy
Postów: 4 362
Pomógł: 714
Dołączył: 12.02.2009
Skąd: Jak się położę tak leżę :D




Dobrze rozumiesz Ziuk. Wewnątrz klasy każda metoda ma dostęp do pól na zasadzie specyfikatora public, bo jak wspomniałem: "To co moje może swobodnie sie ze soba porozumiewać". Tutaj co byś nie robił, to i tak każdy ma dostęp do wszystkiego. Stąd wewnątrz metod odwołania $this->pole są jak najbardziej poprawne. Dopiero próba odwołania zewnetrznego $obiekt_klasy->pole, jeśli byłoby ono private lub protected, jest naruszeniem. Własnie dlatego są metody magiczne __set i __get, które zwyczajowo są tworzone jako:
  1. public function __set($field, $value) {
  2. $this->$field = $value;
  3. }
co sprawia, że jeśli nie istnieje własność o nazwie $field to tworzona jest wtedy niejawnie i wartość mu nadawana jest ustawiana na $value. W Twoim wypadku jest ona przeciążona/nadpisana w taki sposób, że odbywało się to nie wprost do obiektu ale do własności fields i to tylko, jeśli dany index istnieje w tej tablicy, na co zapewne nie zwróciłeś uwagi. Tak więc zapis $obiekt->password = 'test' da radę zrobić, ale juz $obiekt->losowe_pole = 'próba' nie powiedzie się, bo w tablicy fields brak indeksu o nazwie losowe_pole.

Przeciążenie to deklaracja wielu wersji tej samej metody, przy czym kompilator/parser z kontekstu wychwytuje o która z nich chodzi. W PHP nie jest to tak widoczne, ponieważ jawne przeciążenie tu ciężko zrobić i odbywa się z kontekstu obiektu. Jest to lepiej widoczne w językach ze stałym typowaniem, gdzie choćby metoda( int zmienna) i metoda( float zmienna ) są dla komplilatora dwiema zupełnie innymi, choć o tej samej nazwie. Musiałbym sprawdzić jak PHP reaguje na metoda( array $zmienna ) i metoda( object $zmienna ) oraz metoda( $zmienna ), ale z tego co kojarzę to powinien się zbuntować coś o redeklaracji funkcji krzycząc wink.gif


--------------------
Najpierw był manual... Jeśli tam nie zawarto słów mądrości to zapytaj wszechwiedzącego Google zadając mu własciwe pytania. A jeśli i on milczy to Twój problem nie istnieje :D
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 20.08.2025 - 06:39