7 stycznia 2020

Kurs PHP – programowanie obiektowe (tutorial)

Kategoria: Programowanie
Tagi: php,
Autor: Paweł Mansfeld

Kurs PHP – programowanie obiektowe (tutorial)

Chcąc nauczyć się PHP mamy do wyboru korzystanie ze starych książek, które uczą starego stylu programowania PHP lub z nowych, które szczegółowo rozwodzą się nad nowościami PHP7 i nadchodzącej wersji PHP 8, które nie są istotne z punktu widzenia osoby uczącej się PHP od podstaw. Ten artykuł będzie stale aktualizowanym kursem obiektowego PHP od podstaw.

Dla kogo jest ten kurs?

Dla osób, które jakieś podstawy programowania mają opanowane. Nie będę tłumaczył elementarnych pojęć ale też postaram się niczego nie pomijać. Jest to kurs autorski, oczywiście posiłkuję się wieloma książkami i konwencjami w nich zawartymi. Polecam częste powracanie do tego jedynego w swoim rodzaju kursu PHP opublikowanemu na blogu. Kurs ma zachęcić do dalszej nauki i pokazać, że obiektowość nie jest czymś czego trzeba się bać. Tym bardziej w nowoczesnym PHP.

Dlaczego obiektowy kurs programowania a nie po prostu kurs programowania? Z bardzo prostego powodu, gdziekolwiek zetkniesz się z kodem PHP (czy to w pracy w korporacji, w małej agencji czy samodzielnej pracy nad cudzymi projektami) będzie on napisany zgodnie z OOP. Dobrze jest przywyknąć do składni i sposobu myślenia osoby programującej obiektowo. Będziesz w stanie rozumieć kod w stu procentach i nawet jeżeli z jakiegoś powodu chcesz pisać kod strukturalny taka wiedza Ci się przyda.

Po skończeniu tego kursu nikt Ci nie zabrania pisać kodu proceduralnego, sam pisałem wiele lat kod proceduralny i szkody żadnej nikomu (ani sobie) nie wyrządziłem. Uważam jednak, że każdy profesjonalista powinien znać (niekoniecznie zawsze używać) konwencje programowania obiektowego i paradygmaty rządzące nim. Czytaj więcej o zaletach i wadach programowania obiektowego.

Spis treści – kurs PHP

Ósemka to okrągła liczba w świecie Informatyki, dlatego kurs ten będzie podzielony na 8 rozdziałów.

  1. PHP – podstawy programowania
  2. Paradygmat programowania obiektowego PHP < jesteś tutaj
  3. Bazy danych SQL – PDO API
  4. Interfejsy API i integracje
  5. Wzorce projektowe i frameworki
  6. Wydajność
  7. Skalowalność
  8. Bezpieczeństwo

1. Paradygmat programowania obiektowego

Za pomocą zwykłych funkcji i zmiennych można napisać dowolną funkcjonalność. Cały serwis Facebook w obecnej postaci można napisać za pomocą przypisań, pętli warunkowych i pętli while. Problem w tym, że taki kod będzie trudny do rozbudowy w zespole programistów. a najmniejsza zmiana będzie powodowała nieracjonalny koszt czasowy i konieczność rozwiązywania coraz to nowych i banalnych problemów.

W warunkach kiedy kod będzie utrzymywany przez kilka osób lub w długim okresie czasu opłaca się wykorzystać coś co nazywamy paradygmatem programowania obiektowego czyli takim zestawem profesjonalnych reguł, które porządkują to jak tworzy się kod i rozwiązuje się najczęstsze problemy.

Nagrodą, i mocną stroną programowania obiektowego jest hermetyzacja, to ona organizuje i spaja ze sobą funkcje i wartości, które są obiektem – niezależnym (ale komunikującym się z innymi) bytem, który do pewnego stopnia reprezentuje dane pojęcie w programie i jest w pewnym stopniu niezależny od swojego środowiska.

Kod proceduralny można także organizować i grupować np.:

  • za pomocą pewnej konwencji nazewnictwa dodawać przedrostki do nazw zmiennych i funkcji,
  • przechowywać pewne dane w tablicach aby trzymać je w jednym miejscu,

