PHP/Klasy i obiekty: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
m Wycofano edycje użytkownika 192.162.63.49 (dyskusja). Autor przywróconej wersji to Kabexxxior.
Znacznik: Wycofanie zmian
Nie podano opisu zmian
 
Linia 9:
W językach programowania klasy traktowane są zawsze jako rodzaj typów danych. Można powiedzieć, że klasa jest '''definicją''' lub ''szablonem'' obiektów. W PHP deklarujemy je słowem kluczowym '''class''', po którym podajemy jej unikatową nazwę. Zasady jej tworzenia są podobne, jak w przypadku nazw zmiennych, tj. nie mogą one zaczynać się od cyfry. Następnie w nawiasach klamrowych umieszczamy informacje o dozwolonych polach oraz metodach, jakie klasa będzie posiadać:
 
<sourcesyntaxhighlight lang="php" line><?php
class Person
{
Linia 26:
} // end getFullName();
}
</syntaxhighlight>
</source>
 
Zwyczajowo pola deklaruje się na początku klasy, natomiast później - metody, które okazują się być bardzo podobne do funkcji. Podobieństwo jest jak najbardziej uzasadnione. Właściwie funkcje także reprezentują pewne zachowanie, więc nic nie stoi na przeszkodzie, aby wykorzystać tę funkcjonalność do zdefiniowania zachowań obiektów. Zasady działania metod są bardzo podobne - pobierają one argumenty i mogą zwracać wartość. Jedyna istotna różnica to obecność specjalnego wskaźnika <code>$this</code>, który wskazuje zawsze na obiekt, na którym daną metodę wywołujemy. Dzięki niemu możemy dostać się do wartości przechowywanych w polach obiektu oraz wywoływać inne metody. Służy do tego specjalny operator '''->'''. Zauważ, że odwołując się do pól, pomijamy znak dolara. Nie przejmuj się słowem kluczowym '''public'''. Jego znaczenie poznamy za chwilę.
Linia 36:
Obiekty tworzymy operatorem '''new''', po którym podajemy nazwę klasy. Zwraca on referencję do obiektu, którą możemy zapisać w zmiennej:
 
<sourcesyntaxhighlight lang="php" line><?php
// Dolaczamy plik z nasza klasa
require('./Person.php');
Linia 48:
echo 'Witaj, jestem '.$janusz->getFullName().'<br/>';
echo 'A ja jestem '.$adam->getFullName().'<br/>';
</syntaxhighlight>
</source>
 
Możemy też odwołać się bezpośrednio do odpowiednich pól: <code>$adam->name</code>.
Linia 61:
Wspomnieliśmy, że w PHP5 obiekty nie są rodzajem wartości, lecz oddzielnym bytem, do którego skrypt posiada jedynie referencje. Spójrzmy, czym to skutkuje w praktyce. Rozpatrzmy prosty skrypt:
 
