Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Obsługa właściwości tablicowych w klasie z użyciem "metod magicznych"
Gieroslawski
post 3.11.2017, 00:16:45
Post #1





Grupa: Zarejestrowani
Postów: 2
Pomógł: 0
Dołączył: 2.11.2017

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


Witam

Czy w PHP jest możliwy dostęp w klasie do prywatnego pola będącego tablicą, za pomocą tzw. „metod magicznych” __get i __set? Pytam, ponieważ używam wymienionych metod do uzyskiwania dostępu do pól klasy, które są typami prostymi lub obiektami i to działa całkiem przyzwoicie (wiem, że są pewne spadki wydajności ale to w chwili obecnej nie jest dla mnie kluczowe). Chciałbym niektóre utworzone przez siebie klasy wyposażyć we właściwości tablicowe. Przez pojęcie właściwości rozumiem tu mechanizm znany z języków takich jak C# czy ObjectPascal, gdzie w klasie istnieje pole, do którego dostęp odbywa się poprzez prywatne metody dostępowe (akcesory). Pozwala to np.: sprawdzać poprawność przekazywanych wartości (dla metody „set”) czy zwracać określoną wartość dla właściwości, która może nie posiadać odpowiadającego jej prywatnego pola w klasie a zwracana wartość jest np. obliczana (dla metody „get”). Żeby dobrze wyłuszczyć nurtujący mnie problem, przytoczę fragment hipotetycznej klasy:

  1. <?php
  2.  
  3. class TMyComponent
  4. {
  5. private $_count;
  6. private $_name;
  7. private $_values;
  8.  
  9. public function __construct()
  10. {
  11. $this->_ count = 0;
  12. $this->_name = "";
  13. $this->_values = array();
  14. }
  15.  
  16. public function __get($name)
  17. {
  18. $method = "get" . ucfirst($name);
  19. if (method_exists($this, $method))
  20. {
  21. return $this->$method();
  22. }
  23. }
  24.  
  25. public function __set($name, $value)
  26. {
  27. $method = "set" . ucfirst($name);
  28. if (method_exists($this, $method))
  29. {
  30. $this->$method($value);
  31. }
  32. }
  33.  
  34. private function getCount()
  35. {
  36. return $this->_count;
  37. }
  38.  
  39. private function getName()
  40. {
  41. return $this->_name;
  42. }
  43.  
  44. private function setName($value)
  45. {
  46. $this->_name = value;
  47. }
  48.  
  49. public function getValues($index)
  50. {
  51. $item = NULL;
  52. if ($this->isIndexValid($index))
  53. {
  54. $item = $this->_items[$index];
  55. }
  56. return $item;
  57. }
  58.  
  59. public function setValues($index, $value)
  60. {
  61. if ($this->isIndexValid($index))
  62. {
  63. $this->replace($index, $value);
  64. }
  65.  
  66. protected function isIndexValid($index)
  67. {
  68. $result = is_int($index) && ($index < $this->_count) && ($index >= 0);
  69. }
  70.  
  71. }
  72.  
  73. ?>


Dostęp do dwóch pierwszych pól w utworzonym obiekcie wyżej podanej klasy jest dość wygodny, np.:

  1. $myObj = new TMyComponent();
  2.  
  3. ...
  4.  
  5. $n = $myObj->count;
  6. $myObj->name = "abcdef";


Niestety do pola $_values można dostać się tylko przy jawnym wywołaniu metod getValues i setValues, np.:

  1. $oldValue = $myObj->getValues(2); // odczyt z komórki prywatnego pola klasy, gdzie pole jest tablicą
  2.  
  3. $myObj->setValues(2, 64); // zapis do komórki prywatnego pola klasy, gdzie pole jest tablicą


Czy w PHP jest możliwe takie przebudowanie wyżej podanego ciągu instrukcji metod __get i __set aby można było dostać się do pola $_values w taki sposób:

  1. $oldValue = $myObj->values[2]; // odczyt z komórki prywatnego pola klasy, gdzie $values jest właściwością tablicową (a nie polem klasy!)
  2.  
  3. $myObj->values[2] = 64; // zapis do komórki prywatnego pola klasy, gdzie $values jest właściwością tablicową (a nie polem klasy!)


Wg mojej wiedzy dotyczącej PHP (z pewnością niekompletnej), tego nie da się zaimplementować. A to dlatego, ponieważ metoda magiczna __get pobiera tylko jeden argument, którym jest nazwa właściwości. Niestety nie da się przekazać indeksu komórki do tablicy. Podobnie ma się rzecz z metodą magiczną __set, która pobiera tylko dwa argumenty, którymi są nazwa właściwości i wartość podlegająca przypisaniu do prywatnego pola. Być może się mylę i da się ten problem rozwiązać (co byłoby miłym zaskoczeniem). Rzecz jednak w tym, że dość skrupulatnie przekopywałem dział Help serwisu php.net. Próbowałem także przeszukiwać różne fora i blogi dotyczące programowania w PHP. Niestety nie natknąłem się na rozwiązanie takiego problemu. Jedyne na co trafiłem to na propozycje rozszerzenia języka PHP o metody dostępowe get i set działające podobnie jak w C# (tu: https://bugs.php.net/bug.php?id=49526, ale ta propozycja została odrzucona a szkoda, bo ten mechanizm wydaje się być o wiele lepszy niż tzw. "metody magiczne").

Czy jest zatem możliwe obsłużenie w klasie prywatnego pola-tablicy za pomocą metod magicznych __get i __set?
Go to the top of the page
+Quote Post
kayman
post 3.11.2017, 00:56:11
Post #2





Grupa: Zarejestrowani
Postów: 556
Pomógł: 40
Dołączył: 20.07.2012
Skąd: Warszawa

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


  1.  
  2. class MyClass {
  3.  
  4. private $values = array();
  5.  
  6. public function __set($key, $value) {
  7. $this->values[$key] = $value;
  8. }
  9.  
  10. public function __get($key) {
  11. return isset($this->values[$key]) ? $this->values[$key] : null;
  12. }
  13.  
  14. }
  15.  
  16. // ---
  17.  
  18. $obj = new MyClass();
  19. $obj->foo = 'bar';
  20. $var = $obj->foo; // $var = 'bar';
  21.  


Ten post edytował kayman 3.11.2017, 01:08:03
Go to the top of the page
+Quote Post
Gieroslawski
post 5.11.2017, 19:39:43
Post #3





Grupa: Zarejestrowani
Postów: 2
Pomógł: 0
Dołączył: 2.11.2017

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


Szanowny kayman'ie Twoja odpowiedź niestety nie dotyka istoty opisanego przez mnie problemu. Przykład podany przez Ciebie jest bardzo popularny. Nie chodziło mi o banalny/trywialny sposób dostępu do pól klasy zgromadzonych w jednym polu jako tablicy, indeksowanej nazwami właściwości. Jeszcze raz powtórzę: jedno z pól klasy jest tablicą. Być może nieporozumienie wynika z tego, że w dokumentacji PHP występuje pojęcie właściwości, które jest błędnie użyte przez ludzi rozwijających język PHP. To co oni opisują w swoim systemie pomocy jako właściwość to jest w rzeczywistości polem klasy. Właściwości to mechanizm dostępu do pól klasy przez metody dostępowe, które są wywoływane niejawnie (nie jest to ścisła definicja, a tylko mój jak najkrótszy opis).

Ja w swoich klasach potrzebuję dostępu do komórek pola będącego tablicą w klasie, poprzez symulowanie właściwości, które by pozwalały przekazywać indeks komórki w metodach __get i __set. W językach, które wymieniłem w poprzednim poście, tj. C# czy ObjectPascal właściwości mogą być tablicowe. Oznacza to, że w tych językach metody dostępowe "get" i "set" uwzględniają dodatkowy parametr, którym jest indeks. Przykładowo w języku ObjectPascal, klasa którą w pierwszym poście przedstawiłem, miałaby następujący (zbliżony do PHP) kod:

Kod
TMyComponent = class(TObject)
  private
    FCount: Integer;
    FName: String;
    FValues: TList<TPerson>;
    function GetValue(const Index: Integer): TPerson;
    procedure SetName(Value: String);
    procedure SetValue(const Index: Integer; Value: TPerson);
  public
    constructor Create;
    destructor Destroy; override;
    property Count: Integer read FCount;
    property Name: Integer read FName write SetName;
    property Values[const Index: Integer]: TPerson read GetValue write SetValue;
  end;
    
constructor Create;
begin
  FCount := 0;
  FName := "";
  FValues := TList<TPerson>.Create;
end;

destructor Destroy;
begin
  FValues.Free;
  inherited Destroy;
end;

function SetName;
begin
  if (FName <> Value)
   then FName := Value;
end;

function GetValue(const Index: Integer): TPerson;
begin
  Result := nil;
  if IsIndexValid(Index)
   then Result := FValues[Index];
end;

procedure SetValue(const Index: Integer; Value: TPerson);
begin
  if IsIndexValid(Index)
   then FValues[Index] := Value;
end;

function IsIndexValid(const Index: Integer);
begin
  Result := (Index >= 0) and (Index < FValue.Count);
end;

...
// i sposób jej użycia

myObj := TMyComponent.Create;
...
var OldValue: Integer;
...
OldValue := myObj.Values[2];  // odczyt właściwości tablicowej
myObj.Values[2] := 64;  // zapis właściwości tablicowej
...
myObj.Free;


To co ja bym potrzebował, można by przedstawić za pomocą następującego hipotetycznego (całkiem wymyślonego) kodu w PHP:

  1. <?php
  2.  
  3. class TMyComponent
  4. {
  5. private $_count;
  6. private $_name;
  7. private $_values;
  8.  
  9. public function __construct()
  10. {
  11. $this->_ count = 0;
  12. $this->_name = "";
  13. $this->_values = array();
  14. }
  15.  
  16. public function __get($name, $index) // <- hipotetyczny parametr "index" w metodzie __get
  17. {
  18. $method = "get" . ucfirst($name);
  19. if (method_exists($this, $method))
  20. {
  21. if isset($index)
  22. {
  23. return $this->$method($index);
  24. }
  25. else return $this->$method();
  26. }
  27. }
  28.  
  29. public function __set($name, $value, $index) // <- hipotetyczny parametr "index" w metodzie __set
  30. {
  31. $method = "set" . ucfirst($name);
  32. if (method_exists($this, $method, $index))
  33. {
  34. if isset($index)
  35. {
  36. $this->$method($index, $value);
  37. }
  38. else $this->$method($value);
  39. }
  40. }
  41.  
  42. private function getCount()
  43. {
  44. return $this->_count;
  45. }
  46.  
  47. private function getName()
  48. {
  49. return $this->_name;
  50. }
  51.  
  52. private function setName($value)
  53. {
  54. $this->_name = value;
  55. }
  56.  
  57. public function getValues($index)
  58. {
  59. $item = NULL;
  60. if ($this->isIndexValid($index))
  61. {
  62. $item = $this->_items[$index];
  63. }
  64. return $item;
  65. }
  66.  
  67. public function setValues($index, $value)
  68. {
  69. if ($this->isIndexValid($index))
  70. {
  71. $this->replace($index, $value);
  72. }
  73. }
  74.  
  75. protected function isIndexValid($index)
  76. {
  77. $result = is_int($index) && ($index < $this->_count) && ($index >= 0);
  78. }
  79.  
  80. }
  81.  
  82. ?>


Wtedy wyżej przedstawioną hipotetyczną klasę używałoby się następująco (na razie też wyłącznie hipotetycznie :-)

  1. $myObj = new TMyComponent();
  2. ...
  3. $n = $myObj->count;
  4. $myObj->name = "abcdef";
  5. $oldValue = $myObj->values[2]; // odczyt z komórki właściwości, nie z pola klasy, gdzie niejawnie jest wywoływana czytająca metoda dostępowa
  6. $myObj->values[2] = 64; // zapis do komórki właściwości, nie do pola klasy, gdzie niejawnie jest wywoływana zapisująca metoda dostępowa


W tym hipotetycznym kodzie metod __get i __set widać, że posiadają one dodatkowy opcjonalny parametr, który służy do przekazywania indeksu do tablicy. I wg mojej wiedzy w PHP takiej możliwości nie ma, bo metoda __get przyjmuje jeden parametr (nazwę pola w klasie) a metoda __set dwa parametry (nazwę pola w klasie i wartość do zapisania w tym polu). Nie ma możliwości przekazania indeksów dla pól będących tablicami. A tego mi właśnie bardzo brakuje. Na razie zostaje mi obsługa dostępu do pól w klasach na modłę języka Java, tj. poprzez publiczne metody dostępowe, co jest trochę upierdliwe. Chyba, że jednak da się w kodzie PHP jakoś przekazać do metod __get i __set dodatkowy parametr a tylko ja o tym nie wiem. Bardzo by mnie to ucieszyło, usprawniłbym swoją "biblioteczkę" klas.
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 Wersja Lo-Fi Aktualny czas: 19.03.2024 - 12:10