Programowanie obiektowo rozwiązuje ten problem z pudełka – nasze zmienne to pola a funkcje to metody, które należą do zazwyczaj sensownie nazwanej klasy.

Czym jest klasa?

Klasa to taki szablon do tworzenia egzemplarzy obiektów czyli rzeczy, reprezentujące jakieś pojęcia i procesy w naszym programie. Metody to funkcje należące do obiektu a własności to jego zmienne.

Przykład:

Klasa o nazwie Użytkownik, ma zdefiniowane metody dodajDoZnajomych, usunUzytkownika i pola takie jak imie, nazwisko, data urodzenia. Dzięki temu obiekt Jana Kowalskiego może wykonać funkcję dodajDoZnajomych i obiektowi można przypisać wartości w własnościach: “Jan”, Kowalski” 01.01.2001″.

Deklarowanie klas w PHP

class User
  {
   public $name;
   public $email;
   public function __construct($name) {
   $this->name = $name;
   return true;
   }
   public function publish($post) {
   return true;
   }
 }

W taki sposób stworzyliśmy własną klasę. Kod ten zapisujemy pod nazwą User.php. O tym dlaczego nazewnictwo jest istotne przeczytasz w dalszych rozdziałach.

Klasa User posiada własność $name i $email oraz metody __construct() oraz publish(). W kodzie widać jeszcze zmienną $this. To zmienna odnosząca się do własnego obiektu.

Uwagę zwraca podwójny znak podkreślenia w nazwie __construct. Podwójne podkreślenie to cecha charakterystyczna metod magicznych, nie tylko konstruktor posiada taki wyróżnik. Na ten moment musisz tylko wiedzieć, to, że konstruktor to metoda automatycznie wywoływana przy tworzeniu obiektu.

Konstruktor ma swoje przeciwieństwo – destruktor, którego składnia wygląda tak: __destruct(); i jest wywoływany przy usuwaniu obiektu. Jeżeli coś jest niejasne, nie przejmuj się – działanie zobaczysz w praktycznym przykładzie a ten nazwy wejdą Ci szybko w nawyk.

Tworzenie obiektów w PHP

Tworzenie obiektów jest możliwe dzięki słowu kluczowemu new. Wykorzystajmy przykład klasy z poprzedniego punktów i stwórzmy obiekt na jej podstawie.

include 'User.php';
$test = new User('Test');

I to wszystko,w ten sposób stworzyliśmy obiekt klasy User. Zwróć uwagę, że jako argument podaliśmy nazwę tak jak wymagał tego konstruktor (parametr $name).

Za pomocą być może już znanej Ci funkcji var_dump() można teraz zobaczyć co zawiera stworzony obiekt. Tag <pre> dodajemy dla czytelniejszego formatowania.

echo "<pre>";
var_dump($test); 
echo "</pre>";

Dowiadujemy się, że jest to obiekt klasy User i ile ma jakich własności:

Wynik funkcji var_dump na testowym obiekcie

Autoload PHP (automatyczne ładowanie)

W dużych aplikacjach dołączanie “na sztywno” plików przechowujących definicje klas może być uciążliwe. Pewnie zastanawiasz się jak to “zautomatyzować”. Istnieje bardzo prosty mechanizm o kreatywnej nazwie: Automatyczne ładowanie (Autoload). Nasze dwie linijki odpowiedzialne za włącznie pliku User.php i stworzenie obiektu w pliku Test.php będzie wyglądał teraz tak (po usunięciu var_dump()):

function __autoload($classname) {
    include strtolower($classname) . '.php';
 } 
$test = new User('Test');

Wykorzystywanie obiektu

W przypadku kiedy przypisujemy do zmiennej ciąg znaków, można go bardzo łatwo wyświetlić za pomocą: echo $text; . A jak jest w przypadku pobierania własności obiektów?

echo $test->name;