<sourcesyntaxhighlight lang="php" line><?php
function modify($value)
{
Linia 70:
modify($number);
echo $number;
</syntaxhighlight>
</source>
 
Skrypt ten wyświetli nam wartość "6". Zmienna, na której operuje funkcja <code>modify()</code> jest jedynie kopią zmiennej globalnej <code>$number</code>, dlatego jej modyfikacja nie wpływa na wartość oryginału. Inaczej jest w przypadku obiektów:
 
<sourcesyntaxhighlight lang="php" line><?php
require('./Person.php');
 
Linia 86:
modify($janusz);
echo $janusz->getFullName();
</syntaxhighlight>
</source>
 
Tym razem skrypt wyświetlił nam wartość <code>Janusz Nowak</code>, co oznacza, że zmiana stanu obiektu wprowadzona przez funkcję jest widoczna globalnie. Zachowanie jest jak najbardziej prawidłowe. Funkcja <code>modify()</code> operuje nie na kopii, ale na referencji do obiektu. Obie referencje: globalna <code>$janusz</code> oraz lokalna <code>$object</code> są oddzielne, ale wskazują na dokładnie ten sam obiekt. Dlatego wykonanie operacji poprzez jedną z nich sprawi, że druga także zauważy zmiany.
Linia 101:
Pozostałe modyfikatory dostępu to '''protected''' oraz '''private'''. Pierwszy z nich dotyczy dziedziczenia, dlatego zajmiemy się nim później, a tymczasem przyjrzymy się drugiemu z nich. Mówi on, że dane pole lub metoda jest prywatnym elementem klasy. W praktyce oznacza to, że odwołać się do niego możemy wyłącznie z poziomu innej metody w naszej klasie, a z zewnątrz jest on niedostępny. W naszych przykładach będziemy stosować konwencję, w której prywatne elementy będą posiadać nazwy zaczynające się od podkreślenia.
 
<sourcesyntaxhighlight lang="php" line><?php
class SomeClass
{
Linia 121:
// błąd, próba dostępu do pola prywatnego!
echo $someObject->_field;
</syntaxhighlight>
</source>
 
Powyższy skrypt się nie wykona. W ostatniej linijce PHP zgłosi nam błąd, jakim jest próba dostania się do prywatnego elementu klasy spoza obiektu. Jednocześnie działa odwołanie umieszczone wewnątrz metody <code>getField()</code>. Taki rodzaj metody zwie się po angielsku ''getter'', a oprócz niego mamy też ''setter'', który pozwala ustawić wartość określonemu polu. Jest to jedna z konwencji stosowanych w hermetyzacji, w myśl której klasa nie powinna, poza szczególnymi wyjątkami, zawierać publicznych pól. Jeżeli użytkownik ma mieć dostęp do jakiegoś pola, musi to robić za pośrednictwem dodatkowych metod, czyli ''getterów'' i ''setterów''. Zauważmy, że takie podejście pozwala nam tworzyć pola dostępne publicznie tylko do odczytu bez możliwości ich modyfikacji. Wystarczy zadeklarować je jako prywatne i stworzyć publiczny ''getter'', bez ''settera''. Przepiszmy zatem naszą klasę '''Person''' zgodnie z regułami hermetyzacji, ograniczając nieco dostęp:
 
<sourcesyntaxhighlight lang="php" line><?php
class Person
{
Linia 166:
} // end getFullName();
} // end Person;
</syntaxhighlight>
</source>
 
Teraz pola <code>$_name</code> oraz <code>$_surname</code> są prywatne, a ich wartość można odczytać wyłącznie poprzez ''gettery'' <code>getName()</code> oraz <code>getSurname()</code>. Dostępne są także analogiczne ''settery'' <code>setName()</code> oraz <code>setSurname()</code>, jednak zauważmy, że w praktyce można je wywołać tylko raz. Oznacza to, że gdy raz ustawimy danemu obiektowi nazwisko, nie jesteśmy w stanie go już później zmienić, gdyż metoda będzie zawsze zwracała wartość '''false''', odmawiając modyfikacji. Jest to typowy przykład kontroli dostępu i ma on szczególne znaczenie w dużych aplikacjach liczących sobie setki klas, gdzie umożliwia to wymuszenie stosowania się do określonych konwencji i pomaga zapobiegać bałaganowi w kodzie.
Linia 178:
Zacznijmy od napisania klasy głównej. Programista będzie mógł dodawać do niej różne ładowarki, a ona będzie udostępniała wczytaną konfigurację pozostałej części skryptu. Opcje konfiguracyjne będą wczytywane ''leniwie'', tj. gdy zajdzie taka potrzeba. Domyślnie ładowarka będzie jedynie kolejkowana w tablicy <code>$_awaitingLoaders</code>. Dopiero przy próbie odczytu nieistniejącej opcji, skrypt będzie prosić kolejne ładowarki o wczytanie swojej części konfiguracji, dopóki nie trafi na taką, która wczyta to, czego potrzebujemy.
 
<sourcesyntaxhighlight lang="php" line><?php
 
class Config
Linia 211:
} // end addLoader();
} // end Config;
</syntaxhighlight>
</source>
 
Pojawił się tu nowy element składni: <code>public function addLoader(ConfigLoader $loader)</code> - przed nazwą zmiennej pojawiła się nazwa obiektu. Jest to jedno z kolejnych udoskonaleń programowania obiektowego w PHP, czyli określanie typów argumentów. Działa ono zarówno w metodach, jak i funkcjach i mówi, że dany argument może przyjąć wyłącznie obiekt klasy '''ConfigLoader'''. Próba podania dowolnego innego rodzaju wartości zakończy się błędem. W PHP określanie typów argumentów ograniczone jest wyłącznie do klasy, a od PHP 5.1 również do tablic (typ '''Array'''). Nie można wymuszać typów skalarnych (np. liczby całkowite) ani zasobów. W naszym przypadku daje to nam pewność, że programista nie będzie próbował nakarmić naszego systemu konfiguracji jakimiś dziwnymi danymi, które mogłyby doprowadzić do błędu.
Linia 219:
Napiszmy teraz ładowarkę, która będzie wczytywać opcje z pliku INI:
 
<sourcesyntaxhighlight lang="php" line><?php
 
class ConfigLoader
Linia 241:
} // end load();
} // end ConfigLoader;
</syntaxhighlight>
</source>
 
Tu również zastosowaliśmy hermetyzację. Można, a nawet trzeba ustawić nazwę pliku, który chcemy odczytać, jednak nie musi być ona później dostępna, dlatego pominęliśmy całkowicie ''getter''. Metoda ''load()'' wywoływana przez naszą klasę '''Config''' musi zwrócić tablicę z opcjami wczytanymi z pliku.
Linia 247:
To wszystko, spójrzmy teraz, jak wykorzystać nasz system w praktyce:
 
<sourcesyntaxhighlight lang="php" line><?php
require('./Config.php');
require('./ConfigLoader.php');
Linia 270:
echo $config->get('website_name');
echo $config->get('session_time');
</syntaxhighlight>
</source>
 
Naszemu systemowi brakuje wciąż parę rzeczy. Przykładowo, można by pokusić się o zrobienie różnych rodzajów ładowarek: z plików, z bazy danych itd., lecz do tego potrzebna jest nam znajomość dziedziczenia. Ponadto przydałoby się, aby system potrafił odpowiednio raportować błędy. Póki co, jeśli pomylimy się w nazwie pliku konfiguracji, dowiemy się o tym dopiero po wnikliwym śledztwie, ponieważ jedynym śladem w ładowarce jest zwrócenie pustej tablicy. Odpowiednie mechanizmy poznamy w dalszej części podręcznika.