Szykuje się – już piąta z kolei – nowa wersja PHP. Niewiele wiadomo, ale różne źródła podają różne informacje. Oto pierwsza w Polsce tak dokładna analiza PHP 5.
PHP5 nie jest jeszcze oficjalnie oddane do użytku. Aktualnej wersji można używać ale zawiera ona sporo błędów. Wiele elementów jest podobnych albo nawet takich samych w PHP4 i PHP5, ale oczywiście są różnice i tospore.
To, co przedstawię w tym artykule działa w obecnej wersji, ale do wypuszczenia PHP5 oficjalnie może się dużo zmienić. Obecną wersje możesz ściągnąć z i sprawdzić przykłady które zaprezentuje. W PHP5 zmieniono stare elementy jak i dodano nowe, tak np. Zmieniono model obiektów tudzież dodano nowe elementy. Wprowadzono pola nazw oraz wyjątki. Największy jednak nacisk położone na programowanie obiektowe w którym zaszły największe zmiany, które można porównać do Javy i C++.
Spis Treści:
– unifikowane konstruktory
– destruktory
– obiekty jako referencje
– klonowanie obiektów
– definiowanie interfejsów
– określanie metod i właściwości jako PPP czyli:
– Private
– Public
– Protected
– Klasy abstrakcyjne
– elementy statyczne
– __call
– __set i __get
– rzutowanie typów
– wyjątki
– definiowanie własnej obsługi wyjątków
– pola nazw (ze źródeł nie oficjalnych)
– zagnieżdżone pola nazw
– __autoload
– final
– stałe
– dereferencja obiektów
– instanceof
– wartość domyślna parametrów z referencją
Konstruktory:
W PHP4 konstruktory musiały nazywać się tak jak dana klasa, czyli np. konstruktor klasy „jakasklasa” nazywał się „jakasklasa”. W PHP5 konstruktory są unifikowane, czyli zawsze mają taką samą nazwę: __construct(), tak więc konstruktor klasy „jakasklasa” będzie nazywał się „__construct”. Należy pamiętać o podwójnym znaku podkreślenia na początku nazwy. W przykładzie wygląda to tak:
<?php class samochod { var $akcja; function __construct() { |
da nam to w wyniku:
Silnik start |
Działanie jest bardzo proste destruktor uruchamia się wraz z definicją obiektu.
Destruktory:
W PHP4 destruktory nie były dostępne, przez co trzeba było się czasami zdrowo nakombinować aby w razie potrzeby zasymulować taki pseudo destruktor. Na szczęście w PHP5 destruktory są i nazywają się: __destruct(). Oczywiście w każdej klasie ich inicjalizacja wygląda tak samo. Rozwijają nasz przykład możemy wykorzystać destruktor do zatrzymania silnika:
<?php class samochod { var $akcja; function __construct() { |
da nam to w wyniku:
Silnik start Silnik stop |
Tutaj również działanie jest proste, destruktor uruchamia się wraz z „niszczeniem” obiektu, w tym wypadku kiedy skrypt kończy działanie.
Obiekty jako referencje:
W PHP4 można było używać pewnego rodzaju referencji, używając znaku & przy zmiennych, w wyniku czego nie były kopiowane. Jest to coś w rodzaju wskaźników. Jeżeli chodzi o obiekty w PHP5, to w wypadku przypisywania obiektu jest automatycznie tworzona referencja do tego obiektu. Znaczy to że gdy przypisujemy obiekt do zmiennej to tworzymy do niego referencję. Drobny przykład powinien wyjaśnić to działanie w tym wypadku:
<?php class samochod { var $model; function pokaz() { echo $this -> model . „<br>”; } } $merc = new samochod; $merc -> model = „clk”; $ford -> model = „mustang”; $ford -> pokaz(); |
Da to w wyniku:
clk mustang mustang |
Jak widać nie zostały utworzone dwie kopie tego samego obiektu, tylko została utworzona referencja i to bez potrzeby użycia &.
Klonowanie obiektów:
Jako że obiekty są przekazywane automatycznie przez referencję, t może zajść potrzeba ich kopiowania aby tworzyć niezależne od siebie kopie, co może przydać się przy traktowaniu obiektów jako swoistych „magazynów” do czego część z was przyzwyczaiła się w PHP4. Aby sklonować obiekt używamy predefiniowanej metody __clone(). Działa to na zasadzie przypisania, gdzie z lewej strony umieszczamy obiekt który chcemy sklonować wywołując __clone(), a z lewej zmienną do której ma być zapisana kopia obiektu. W praktyce wygląda to tak:
<?php class samochod { var $model; function pokaz() { echo $this -> model . „<br>”; } } $merc = new samochod; $merc -> model = „clk”; $ford -> model = „mustang”; $ford -> pokaz(); |
Da to w wyniku:
clk mustang clk |
Jak widać obydwie kopie są od siebie nie zależne, co pozwala trzymać kopie różnych zmiennych w różnych obiektach
Definiowanie interfejsów:
Zarówno w PHP4 jak i w PHP5 możliwe jest dziedziczenie tylko z jednej klasy. Zdarzały się przypadki gdzie to ograniczenie skutecznie plątało projekt kodu tworząc np. zbyt poplątane drzewo w hierarchii klas. Ale w PHP5 jest z tego wyjście. Możliwe jest definiowanie interfejsów w których w rzeczywistości nie ma żadnych metod. W interfejsie definiuje się tylko nazwy i parametry metod, które mogą być potem użyte w klasie do której zaimplementujemy dany interfejs. W odróżnieniu od zwykłego dziedziczenia możemy do klasy przypisać tyle interfejsów ile tylko nam się podoba, z tym że w klasie muszą być zdefiniowane te metody które występują w interfejsach, tym razem w całości. Do klasy dołączamy interfejsy za pomocą słowa kluczowego implements, oddzielając kolejne interfejsy przecinkami. W praktyce wygląda to tak:
<?php interface silnik { function start($samochod); function stop($samochod); } interface bagaznik { function otworz(); function zamknij(); } class samochod implements silnik,bagaznik { function start($samochod) { echo „Samochód $samochod włączony.”; } function stop($samochod) { echo „Samochód $samochod wyłączony.”; } function otworz() { echo „Bagażnik otwarty.”; } function zamknij() { echo „Bagażnik zamknięty.”; } } $mojsamochod = new samochod; |
Powyższy kod da w wyniku:
Samochód ford włączony.Bagażnik otwarty. Bagażnik zamknięty.Samochód ford wyłączony. |
Zapytacie po co to… Dzięki temu kod jest bardziej czytelny. W definiowanych interfejsach widać na pierwszy rzut oka co jest, a co jest w klasie widać już na początku jej deklaracji dzięki interfejsom. Pozwala to utrzymać ład w kodzie. W powyższym kodzie już po pierwszych dwóch linijkach deklaracji klasy wiadomo jakie będzie ona miała metody i czego te metody potrzebują. Nie ważne jak działają, może to wyjaśniać komentarz, ważne że można się do nich odwołać i jest to jasne w jaki sposób.
Określanie metod i właściwości jako PPP czyli:
W PHP4 do każdej metody i właściwości można było dostać się spoza obiektu. Można powiedzieć że one wszystkie były określone domyślnie jako publiczne. W PHP5 wprowadzono 3 modyfikatory które zmieniają postać rzeczy. Dzięki im możemy ustawić dostęp do danej metody lub właściwości. Te modyfikatory to Private, Public i Protected.
Private: Dostęp do metody lub właściwości określonej jako Private mają tylko metody z tej samej klasy, czyli w klasach potomnych i z zewnątrz nie ma dostępu.
Public: Do metody lub właściwości określonej jako Public dostęp jest możliwy zarówno w klasach potomnych jak i z zewnątrz obiektu.
Protected: Do metody lub właściwości określonej jako protected mają dostęp tylko metody z tej samej lub potomnej klasy, czyli z zewnątrz nie ma dostępu. W praktyce wygląda to tak:
<?php class samochody_zapisz { private $ford; public $audi; protected $mercedes; private function zapisz_ford($model) { class samochody_pokaz extends samochody_zapisz $this -> pokaz_ford(); //dziala $zapisz = new samochody_zapisz; //$zapisz -> zapisz_ford(’mustang’); //nie dziala //$pokaz -> zapisz_ford(’mustang’); //nie dziala //$pokaz -> pokaz_ford(); //nie dziala $pokaz -> wyswietl(); //dziala |
Wyniku nie będę przedstawiał gdyż kod zawiera elementy błędne (umieszczone w komentarzach). Oczywiście kod z wykomentowanymi błędami zadziała.
Klasy abstrakcyjne:
Tego w PHP4 też nie było. Co to jest..? Można powiedzieć że abstrakcyjne elementy nie są przeznaczone bezpośrednio do użytku, tzn., jeżeli deklarujemy całą klasę, lub chociaż jedną metodę danej klasy jako abstrakcyjną nie będzie można utworzyć obiektu tej klasy, tzn. pojedynczego egzemplarzu. Przydaję się to w dziedziczeniu, ponieważ niektóre klasy tworzone są tylko po to, aby kilka innych mogło z nich dziedziczyć. Jak natomiast ma się sprawa co do dziedziczenia abstrakcyjnych metod, otóż jeżeli określamy metodę jako abstrakcyjną, tylko ją definiujemy, natomiast inicjalizacja tej metody musi odbyć się w klasie potomnej. Oznacza to że nie wszystkie metody w klasie abstrakcyjnej muszą być abstrakcyjne. W przykładzie wygląda to tak:
<?php abstract class silnik { protected $modelsamochodu; public function wlaczsilnik() { class samochod extends silnik $ford = new samochod; |
Wynik działania powyższego skryptu będzie następujący:
Silnik samochodu mustang włączony.Silnik samochodu mustang wyłączony. |
Jak widać jest czytelniej. Można było obie metody z klasy silnik ustawić jako abstrakcyjne, ale chciałem pokazać różnicę.
Elementy statyczne:
Elementów statycznych można używać jako tzw. metod i zmiennych klasy (class method i class variable). Dzięki temu można się do nich odwoływać bezpośrednio z poziomu klasy, tzn. bez potrzeby tworzenia obiektu danej klasy. Dostęp do nich możemy uzyskać poprzez „::”. Elementy statyczne mogą być inicjalizowane, tzn. mogą mieć wartość początkową. W praktyce wygląda to tak:
<?php class samochod { static public $model = 'mustang’; static public function przejazdzka($gdzie) { echo samochod::$model . '<br>’; |
Wynik działania tego skryptu będzie następujący:
mustang Jedziemy do Warszawa i wracamy. |
Dzięki temu możemy korzystać z zawartości klas bez tworzenia ich egzemplarzy.
__call:
Jest to specjalna metoda, którą możemy zdefiniować wewnątrz klasy. Służy ona do przechwytywania wywołań metod nie istniejący lub nie dostępnych. W wypadku gdy odwołasz się do takiej, zostanie użyta funkcja __call() (jeżeli została zdefiniowana) któraprzechwyci nazwę wywoływanej metody i listę jej parametrów w postaci tablicy. Metoda call posiada dwa parametry nazwę i parametry wywoływanej metody. Metodę __call() można wykorzystać do przeładowywania metod. Użycie metody __call() wygląda następująco:
<?php class samochod { function __call($nazwa, $parametry) { echo „Wywołałeś/aś metodę $nazwa”; } } $ford = new samochod; |
Wynik działania będzie następujący:
Wywołałeś/aś metodę jakasmetoda. |
Natomiast jeżeli chodzi o przeładowywanie to sprawa ma się równie prosto, przykładowe przeładowanie metody może wyglądać tak:
<?php class samochod { function __call($funkcja, $parametry) { if ($funkcja == 'otworz’) { switch ($parametry[0]) { case 'dzwi’: $this -> otworzdzwi(); break; case 'bagaznik’: $this -> otworzbagaznik(); break; default: echo 'Nic takiego nie otworze’; break; } } else { echo 'Nie ma takiej metody’; } } function otworzbagaznik() { echo 'Bagażnik otwarty’; } function otworzdzwi() { echo 'Dżwi otwarte’; } } $ford = new samochod; |
Wynik działania będzie taki:
Dżwi otwarte.Bagażnik otwarty.Nic takiego nie otworze.Nie ma takiej metody. |
Jak widać jest to prosty sposób na przeładowywanie metod podobnie jak w innych językach programowania.
__set i __get:
Tutaj sprawa ma się podobnie do __call(), z tą różnicą że __set() i __get() służą do przechwytywania właściwości (zmiennych) w obiekcie. Pytanie po co aż dwie, a no po to żeby obsłużyć próbę zapisu i odwołanie się do danej właściwości, w metodach to nie było potrzebne. I tak __set() przechwytuje zmienne które próbujemy ustawić, posiada dwa parametry, pierwszy to nazwa zmiennej, a drugi to jej wartość. __get() natomiast przechwytuje odwołanie się do zmiennej i posiada jeden parametr którym jest nazwa zmiennej. Użycie __set() wygląda następująco:
<?php class samochod { function __set($nazwa, $wartosc) { echo „Próbujesz ustawić zmienną $nazwa na wartość $wartosc.”; } } $ford = new samochod; |
Wynikiem powyższego skryptu będzie:
Próbujesz ustawić zmienną model na wartość mustang. |
Możesz w ten sposób zapisywać wartości poprzez zmienne np. do odpowiedniej tablicy przy użyciu wcześniej zdefiniowanej metody który by wykonywała wewnątrz __set().
Jeżeli chodzi o __get() to sprawa ma się następująco:
<?php class samochod { function __get($nazwa) { echo „Próbujesz uzyskać dostęp do zmiennej $nazwa.”; } } $ford = new samochod; |
Skrypt zadziała następująco:
Próbujesz uzyskać dostęp do zmiennej model. |
Tego natomiast można użyć do odczytywania bardziej złożonych struktur, chociażby tablic, również przy użyciu odpowiedniej metody która by potrafiła to obsłużyć.
Rzutowanie typów:
Nie jest to może dosłownie takie rzutowanie typów jak w innych jeżykach programowania, ale coś z tej dziedziny. Możliwość jest taka, że można określić w definicji funkcji której klasy obiekt musi być podany jako argument, tzn. Podając listę parametrów w definicji, to przed konkretnym parametrem umieszczamy nazwę klasy której obiekt może być danym parametrem. Przykładowo wygląda to tak:
<?php class samochod { public $model; } class garaz { public function parkoj(samochod $samochod) { echo „Samochód ” . $samochod -> model . ” został zaparkowany.”; } } $ford = new samochod; $garaz = new garaz; |
Wynikiem powyższego skryptu będzie:
Samochód mustang został zaparkowany. |
Niby co za różnica, ale pomaga w pisaniu programu i pozwala się wystrzec nie potrzebnych błędów, które zwłaszcza mogą być problemem podczas gdy kilku programistów pracuje przy jednym projekcie.
Wyjątki:
Wyjątki w PHP5, podobnie jak w innych językach programowania to dobra rzecz do obsługi błędów i nie przewidzianych sytuacji. Możemy sami określić co gdzie i jak ma być zrobione na wypadek nie przewidzianej sytuacji, czyniąc aplikację bardziej przystępną. Do obsługi wyjątków w PHP5 służą trzy słowa kluczowe: try, catch i throw. Obsługa wyjątków jest zapisana w predefiniowanej klasie Exception. Kod który chcemy przetestować umieszczamy w bloku try, a obsługę wyjątku w bloku catch. Przykładowa standardowa obsługa wyjątków wygląda następująco:
<?php class samochod { public $samochod; public function start($mojsamochod) { $ford = new samochod; try { |
W tym wypadku otrzymamy prawidłowy wynik, czyli:
Samochód mustang wystartował… |
Jeżeli natomiast podamy nieprawidłowy samochód, zostanie wygenerowany błąd:
<?php class samochod { public $samochod; public function start($mojsamochod) { $ford = new samochod; try { |
W takim wypadku został podany zły samochód, więc zostaje wygenerowany błąd:
Nie mam takiego samochodu. |
Definiowanie własnej obsługi wyjątków:
Tutaj sprawa ma się podobnie do zwykłej obsługi wyjątków. Chodzi o to że możemy rozszerzyć standardową obsługę wyjątków rozszerzając standardową klasę Exception o naszą, w której możemy użyć własnych meto do tej obsługi. Trzeba tylko pamiętać, aby w naszej klasie potomnej umieścić odpowiedni konstruktor, który będzie inicjalizował metodę exception() z klasy potomnej Exception, oraz należy zdefiniować metodę która zwróci komunikat błędu. W praktyce wygląda to tak:
<?php class nowewyjatki extends Exception { private $blad; function __construct( $blad ) { function getMessage() { |
Można również dodać metodę z wyjątków: getFile() która zwraca nazwę pliku w którym wystąpił błąd. Stosując powyższą klasę nasz poprzedni kod mógłby wyglądać następująco:
<?php class nowewyjatki extends Exception { private $blad; function __construct( $blad ) { function Komunikat() { class samochod public function start($mojsamochod) { $ford = new samochod; try { |
Da to w wyniku:
Nie mam takiego samochodu jest przyczyną błędu. W pliku: C:\apache\htdocs\php\index.php |
Na pierwszy rzut oka nie widać różnicy, ale takowa jest, ponieważ można dodawać więcej metod które będą obsługiwały różne wyjątki. Można polepszyć obsługę i wyświetlanie błędów dzięki wyjątkom które sami zdefiniujemy.
Pola nazw:
W php5 można używać pól nazw do grupowania klas lub funkcji. W oddzielnych polach nazw możemy deklarować np. różne klasy o tych samych nazwach. Może to być użyteczne np. gdy chcemy wykorzystać ten sam interfejs do wykonania różnych czynności. Przykładowe użycie pola nazw może wyglądać następująco:
<?php namespace motoryzacja { class samochod { function __construct() { echo „Klasa samochód z pola nazw motoryzacja została uruchomiona.”; } } function auto() { echo „Funkcja auto() z pola nazw motoryzacja została uruchomiona.”; } } $ford = new motoryzacja::samochod; |
Wynikiem działania powyższego kodu będzie:
Klasa samochód z pola nazw motoryzacja została uruchomiona. Funkcja auto() z pola nazw motoryzacja została uruchomiona. |
Jak widać można dzięki temu podzielić kod. Trzeba jednak uważać bo można przesadzić i kod stanie się mało czytelny.
Zagnieżdżone pola nazw:
Pola nazw w PHP5 można zagnieżdżać. Pozwala to tworzyć bardziej skomplikowane podziały kodu, co może pomóc w poprawieniu wyglądu i wyrazistości. Przykładowo pola nazw możemy zagnieżdżać w ten sposób:
<?php namespace motoryzacja:klasy { class samochod { function __construct() { echo „Klasa samochód z pola nazw motoryzacja:klasy została uruchomiona.”; } } } namespace motoryzacja:funkcje { function auto() { echo „Funkcja auto() z pola nazw motoryzacja:funkcje została uruchomiona.”; } } $ford = new motoryzacja:klasy::samochod; |
Wynik będzie podobny, czyli:
Klasa samochód z pola nazw motoryzacja:klasy została uruchomiona. Funkcja auto() z pola nazw motoryzacja:funkcje została uruchomiona. |
__autoload:
W PHP5 jest możliwość zadeklarowania funkcji __autoload(), która będzie automatycznie ładowała potrzebne klasy. Dzięki niej, możemy podzielić klasy w oddzielne pliki, a do programu zostaną załadowane tylko te, które będą aktualnie potrzebne, pozwala to oszczędzić miejsce, pamięć i sprawić że w kodzie pomimo dużej zawartości wciąż będzie panował ład. W praktyce wygląda to tak, na początek definiujemy plik z klasą:
<?php class samochod { function prezentuj() { echo „Prezentacja samochodu…”; } } ?> |
Teraz plik ze skryptem i definicją __autoload():
<?php function __autoload($klasa) { include_once(„$klasa.php”); } $ford = new samochod; |
Wynikiem działanie powyższego kodu będzie:
Prezentacja samochodu… |
Jak widać, skrypt sam załadował potrzebną klasę, dzięki czemu został utworzony obiekt. Dzięki include_once mamy pewność że przy tworzeniu następnego egzemplarzu danej klasy nie zostanie ona dołączona ponowni. Pozwala to znacznie uprościć pracę z wieloma klasami.
final:
W PHP5 zostało wprowadzone słowo kluczowe final. Po poprzedzeniu metody tym słowem nie będzie możliwe jej nadpisanie w klasach potomnych. Jeżeli zajdzie taka próba zostanie wygenerowany błąd. W przykładzie wygląda to tak:
<?php class silnik { final function silnikstart() { echo „Silnik uruchomiony”; } } ?> |
Teraz gdy jakaś klasa odziedziczy klasę silnik i będzie zawierała metodę silnikstart() zostanie wygenerowany błąd. To samo tyczy się wszystkich klas potomnych.
Stałe:
W PHP5 zostały wprowadzone stałe tzw. per-class, czyli stałe inicjalizowane za pośrednictwem klas. Wygląda to mniej więcej tak:
<?php class samochod { const MODEL = „mustang”; } echo „Model samochodu to: ” . samochod::MODEL . „.”; |
Da to w wyniku:
Model samochodu to: mustang. |
PHP5 zezwala na stosowanie pewnych wyrażeń do inicjalizacji stałych, ale przy wykorzystaniu stałych. Nie można definiować stałych których wartość jest ustalana w trakcie kompilowania. Można więc zainicjalizować jedne stałe stałymi, a następnie inną stałą wcześniej zainicjalizowanymi stałymi. W praktyce wygląda to tak:
<?php class samochod { const MARKA = „Ford”; const MODEL = „mustang”; const SAMOCHOD = MARKA . ” ” . MODEL; } echo samochod::SAMOCHOD; |
Da to w wyniku:
Ford mustang |
Dereferencja obiektów:
W PHP4 zwracanie obiektu jako wyniku funkcji i bezpośrednie się do niego odwołanie nie było możliwe. Takie działanie nazywa się dereferencja i w PHP5 jest możliwe. Dzięki temu można wykorzystać funkcję aby użyć obiektu wybranej klasy. W praktyce wygląda to tak:
<?php class ford { function start() { echo „Samochód ford uruchomiony.”; } } class mercedes function start($samochod) { start(’ford’) -> start(); |
Powyższy kod da w wyniku:
Samochód ford uruchomiony.Samochód mercedes uruchomiony. |
instanceof:
PHP4 wprowadza nowe słowo kluczowe „instanceof” dzięki któremu możemy sprawdzić czy dany obiekt jest egzemplarzem danej klasy,czy jest jej potomkiem oraz czy zawiera odpowiedni interfejs. W praktyce wygląda to tak:
<?php interface auto { function parkoj(); } class silnik class samochod extends silnik if ($ford instanceof samochod) { |
Da to w wyniku:
Powiązanie z klasą samochod.Powiązanie z klasą silnik.Powiązanie z interfejsem auto. |
Można to wykorzystać do sprawdzania kodu oraz hierarchii klas, wykorzystując konstrukcje warunkowe, tak jak to pokazano w przykładzie.
Wartość domyślna parametrów z referencją:
W PHP5 jest możliwość ustawiania domyślnych wartości dla parametrów funkcji które mają być przekazane przez referencję. Wygląda to tak:
<?php function samochod(&$marka=’brak’) { if ($marka == 'brak’) { die (’Podaj markę samochodu.’); } else { echo „Wszystko ok.”; } } samochod(); |
Działanie tego kodu da w wyniku napis:
Podaj markę samochodu. |
Proste a zarazem bardzo praktyczne. Pamiętaj że przez referencję można podać tylko zmienną.