Takie wyrażenie można spokojnie konkatenować jak w przypadku zwykłej zmiennej. Najpierw podajemy zmienną obiektu a potem za pomocą operatora obiektowego (strzałki, która jest charakterystyczna dla PHP) podajemy właściwość, którą chcemy odczytać.

Kiedy chcemy wywołać metodę po prostu piszemy:

$test->publish("argument");

Jeżeli w klasie definiujemy argumenty, muszą być one później podawane przy wywoływaniu – obowiązuje tutaj dokładnie taka sama zasada jak przy deklarowaniu i wywoływaniu funkcji. Jeżeli funkcja nie przyjmuje argumentów należy otworzyć i zamknąć nawias: $test->publish();

Własności i metody statyczne w PHP

Nauczyliśmy się przed chwilą, że klasy tworzymy po to aby generować na ich podstawie obiekty. Czasem jednak tworzenie obiektu nie jest potrzebne by wykorzystać jakąś metodę. Do tego służą własności i metody statyczne.

Kiedy używamy metod statycznych? Kiedy ich działanie nie zmienia się w zależności od konkretnych obiektów. Może to być metoda konwertująca jednostki prędkości w klasie Samochod lub metoda pobierająca aktualne kursy walut z API – nie potrzebujemy żadnego obiektu aby zmienić daną wartość, ponieważ zasada działania tej metody jest przewidywalna i równie dobrze mogła by to być jakaś zwykła funkcja – nie powiązana z żadną klasą.

class User
 {
  public $name;
  public $email;
  public $language;
  public static function getUsersByLanguage($language) {
 $user_list = "Jakieś dane pochodzące z odpytania bazy lub API związane z parametrem $language";
  return $user_list;
  }
 }

Aby wywołać funkcję statyczną należy użyć operatora :: (podwójna kropka, w PHP zabawnie nadano jej nazwę z języka hebrajskiego PAAMAYIM_NEKUDOTAYIM). Użyjmy kodu poniżej (pamiętaj, że do jej wywołania nie trzeba wcale wywoływać obiektu):

 $polish_users = User::getUsersByLanguage('Polish'); 

Metody w ten sposób wywoływane musza być zadeklarowane jako statyczne. Przykład mówi sam za siebie, nie potrzebujemy tworzyć obiektu aby zwrócić interesujące nas wartości. Takie metody można łatwo rozpoznać po tym że nie odwołują się obiektu za pomocą wyrażenia $this. Metody statyczne często można znaleźć w bibliotekach i frameworkach.

Namespace – przestrzenie nazw

Jeżeli korzystamy z dodatkowych bibliotek lub cudzych skryptów, może się zdarzyć, że dwie różne klasy będą miały identyczne nazwy. Aby uniknąć kolizji w PHP wprowadzono (tak jak w innych obiektowych językach programowania) przestrzenie nazw.

Za pomocą słowa kluczowego namespace deklarujemy przestrzeń nazw.

Jeżeli naszą klasę Users będziemy chcieli przypisać do przestrzeni accounts, kod deklarujący klasę w pliku User.php będzie musiał być poprzedzony specjalną instrukcją:

namespace accounts; 
class User{
...

Jeżeli będziemy wtedy użyć tej funkcji, wykorzystamy operator przestrzeni nazw czyli lewego ukośnika:

$user = new \accounts\User();

Dziedziczenie w PHP

Dziedziczenie to jedna z relacji pomiędzy klasami jaką możemy spotkać w świecie programowania obiektowego. Już sama nazwa “dziedziczenie” może nas naprowadzić na czym to polega. Mówi się, że klasa dziedziczy po innej klasie lub ją rozszerza. Klasa która dziedziczy po innej klasie przyjmuje wszystkie cechy klasy nadrzędnej i może jednocześnie dodać swoje “cechy” lub zmodyfikować istniejące. Mówimy wtedy, że klasa się “specjalizuje”. Dziedziczenie pozwala wykorzystać istniejące klasy i uniknąć powtarzania pisania już raz napisanego bądź bardzo podobnego kodu. Jak zwykle najlepiej pokazać to na przykładzie.

W dwóch klasach potomnych będziemy dziedziczyć po klasie User a następnie napiszemy specjalizowaną implementację metody login().

User.php:

class User
  {
   public $name;
   public $email;
   public function __construct($name) {
   $this->name = $name;
   return true;
   }
   public function login() {
   return true;
   }
  }

RegisteredUser.php:

class  RegisteredUser  extends User
 {
  public function login() {
  echo "Logowanie użytkownika ".$this->name." do strefy użytkowników zarejestrowanych…";
  }
 }

Admin.php

class Admin extends User
 {
  public function login() {
   echo "Logowanie użytkownika ".$this->name." do strefy administratora…"; 
  }
 }

Pamiętaj, że metody specjalizowane, muszą przyjmować takie same argumenty jak pierwotna metoda z klasy nadrzędnej. W tym przypadku nie przyjmują żadnych argumentów.

Wskazywanie typów parametrów

Utwórzmy klasę Post, która przyda nam się do poznania tego jak się przekazuje obiekty do metod obiektowych.

class Post
 {
  public $postTitle;
  public $postContent;
  public $postMeta; 
 }

Zmodyfikujemy teraz metody publish aby argumenty przyjmowały tylko obiekty klasy Post. Nazwę klasy wpisujemy w definicji przed nazwą argumentu:

public function publish(Post $post) {
 return true;
}

Polimorfizm

Polimorfizm (lub inaczej: wielopostaciowość) to kolejny filar programowania obiektowego. Polega on na tym, że obiekt klasy potomnej może być traktowany zarówno jako instancja klasy podrzędnej jak i nadrzędnej. Aby to zrozumieć wystarczy wklepać poniższy kod:

$user = new Admin('Jan');
if($user instanceOf User) {
 echo $user->name . " należy do klasy User\n";
}
if($user instanceOf RegisteredUser) {
 echo $user->name . " należy do klasy  RegisteredUser \n";
}
if($user instanceOf Admin) {
 echo $user->name . " należy do klasy Admin\n";
}

Uzyskamy wtedy coś takiego:

Wynik instrukcji instanceOf

Referencja

Obiekty zachowują się inaczej niż proste typy danych. W przypadku przypisania $a = $b otrzymujemy dwie zmienne które można traktować całkowicie niezależnie. W przypadku kodu:

$user1 = new User("Jan");
$user2 = $user1;
$user2 -> name = 'Maja';

Po odczytaniu własności:

echo 'Mamy następujących użytkowników: '
  . $user1->name . ' i '
  . $user2->name;

Okaże się, że informacja wygląda tak:

Mamy następujących użytkowników: Maja i Maja 

Jak to możliwe? Przy “przypisaniu” $user2 = $user1; nie skopiowaliśmy zawartości $user1. Co się zatem stało? Utworzyliśmy tylko zmienną, która przechowuje referencję, za pomocą której PHP odwołuje się do tego samego obiektu.

Jeżeli już jesteśmy przy operatorach, to za pomocą:

  • == możemy sprawdzić, czy obiekty przynależą do tej samej klasy i mają te same wartości własności,
  • === możemy sprawdzić czy zmienne są referencjami do tego samego obiektu.

Bardzo ważne jest zrozumienie tego, że zmienne do których przypisano obiekty zawsze przechowują referencję do danego obiektu. Tak samo, jeżeli jakiś obiekt staje się argumentem funkcji, działa ona bezpośrednio na tym obiekcie.

Jeżeli chcesz działać na odizolowanej instancji danego obiektu (czyli takiej jakby kopii) trzeba użyć słowa kluczowego clone.

 $user1 = new User("Jan");
 $user2 = clone $user1;
 $user2 -> name = 'Maja';

I teraz kiedy wyświetlimy dane (user1 i user2), okaże się, że:

 Mamy następujących użytkowników: Jan i Maja

Jeszcze jedno, pamiętasz magiczną metodę __construct() i __detruct()? No to mamy kolejną: __clone(), która jest wywoływana przy klonowaniu obiektu. Za jej pomocą można wykonać dodatkowe czynności lub zablokować wykonanie tego procesu.

Mechanizm działania referencji powoduje, że metody działające na rzecz jakiegoś obiektu nie muszą go zwracać. Możemy zbudować tzw. płynny interfejs czyli wywoływanie łańcuchu metod.

$user->setEmail("jan@oopoczta.pl")->setStatus('Unconfirmed');

Modyfikatory dostępu: public, private, protected

W obiektowych językach programowania mamy coś takiego jak modyfikatory dostępu. Do tej pory korzystaliśmy z modyfikatora public a są jeszcze private i protected.

  • public (publiczny) pozwala na dostęp do składowych klasy także spoza tej klasy,
  • private (prywatny) powoduje, że metody i pola są widoczne tylko wewnątrz klasy (próba dostępu do pola lub metody spoza niej zakończy się błędem)
  • protected (chroniony) – są chronione z zewnątrz tak jak private ale z tym wyjątkiem że dostęp mają do nich także podklasy powstałe wskutek dziedziczenia.

Tak jak mówi się że “każdy powinien wiedzieć tyle ile musi”, tak pola i metody powinny być chronione przed dostępem z zewnątrz jeżeli dostęp do nich nie jest potrzebny – co najmniej modyfikatorem protected.

Metody get i set

Metody __get() i __set() to kolejne magiczne metody, które są wywoływane kiedy odczytujemy lub przypisujemy wartości do konkretnych pól. Za pomocą tych funkcji można zmieniać wartości w polach private i protected. ten przykład ilustruje działanie takich metod za pomocą odwołań do nieistniejącej własności.

Przykład:

class User
 {
     public $name;
     public function __construct($name) {
         $this->name = $name;
         return true;
     }
     protected $data = array();
     public function __get($property) {
         return $this->data[$property];
     }
     public function __set($property, $value) {
         $this->data[$property] = $value;
     return true;
     }
 }
$user = new User("Jan");
$user -> lastName = "Kowalski";
echo "Imię i Nazwisko: ".$user->name." ".$user->lastName;

Tak by wyglądał wstęp do obiektowego programowania w PHP. W kolejnym wpisie z tej serii mamy bazy danych wraz z rozszerzeniem PDO. Nauczysz się jak tworzyć instrukcje preparowane, które zapewniają o wiele większą czytelność kodu, są zgodne z paradygmatem obiektowym oraz zapewniają dużo lepsze bezpieczeństwo.

Podsumowanie

Niniejszy, stale aktualizowany kurs PHP to dobry wstęp dla osób, które chciałby by poznać chociaż w podstawowym stopniu programowanie obiektowe. Zaglądaj tu regularnie a ewentualne pytania pisz w komentarzu.

Źródła

https://www.php.net/manual/en/language.oop5.php

Oceń artykuł na temat: Kurs PHP – programowanie obiektowe (tutorial)
Średnia : 4.7 , Maksymalnie : 5 , Głosów : 7


 

Odpowiedz lub skomentuj

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *




Komentarze

Sf

15 czerwca 2020

Mam pytanie, jak zrobić coś w stylu twojej aplikacji na smartfon, aby otwierało za pomocą chrome moja stronę?

Paweł Mansfeld

15 czerwca 2020

To jest PWA. Pisałem o tym tutaj: https://mansfeld.pl/webdesign/tworzenie-progresywnej-aplikacji-webowej-pwa/. Tworzymy plik manifest według przykładu i dodajemy kod JS. Podlinkowałem tam narzędzie do generowania kodu w zależności od chęci uzyskania konkretnego zachowania. Daj znać jakby z czymś był problem.

Tomasz

21 lipca 2020

Czy w ostatnim przykładzie w deklaracji klasy User nie brakuje właściwości: public $lastName; ?

Paweł Mansfeld

21 lipca 2020

No właśnie nie, bo odwołujemy się do nieistniejących własności. W deklaracji jest zmienna $property, która umożliwia nam na tworzenie takich odwołań - na tym polega przykład.

 

Wykryto brak połączenia z Internetem.