Certyfikat ZendPHP

Obiektowe PHP

PHP zorientowane obiektowo

Kod zorientowany obiektowo działa wolniej niż kod proceduralny, ale ułatwia modelowanie i manipulowanie złożonymi strukturami danych. PHP wspiera programowanie obiektowe od wersji 3.0 i od tego czasu jego model obiektowy został znacznie rozszerzony i zreformowany. Nie zamierzamy uczyć programowania obiektowego, ale skupimy się raczej na implementacji PHP. Oczekuje się, że masz przynajmniej trochę doświadczenia w kodowaniu w PHP.

Wskazówka : Jest to jedna z trzech najważniejszych części egzaminu certyfikacyjnego

Deklarowanie klas i tworzenie instancji obiektów

Klasy deklarowane są za pomocą słowa kluczowego class.

< ?php
class ExampleClass
{
// kod klasy
}

Klasy można nazwać przy użyciu tych samych reguł, co zmienne. Twoje standardy kodowania określą konwencję, z której korzystasz. Aby utworzyć instancję obiektu z klasy, użyj nowego słowa kluczowego:

< ?php
$exampleObject = new ExampleClass();
// Jeśli nie przekazujesz parametrów konstruktora, możesz pominąć nawiasy, jeśli chcesz
$anotherObject = new ExampleClass;

Szczegóły zajmiemy się później, ale poniższa tabela podsumowująca pokazuje składnię i ograniczenia dotyczące dziedziczenia i cech

Pojęcie : Składnia : Ograniczenia

Dziedziczenie z klasy : class A extends A_Parent : Klasa może mieć tylko jednego rodzica
Interfejs dziedziczenia : Interface A extends B, C : Może dziedziczyć wiele interfejsów
Dziedziczenie z klasy abstrakcyjnej : Interface A extends B, C : Może dziedziczyć wiele interfejsów
Implementuje interfejsu : class A implements A_Interface : Klasa może implementować wiele interfejsów
Trait : class Foo { use A_trait; } : Klasa może wykorzystywać wiele traits

Przypisanie obiektu jest zawsze przez odniesienie. W poniższym przykładzie zauważ, że kiedy zmieniamy właściwość w skopiowanym obiekcie, oryginalny obiekt również się zmienia. W rzeczywistości dwie zmienne zajmują to samo miejsce w pamięci, ponieważ odwołanie jest wskaźnikiem do oryginalnych danych. Nie tworzymy całkowicie nowej kopii obiektu.

< ?php
$a = new stdClass();
$a->property = "Hello World";
// przypisanie obiektu jest przez odniesienie
$b = $a;
$b->property = "Assigned by reference";
// $a także się zmieniło, ponieważ $b jest wskaźnikiem do $a
var_dump($a);
/*
object(stdClass)#1 (1) {
["property"]=>
string(21) "Przypisany przez odniesienie"
}
*/

Klasy automatycznego ładowania

Klasy powinny zostać zdefiniowane przed ich użyciem, ale można użyć automatycznego ładowania w celu załadowania klas, gdy są one wymagane. W połączeniu ze standardami kodowania, takimi jak PSR4, które określają, gdzie PHP będzie szukać klasy, może to być niezbędna funkcja

Wskazówka : Nie będzie pytań o PSR4 podczas egzaminu Zend, ale standardy przedstawione przez grupę FIG są bardzo ważne w świecie PHP.

Automatyczne ładowanie w PHP odbywa się za pomocą funkcji spl_autoload_register(). Implementacja zgodna z PSR4 jest podana na stronie internetowej grupy PHP FIG, ale spójrzmy na prostszą demonstrację z podręcznika PHP2 na przykład:

< ?php
function my_autoloader($class) {
include 'classes/' . $class . '.class.php';
}
spl_autoload_register('my_autoloader');
// Lub przy użyciu anonimowej funkcji od PHP 5.3.0
spl_autoload_register(function ($class) {
include 'classes/' . $class . '.class.php';
});

Użycie spl_autoload_register() pozwala określić, jaką funkcję wywoła PHP, jeśli nie będzie w stanie załadować klasy. Możesz włączyć pliki do tej funkcji i zadeklarować klasę. Jeśli PHP nie będzie w stanie znaleźć klasy po uruchomieniu tej funkcji, wygeneruje błąd krytyczny.

Modyfikatory widoczności lub dostępu

Widoczność metody lub właściwości można ustawić, poprzedzając deklarację publiczną, chronioną lub prywatną.

•  Dostęp do członków klasy publicznej można uzyskać z dowolnego miejsca.
• Dostęp do chronionych członków klasy można uzyskać z klasy i jej dzieci.
•  Dostęp do członków klasy prywatnej można uzyskać wyłącznie z poziomu samej klasy.

Jeśli nie określisz wyraźnie widoczności, domyślnie będzie to public. Interfejsy mogą zawierać tylko metody publiczne. Każda klasa implementująca interfejs musi być zgodna z widocznością metody, dlatego też metody te będą w niej publiczne. Metody w klasach abstrakcyjnych mogą mieć widoczność. Metoda w klasie, która rozszerza klasę abstrakcyjną, musi mieć taką samą lub mniej restrykcyjną widoczność.

Instancje Właściwości i metod

Konkretne obiekty tworzone z klas są również nazywane instancjami. Podczas tworzenia obiektu z klasy mówi się, że tworzy się obiekt. Ta sekcja koncentruje się na właściwościach i metodach, które należą do obiektów. Przyjrzymy się temu, co to jest, jak działa składnia PHP, reguły nazewnictwa i jak z nich korzystać.

Właściwości

Właściwości klasy są deklarowane przy użyciu jednego z modyfikatorów widoczności, po którym następuje nazwa właściwości. Nazwy właściwości podlegają tym samym regułom nazewnictwa co zmienne.

< ?php
class Properties
{
// Nie musisz określać wartości domyślnej
public $email;
// Wartość skalarna jest wyrażeniem
protected $name = 'Alice';
// Tablica jest wyrażeniem
protected $accounts = ['cheque', 'savings'];
// Możesz użyć stałego wyrażenia jako wartości domyślnej
private $balance = 60 * 5;
}

Właściwości mogą być inicjowane do wartości domyślnych. Mogą być inicjowane wyrażeniami, ale nie funkcjami.

< ?php
class BrokenPropertyInit
{
private $lastLogin = time(); // nie będzie działać
}

Ten przykład nie działa, ponieważ nie można zainicjować właściwości klasy za pomocą funkcji.

Metody

Metody są funkcjami w konstrukcji zasięgu. Są one deklarowane w funkcji za pomocą modyfikatora widoczności, po którym następuje deklaracja funkcji. Jeśli pominiesz modyfikator widoczności, metoda będzie miała widoczność publiczną.

< ?php
class MethodExample
{
private $name;
// wyraźnie określona widoczność
public function setName($name) {
$this->name = $name;
}
// widoczność publiczna domyślnie
function getName($name) {
return $this->name;
}
}

Metody mogą uzyskać dostęp do niestatycznych właściwości obiektu za pomocą pseudo-zmiennej $this. Pseudo-zmienna $this jest zdefiniowana w obiektach i odnosi się do samego obiektu. Metody statyczne są deklarowane bez tworzenia instancji obiektu, więc $ to nie jest dostępne.

Metody statyczne i właściwości

Deklaracja metody lub właściwości jako statycznej czyni ją dostępną bez potrzeby konkretnej implementacji klasy. Ponieważ metoda statyczna może być wywoływana bez obiektu instancji, zmienna pseudo $this nie jest dostępna w tych metodach. W metodach statycznych i właściwościach można zastosować dowolny modyfikator widoczności. Nie należy wywoływać metody niestatycznej statycznie. Spowoduje to wygenerowanie ostrzeżenia o wycofaniu:

< ?php
class A
{
// to nie jest metoda statyczna
public function sayHello()
{
echo "Hello World";
}
}
//Przestarzałe: Niestatyczna metoda A :: sayHello () nie powinna być wywoływana statycznie
A::sayHello();

Odwołanie do właściwości statycznej lub metody odbywa się za pomocą operatora rozdzielczości zakresu, który jest dwukropkiem.

< ?php
class MyClass
{
// Funkcje statyczne deklarowane są za pomocą słowa kluczowego static
public static function sayHello() {
echo "Hello World" . PHP_EOL;
}
public function someFunction() {
// self odnosi się do "tej klasy", podobnie jak $ this odnosi się do obiektu
self::sayHello();
}
}
// Dostęp do funkcji statycznych można uzyskać za pomocą operatora rozdzielczości zakresu.
MyClass::sayHello(); // Hello World
$object = new MyClass();
$object->someFunction(); // Hello World

Kiedy odwołujemy się do właściwości statycznej z klasy, możemy do niej odwoływać się za pomocą self, parent lub static. Gdy odwołujemy się do członka klasy statycznej spoza klasy, operator rozdzielczości przedrostka poprzedza nazwą klasy. W poprzednim przykładzie odwoływaliśmy się do funkcji statycznej za pomocą MyClass :: sayHello ().

Właściwości statyczne

Właściwości statyczne są również deklarowane za pomocą słowa kluczowego static i można uzyskać do nich dostęp za pomocą operatora rozdzielczości zakresu. Na przykład:

< ?php
class Foo
{
// Właściwości statyczne deklarowane są za pomocą słowa kluczowego static
private static $message = 'Hello World';
public function __construct() {
// Dostęp do właściwości statycznych można uzyskać za pomocą operatora rozdzielczości zakresu
echo self::$message;
}
}
$foo = new Foo; // Hello World
echo Foo::$message; // PHP Błąd krytyczny: nie można uzyskać dostępu do własności prywatnej
Foo::$message

W tym przykładzie uzyskujemy dostęp do właściwości static w konstruktorze za pomocą słowa kluczowego self. Aby zademonstrować, że właściwości statyczne mogą mieć do nich jakąkolwiek widoczność, próbujemy uzyskać do nich dostęp spoza klasy i pojawia się błąd krytyczny.

Praca z obiektami

To bardzo ważna część tej sekcji i powinieneś zwrócić szczególną uwagę na szczegóły. Wprowadzimy różnicę między "płytką" a "głęboką" kopią i przyjrzymy się, w jaki sposób zmienne tablicowe są rzutowane na inne typy zmiennych. Zobaczymy, jak przechowywać obiekt do późniejszego użycia (lub przekazania go do innego programu), a także spojrymy na niektóre sztuczki, w które możesz grać, aliasingując nazwy klas.

Kopiowanie obiektów

Podobnie jak w przypadku przypisania, PHP zawsze przekazuje obiekty przez referencję. Zamiast tworzyć całą kopię obiektu, mówimy raczej "dane można znaleźć w tej lokalizacji". Jeśli chcesz utworzyć kopię obiektu, musisz użyć słowa kluczowego clone(). < ?php
//tworzenie płytkiej kopii obiektu
$objectCopy = clone $originalObject;

PHP utworzy płytką kopię obiektu. W płytkiej kopii, jeśli źródło zawiera odniesienia do zmiennych lub innych obiektów, wówczas odniesienia te są kopiowane do nowego obiektu. Oznacza to, że oryginalny i sklonowany obiekt ma wspólne odwołanie do tego samego obiektu docelowego, natomiast głęboka kopia tworzy nowe wersje obiektów, do których istnieją odniesienia, i wstawia odniesienia do nich w sklonowanym obiekcie. Jest to wolniejsze i droższe, ponieważ wiąże się z tworzeniem znacznie większej liczby obiektów. Sklonowany obiekt będzie zawierał odniesienia do nowych kopii obiektów, do których odnoszą się oryginały. Podczas klonowania obiektu PHP spróbuje wykonać metodę _clone() w tym obiekcie. Możesz zastąpić tę metodę, aby uwzględnić własne zachowanie podczas klonowania obiektu. Ta metody nie można wywołać bezpośrednio.

Wskazówka : Jeśli chcesz głębokiego klonowania obiektu, możesz zaimplementować tę logikę w magicznej metodzie __clone ().

Serializacja obiektów

Serializacja obiektów realizowana jest za pomocą funkcji serialize() i unserialize(). Funkcje te obsługują dowolny typ zmiennej PHP, z wyjątkiem zasobów. Gdy obiekt jest serializowany, PHP spróbuje wywołać na nim metodę _sleep(), a gdy nie jest zserializowany, wywoływana jest funkcja _wakeup(). Są to magiczne metody, które możesz zaimplementować w swojej klasie, aby zmienić sposób, w jaki PHP obsługuje te zdarzenia. Serializacja obiektu daje strumień bajtów reprezentujący dowolną wartość, która może być przechowywana w PHP. Zasoby nie mogą być serializowane. Łańcuchy w PHP mogą zawierać strumienie bajtów, dzięki czemu można umieszczać w nich serializowane obiekty. Ciąg będzie odnosił się do klasy obiektu zserializowanego i będzie zawierał wszystkie powiązane z nim zmienne. Odniesienia do czegokolwiek poza obiektem nie mogą być przechowywane i zostaną utracone, ale zostaną zachowane odniesienia do czegokolwiek wewnątrz obiektu. Kiedy rozpakowujesz obiekt, PHP musi mieć zadeklarowaną klasę. Jeśli nie ma zdefiniowanej klasy, nie będzie w stanie utworzyć obiektu poprawnego typu, a zamiast tego utworzy obiekt typu _PHP_Incomplete_Class_Name, który nie ma żadnych metod. Oto prosty przykład, w którym serializujemy i rozpakowujemy obiekt.

< ?php
$objectOriginal = new A;
$string = serialize($objectOriginal);
file_put_contents('serialize.txt', $string);
// w innym pliku PHP
$string = file_get_contents('serialize.txt');
$objectCopy = unserialize($string);

Istnieje wiele potencjalnych problemów bezpieczeństwa związanych z serializowaniem obiektów. Zajmujemy się nimi w części 6 dotyczącej bezpieczeństwa, ale warto o tym pamiętać, gdy spojrzysz na drugi (opcjonalny) argument za rezygnacją z serializacji. Ten argument pomaga złagodzić atak, w którym przeciwnik może zmienić wartość parametru przekazywanego do unserialize (). Drugi argument dla unserialize pozwala ci określić, co PHP powinno chcieć rozproszyć. Najlepszą praktyką bezpieczeństwa jest zawsze go używać.

Wartość : Znaczenie

Pominięte : PHP może tworzyć obiekty dowolnej klasy
FALSE : Nie akceptuj żadnych zajęć
TRUE : Zaakceptuj wszystkie klasy
Tablica nazwy klasy : Akceptuj tylko określone klasy
Każda inna wartość : unserialize() zwróci false i zwróci E_WARNING

Oto bardziej wszechstronny przykład, w jaki sposób rozproszyć obiekt w PHP:

< ?php
class A {
public function __wakeup() {
echo "Good morning";
}
};
class B {}
$a = new A();
$stored = serialize($a);
unset($a);
// to działa, ponieważ nazwa klasy jest dozwolona
$a = unserialize($stored, ['allowed_classes' => [A::class]]);
// to tworzy __PHP_Incomplete_Class, ponieważ klasa nie pasuje
$b = unserialize($stored, ['allowed_classes' => [B::class]]);
// to tworzy __PHP_Incomplete_Class, ponieważ klasa nie pasuje
$c = unserialize($stored, ['allowed_classes' => false]);
// to działa, ponieważ wszystkie klasy są dozwolone
$d = unserialize($stored, ['allowed_classes' => true]);
// generuje to ostrzeżenie, ponieważ typ parametru jest niepoprawny
$e = unserialize($stored, ['allowed_classes' => 'Not boolean or array']);

Uwaga : Nie używaj serialize() do przekazywania danych do użytkownika. Zamiast tego użyj json_encode! Dlaczego? Z powodu mantry "wszystkie dane wejściowe użytkownika są potencjalnie złe". Nie chcesz dawać użytkownikom możliwości uruchomienia kodu za pomocą unserialize ).

Rzutowanie między tablicami a obiektami

Omówiliśmy zmienne rzutowania w rozdziale na temat podstaw PHP. Należy zauważyć, że można również użyć tej samej składni do rzutowania między tablicą a obiektem. Spójrzmy:

< ?php
$array = [
'key' => 'value',
'nested_array' => [
'another_key' => 'different_value'
]
];
$object = (object)$array;
var_dump($object);

W tym przykładzie użyłem składni rzutowania (obiektowej), aby zmusić tablicę do stania się obiektem. PHP wytworzy obiekt StdClass, który ma właściwości odpowiadające kluczom tablicy. Ten kod generuje:

object(stdClass)#1 (2) {
["key"]⇒
string(5) "value"
["nested_array"]⇒
array(1) {
["another_key"]⇒
string(15) "different_value"
}
}

Uwaga : Zagnieżdżona tablica nie jest przekształcana w zagnieżdżony obiekt.

Możliwe jest rzutowanie obiektu na tablicę przy użyciu składni rzutowania (tablica). Gdybyśmy mieli uruchomić polecenie assert((array) $object === $array); na końcu tej listy kodów kod zostałby uzupełniony bez błędów, ponieważ potwierdzenie zostało przekazane

Rzutowanie obiektów na ciąg

Możesz zdefiniować sposób rzutowania obiektu na łańcuch, deklarując metodę _toString (). PHP wywoła tę metodę i zwróci jej wynik, gdy spróbuje rzutować obiekt na ciąg.

< ?php
class User
{
private $firstName = 'Example'; private $lastName = 'User';
function __toString() {
return $this->firstName;
}
}
$user = new User;
// 'echo'oczekuje typu string, więc PHP niejawnie rzutuje obiekt na string
echo $user; // Example

To pozwala budować i formatować ciąg, który ma znaczenie dla Twojego obiektu. Jeśli nie zadeklarujesz tej metody na swoim obiekcie, PHP wygeneruje krytyczny błąd krytyczny informujący, że nie może przekonwertować obiektu na ciąg znaków

Aliasy klas

PHP pozwala tworzyć aliasy dla klas za pomocą funkcji class_alias(). Ta funkcja przyjmuje trzy parametry - oryginalną nazwę klasy, alias, który należy dla niej utworzyć, oraz opcjonalną wartość logiczną wskazującą, czy należy wywołać autoloader, jeśli klasa nie zostanie znaleziona. Na pierwszy rzut oka może nie być od razu widoczne, jaki może być przypadek użycia aliasu klasy. Ich głównym przykładem użycia jest warunkowe importowanie przestrzeni nazw. Słowo kluczowe use jest przetwarzane w czasie kompilacji, a nie w czasie wykonywania. Oznacza to, że nie można użyć logiki warunkowej do zmiany obszarów nazw do zaimportowania. Klasa funkcji _alias() umożliwia warunkowe importowanie przestrzeni nazw. Na przykład możesz zamienić, której klasy użyć do buforowania bazy danych, w zależności od tego, czy rozszerzenie memcached jest dostępne. W poniższym kodzie nie będziemy mogli importować alternatywnych klas za pomocą słowa kluczowego use, ale za pomocą aliasingu, możemy zmienić klasę, do której odnosi się pamięć podręczna.

< ?php
if (extension_loaded('memcached')) {
class_alias('Memcached', 'Cache');
} else {
class_alias('InternalCacheProvider', 'Cache');
}
class Database
{
// Klasa pamięci podręcznej jest aliasowana do Memcached lub InternalCacheProvider
public function _onstruct(Cache $cache) {}
}

Konstruktory i niszczyciele

Konstruktor to metoda uruchamiana podczas tworzenia obiektu z klasy. Podobnie destruktor powstaje, gdy obiekt jest usuwany. Są zadeklarowane jak w tym przykładzie:

< ?php
class constructorExample
{
// wywoływane podczas tworzenia instancji
public function _construct() {
}
// wywoływany przy usuwaniu
public function _destruct() {
}
// Konstruktor stylu PHP4 - przestarzały w PHP7
public function constructorExample() {
}
}

Pierwszeństwo konstruktorów

W PHP 4 metody konstruktorów zostały zidentyfikowane przez takie same nazwy jak klasy, w których zostały zdefiniowane. Ta forma konstruktora jest przestarzała w PHP 7.

< ?php
class constructorExample
{
// Konstruktor stylu PHP4 - przestarzały w PHP7
public function constructorExample() {
echo "Constructed!";
}
}
$test = new constructorExample;

Dla kompatybilności wstecznej PHP 7 wyszuka funkcję o tej samej nazwie co klasa, jeśli nie będzie w stanie znaleźć funkcji _construct(). Ta funkcjonalność jest przestarzała w PHP 7 i zostanie usunięta w przyszłych wersjach PHP. Jeśli zbudujemy obiekt z tej klasy, nie otrzymamy ostrzeżenia o wycofaniu. Dlaczego? PHP 7.1 najpierw szuka konstruktora nowoczesnego stylu, a jeśli jest obecny, wywoła go. Jeśli nie ma nowoczesnego konstruktora, PHP 7.1 będzie szukał przestarzałego konstruktora, a jeśli jest obecny, wygeneruje ostrzeżenie i wywoła go.

Parametry konstruktora

Jeśli konstruktor klasy przyjmuje parametr, musisz przekazać go podczas tworzenia instancji klasy.
< ?php
class User {
public function __construct($name) {
$this->name = $name;
}
}
}
$user = new User('Alice');

Tutaj przekazujemy ciąg "Alice" do funkcji konstruktora. Praktycznym przykładem może być zastrzyk zależności

Dziedziczenie

PHP obsługuje dziedziczenie w swoim modelu obiektowym. Jeśli rozszerzysz klasę, klasa potomna odziedziczy wszystkie nieprywatne właściwości i metody klasy nadrzędnej. Innymi słowy, dziecko będzie miało publiczne i chronione elementy klasy nadrzędnej. Możesz zastąpić je w klasie potomnej, ale w przeciwnym razie będą miały tę samą funkcjonalność. PHP nie obsługuje dziedziczenia z więcej niż jednej klasy na raz. Składnia powodująca odziedziczenie klasy jest bardzo prosta. Deklarując klasę, po prostu podajemy nazwę klasy, którą rozszerza, jak w tym przykładzie:

< ?php
class ParentClass
{
public function sayHello() {
echo _CLASS_;
}
}
class ChildClass extends ParentClass
{
// nic tej klasie
}
$kid = new ChildClass;
$kid->sayHello(); // ParentClass

W tym przykładzie ChildClass jest zadeklarowany jako rozszerzenie ParentClass. Dziedziczy metodę sayHello (). Gdybyśmy mieli zdefiniować GrandChildClass, która dziedziczy z ChildClass, to i ona odziedziczyłaby wszystkie metody ParentClass. W rzeczywistości każda klasa w łańcuchu dziedziczenia odziedziczy wszystkie metody i właściwości swoich przodków.

Uwaga >: Magiczna stała _CLASS_ podaje nazwę aktualnie wykonywanej klasy. Wywołujemy metodę dziedziczoną w klasie potomnej, ale wykonuje ona funkcję w klasie nadrzędnej i zgłasza, że nazwa klasy to ParentClass.

Słowo kluczowe final

PHP 5 wprowadziło słowo kluczowe final. Możesz zastosować go do całej klasy lub do określonych metod w klasie. Efektem słowa kluczowego final jest zapobieganie rozszerzaniu klas lub zastępowaniu metod. Widoczność wszystkich końcowych właściwości i metod jest publiczna. Oznaczenie klas lub funkcji jako ostatecznych pomaga uniknąć błędnej zmiany zachowania podczas rozszerzania klasy. PHP spowoduje błąd krytyczny, jeśli spróbujesz przesłonić ostatnią metodę w klasie potomnej lub jeśli spróbujesz zadeklarować klasę rozszerzającą klasę oznaczoną jako końcową. Oznaczasz klasę lub metodę jako końcową, używając końcowego słowa kluczowego przed jej definicją, na przykład w tym przykładzie, w którym zaznaczam funkcję jako końcową:

class Employee
{
final public function calculateWage(float $hourlyRate, int
$numHoursWorked)
{
return $hourlyRate * $numHoursWorked;
}
}

Spójrzmy na inny przykład, który pokazuje popełniony błąd i podkreśla przydatność słowa kluczowego. Lista kodów w poniższym przykładzie nie ma żadnego zastosowania słowo kluczowe, final, więc będzie działać bezbłędnie i obliczy raczej hojny pakiet płac dla pracownika. Skomentowałem dwa wiersze, aby pokazać błąd, który zostanie zgłoszony, jeśli oznaczymy odpowiednio klasę lub metodę jako ostateczną.

< ?php
// Błąd krytyczny: Ups klasy nie może dziedziczyć po końcowej klasie (Employee)
final class Employee
{
\final public function calculateWage(float $hourlyRate, int
$numHoursWorked)
{
return $hourlyRate * $numHoursWorked;
}
}
// Błąd krytyczny: klasa CannotExtendFinalClass nie może dziedziczyć po wersji ostatecznej
class (Employee)
class Oops extends Employee {
// Błąd krytyczny: nie można przesłonić ostatniej metody Employee ::calculateWage() in /in/afkAJ on line 17
public function calculateWage(float $hourlyRate, int $numHoursWorked) {
if ($this->employeeName === 'Andrew') {
return 1000000;
}
return $hourlyRate * $numHoursWorked;
}
}
$oops = new Oops;
$oops->employeeName = 'Andrew';
echo $oops->calculateWage(10.00, 50);

Uwaga : Jest to nieco inne niż użycie final w Javie, odpowiednikiem słowa kluczowego final Java w PHP jest const.

Przesłanianie

Klasa podrzędna może zadeklarować metodę o tej samej nazwie co klasa nadrzędna, pod warunkiem, że metoda nie jest oznaczona jako nadrzędna w metodzie nadrzędnej. Podpis parametru metody w elemencie potomnym musi być podobny do elementu nadrzędnego; na przykład poniższy kod wygeneruje ostrzeżenie, że deklaracja potomna musi być zgodna z rodzicem:

< ?php
class Employee
{
public function calculateWage(float $hourlyRate, int $numHoursWorked)
{
return $hourlyRate * $numHoursWorked;
}
}
class Oops extends Employee {
public function calculateWage(int $hourlyRate, int $numHoursWorked) {
return $hourlyRate * $numHoursWorked;
}
}

Jeśli funkcja zostanie zastąpiona w ten sposób i wywołana na dziecko, klasa rodzica nie zostanie wywołana. Dotyczy to konstruktorów i destruktorów, ale w takich przypadkach jest to dość często omawiane w ten sposób:

< ?php
class ChildClass extends ParentClass
{
public function __construct() {
parent::_construct();
// more constructor functions here
}
}

Wywołanie metody parent :: _ construct() wywoła metodę konstruktora klasy nadrzędnej. Gdy przepływ sterowania powróci do potomka, zostaną wywołane pozostałe funkcje jego konstruktora. Jeśli dziecko zastępuje metodę z klasy nadrzędnej, klasa dziecka nie może mieć niższej widoczności niż klasa nadrzędna. Innymi słowy, jeśli metoda rodzica jest publiczna, dziecko nie może zastąpić tej metody jako chronionej lub prywatnej.

Interfejsy

Interfejsy umożliwiają określenie metod, które klasa musi wdrożyć bez określania szczegółów implementacji. Są one powszechnie używane do definiowania kontraktu w paradygmacie architektury zorientowanej na usługi, ale mogą być również używane, gdy chcesz określić, w jaki sposób przyszłe klasy będą oddziaływać z twoim kodem. Wszystkie metody w interfejsie muszą być zadeklarowane jako publiczne i same nie mogą mieć żadnej implementacji. Interfejsy nie mogą mieć właściwości, ale mogą mieć stałe. Interfejsy są zadeklarowane jak w tym przykładzie:

< ?php
interface PaymentProvider
{
public function showPaymentPage();
public function contactGateway(array $messageParameters);
public function notify(string $email);
}

Klasa zostanie zadeklarowana jako implementująca ją w następujący sposób:

< ?php
class CreditCard implements PaymentProvider
{
public function showPaymentPage() {
// implementacja
}
public function contactGateway() {
// implementacja
}
public function notify(string $email) {
// implementacja
}
}

Klasy mogą implementować więcej niż jeden interfejs na raz, wymieniając nazwy interfejsów oddzielone przecinkami. Klasy mogą dziedziczyć tylko jedną klasę, ale mogą implementować wiele interfejsów

Klasy abstrakcyjne

PHP obsługuje klasy abstrakcyjne, które są klasami zawierającymi jedną lub więcej metod abstrakcyjnych. Metoda abstrakcyjna to metoda, która została zadeklarowana, ale nie została zaimplementowana. W poniższym przykładzie klasy abstrakcyjnej funkcja girlDescendingStairs() jest metodą abstrakcyjną. Jest definiowana przy użyciu słowa kluczowego abstract i nie ma żadnej implementacji. Zauważ, że nie ma bloku kodu dla metody abstrakcyjnej.

< ?php
abstract class Paintings
{
abstract protected function girlDescendingStairs();
protected function persistenceOfMemory() {
echo " I have an implementation so this is not an abstract method ";
}
public function __construct() {
echo "I cannot be constructed!";
}
}

Klasa abstrakcyjna nie może być zbudowana; nie możemy utworzyć nowego obiektu z klasy Paintings. Zajęcia abstrakcyjne mają zostać rozszerzone. Klasa rozszerzająca klasę abstrakcyjną musi zdefiniować wszystkie metody oznaczone jako abstrakcyjne w klasie nadrzędnej. Jeśli klasa potomna nie implementuje metod, muszą one również zostać oznaczone jako abstrakcyjne, a zatem klasa potomna również będzie abstrakcyjna. Kiedy klasa potomna rozszerza klasę abstrakcyjną, musi zdefiniować metody abstrakcyjne z taką samą lub mniej restrykcyjną widocznością. Podpis metod zadeklarowanych w klasie potomnej musi być zgodny z podpisem metody abstrakcyjnej. Oznacza to, że liczba i typ wymaganych (nie opcjonalnych) argumentów metody musi być taka sama. Prywatnych metod nie można oznaczyć jako abstrakcyjne. Zobaczmy, jak możemy rozszerzyć klasę abstrakcyjną:

< ?php
abstract class Paintings
{
abstract protected function girlDescendingStairs();
protected function persistenceOfMemory() {
echo "I have an implementation so this is not an abstract method";
}
public function _construct() {
echo "I am being constructed!";
}
}
class Foo extends Paintings {
public function girlDescendingStairs() { echo "Whee!"; }
}
$foo = new Foo; // I cannot be constructed!
$foo->girlDescendingStairs(); // Whee!

Definiuję nową klasę, którą wyobrażam sobie Foo, która rozszerza klasę abstrakcyjną. Wdrożyłem abstrakcyjną metodę girlDescendingStairs i zmieniłem widoczność z protected na mniej ograniczony zakres, public. Nie zastąpiłem metod nie-abstrakcyjnych zdefiniowanych przez klasę abstrakcyjną. Klasa Foo nie ma metod abstrakcyjnych, więc mogę z niej zbudować obiekt. Zauważ, że kiedy to robię, wywoływany jest konstruktor rodzica, więc Foo błędnie zgłasza, że nie można go zbudować.

Klasy Anonimowe

PHP 7 wprowadził anonimowe klasy, które pozwalają na definiowanie klasy w locie i tworzenie z niej obiektu. Oto prosty przykład użycia anonimowej klasy:

< ?php
$object = new class('argument') {
public function __construct(string $message) {
echo $message;
}
};

Zwróć uwagę, jak definiujemy klasę wbudowaną i że możliwe jest przekazywanie argumentów przy użyciu podobnej składni, jak przy tworzeniu obiektu z nazwanej klasy. Ten kod wyświetli ciąg "argument", ponieważ wywoływany jest konstruktor, a ciąg "argument" jest do niego przekazywany. Jednym z przypadków użycia anonimowych obiektów jest rozszerzenie nazwanej klasy; na przykład, jeśli chcesz zastąpić metodę lub właściwość. Zamiast deklarować klasę w osobnym pliku, możesz utworzyć jednorazową implementację.

Reflection

Interfejs API refleksji PHP umożliwia kontrolę elementów PHP w czasie wykonywania i pobieranie informacji o nich. Reflection API został wprowadzony wraz z PHP 5.0, a ponieważ PHP 5.3 jest domyślnie włączony. Jednym z powszechnych miejsc, w których stosuje się odbicie, są testy jednostkowe. Jednym z przykładów przydatności refleksji jest testowanie wartości własności prywatnej w klasie. Możesz użyć refleksji, aby udostępnić prywatną własność, a następnie dokonać asercji. Istnieje kilka klas refleksji, które pozwalają sprawdzić określone typy zmiennych. Każda z tych klas nosi nazwę typu zmiennej, której można użyć do sprawdzenia.

Klasa : Używana do kontroli

ReflectionClass : Klasy
ReflectionObject : Objekty
ReflectionMethod : Metody obiektów
ReflectionFunction : Funkcje takie jak podstawowe funkcje PHP lub funkcje użytkownika
ReflectionProperty : Właściwości

Podręcznik PHP zawiera wyczerpującą dokumentację na temat klas refleksji i ich metod. Rzućmy okiem na przykład użycia ReflectionClass.

< ?php
$reflectionObject = new ReflectionClass('Exception');
print_r($reflectionObject->getMethods());

Parametrem przekazanym do konstruktora klasy odbicia jest albo nazwa ciągu klasy, albo konkretna instancja (obiekt) klasy. Obiekt ReflectionClass ma kilka metod, które pozwalają uzyskać informacje o kontrolowanej klasie. W poprzednim przykładzie wyprowadzamy tablicę wszystkich metod, które posiada klasa Exception.

Podpowiadanie typów

Podpowiedzi dotyczące typów pozwalają określić typ zmiennej, który ma być parametrem funkcji. W poniższym przykładzie określamy, że parametr $arr przekazywany do funkcji printArray() musi być tablicą.

< ?php
function printArray(array $arr) {
echo "< pre >" . print_r($arr,true) . "< /pre >";
}
// Parametrem funkcji musi być klasa implementująca Interfejs PaymentProvider
function sendNotificationToPaymentProvider(PaymentProvider $paymentProvider)
{
$paymentProvider->contactGateway($messageParameters);
}
function sayHello(string $name)
{
echo "Hello " . $name;
}

W PHP 5, jeśli przekażesz parametr niewłaściwego typu, wygenerowany zostanie błąd krytyczny możliwy do odzyskania. W PHP 7 zgłaszany jest wyjątek TypeError. Od wersji PHP 7 podpowiedzi są nazywane "deklaracjami typów". Użyję tej nowej nomenklatury, ale warunki są wymienne w kontekście PHP. Jako wskazówki typu możesz określić typy kompozytów, kallaby i typy zmiennych skalarnych. Ponadto podpowiedzi typu NULL można użyć, jeśli jako parametr domyślny funkcji użyto NULL.

< ?php
function nullExample(null $msg = null) {
echo $msg;
}

Jeśli podasz klasę jako wskazówkę dotyczącą typu, wszystkie jej elementy potomne i implementacje będą poprawnymi parametrami.

< ?php
class A {}
class B extends A {}
class C extends B {}
function foo(A $object) {}
$testObj = new C;
foo($testObj); // nie wystąpił błąd

W tym przykładzie nasza funkcja oczekuje obiektu klasy A. Przechodzimy przez obiekt klasy B. Ponieważ B dziedziczy z A, nasz kod zostanie uruchomiony. Jeśli nazwa klasy, którą podajesz, jest interfejsem, PHP pozwoli na przekazanie dowolnego obiektu, który implementuje interfejs (lub jest przodkiem klasy, która to robi).

Stałe klas

Stała jest wartością, która jest niezmienna. Stałe klas pozwalają definiować takie wartości dla poszczególnych klas; nie zmieniają się między instancjami klasy. Wszystkie obiekty utworzone z tej klasy mają tę samą wartość dla stałej klasy. Stałe klasy podlegają tym samym regułom nazewnictwa co zmienne, ale nie poprzedzają ich symbolem $. Zgodnie z konwencją stałe nazwy są deklarowane wielkimi literami. Rozważmy przykład:

< ?php
class MathsHelper
{
const PI = 4;
public function squareTheCircle($radius) {
return $radius * (self::PI ** 2);
}
}
echo MathsHelper::PI; // 4

Stałe klas są publiczne i dlatego są dostępne ze wszystkich zakresów. Używamy operatora rozdzielczości zakresu i nazwy klasy, w której jest zadeklarowany, gdy uzyskujemy do niego dostęp spoza klasy. Wartość musi być stałym wyrażeniem, a nie (na przykład) zmienną, właściwością lub wywołaniem funkcji. Stałe klasy, podobnie jak stałe tradycyjne, mogą zawierać tylko wartości skalarne.

Późne wiązanie statyczne

Późne wiązanie statyczne zostało wprowadzone w PHP 5.3.0 i jest metodą odwoływania się do wywoływanej klasy (w przeciwieństwie do klasy wywołującej) w kontekście dziedziczenia statycznego. Pomysł polegał na wprowadzeniu słowa kluczowego, które odwoływałoby się do klasy początkowo wywoływanej w czasie wykonywania, a nie do klasy, w której metoda została zdefiniowana. Zamiast wprowadzenia nowego słowa zarezerwowanego podjęto decyzję o użyciu słowa kluczowego static.

Przekierowanie połączeń
br> Wywołanie "przekierowujące" to wywołanie statyczne, które jest wprowadzane przez parent:: ,static:: lub wywołanie funkcji forward_static_call(). Wywołanie self:: może być również wywołaniem przekierowującym, jeśli klasa wróci do klasy odziedziczonej, ponieważ nie ma zdefiniowanej metody. Późne wiązanie statyczne polega na zapisaniu klasy w ostatnim "wywołaniu nieprzekazującym". Innymi słowy, opóźnione rozwiązanie wiązania statycznego zatrzyma się przy całkowicie rozwiązanym wywołaniu statycznym. Zamierzam szczegółowo przejść przez zmodyfikowany przykład przykładu PHP Manual.

< ?php
class A {
public static function foo() {
echo static::who();
}
public static function who() {
return 'A';
}
}
class B extends A {
public static function test() {
A::foo();
parent::foo();
self::foo();
}
}
class C extends B {
public static function who() {
echo 'C';
}
}
C::test(); // ACC

Wydajność ACC może początkowo być sprzeczna z intuicją, ale przejdźmy ją powoli. Wywołanie C :: test () jest w pełni rozwiązane, więc klasa C jest początkowo zapisywana jako ostatnie nie-przekazujące wywołanie. W funkcji C nie ma metody test (), więc wywołanie jest przekazywane niejawnie do jego rodzica. Tak więc wywoływana jest metoda test () w klasie B.

Wywołanie A::foo()

Pierwsze wywołanie w test() konkretnie określa klasę A jako zakres. Oznacza to, że połączenie zostało w pełni rozwiązane. Klasa przechowywana jako ostatnie nieprzesyłające wywołanie jest zmieniana na A. Metoda foo() w A jest wywoływana, a słowo kluczowe static jest rozstrzygające w celu znalezienia klasy, dla której ma zostać wywołana metoda who(). Ostatnie nieprzekazujące wywołanie dotyczyło klasy w A, a zatem wywoływana jest metoda who() w klasie A.

Wywołanie parent:: foo()

Następne wywołanie w teście () odnosi się do elementu nadrzędnego B, więc połączenie jest jawnie przekazywane do elementu nadrzędnego B, którym jest A. Jest to wywołanie przekierowane, więc wartość zachowana jako ostatnie w pełni rozstrzygnięte wywołanie statyczne (którym jest C) pozostaje niezmieniona. Wywoływana jest metoda foo () w A, a słowo kluczowe static jest rozstrzygane w celu znalezienia klasy, dla której ma zostać wywołana metoda who (). Ostatnie nieprzesyłające wywołanie dotyczyło klasy w C, a zatem wywoływana jest metoda who () w klasie C.

Wywołanie self:: foo ()

Klasa B nie ma zdefiniowanej metody foo (), dlatego wywołanie jest niejawnie przekazywane do nadrzędnej klasy A. Jest to wywołanie przekierowane, więc zachowana wartość przechowywana jako ostatnie w pełni rozstrzygnięte wywołanie statyczne (którym jest C) pozostaje niezmieniona . Powoduje to wywołanie metody who () klasy C, gdy słowo kluczowe static jest rozstrzygane w klasie A.

Metody magiczne (__ *)

PHP traktuje dowolną metodę z nazwą poprzedzoną dwoma podkreśleniami jako magiczną. PHP nazywa te metody "magicznie" (bez konieczności ich wywoływania) w określonych momentach cyklu życia obiektu. Lubię myśleć o nich jako o hakach wywoływanych przez zdarzenia. PHP wywołuje magiczną metodę, gdy zdarzenie powiązane jest z obiektem. PHP nie zapewnia implementacji dla tej klasy i od programisty zależy, czy zastąpisz metodę w swojej klasie. Metody magiczne dotyczą tylko klas; nie istnieją jako samodzielne funkcje. Istnieje 15 predefiniowanych funkcji magicznych i zaleca się unikanie nazywania innych funkcji podwójnym prefiksem podkreślenia.

__get i __set

Te magiczne metody są wywoływane, gdy PHP próbuje odczytać (uzyskać) lub napisać (ustawić) niedostępne właściwości.

< ?php
class BankBalance {
private $balance;
public function __get($propertyName) {
// echo "No property " . $propertyName;
return "No value";
}
public function __set($propertyName, $value) {
echo "Cannot set $propertyName to $value";
}
}
$myAccount = new BankBalance();
$myAccount->balance = 100;
// Cannot set balance to 100No value
echo $myAccount->nonExistingProperty;

Do metody __get () przekazywana jest nazwa szukanej właściwości. Możesz zwrócić wartość brakującej właściwości w metodzie lub obsłużyć ją tak, jak chcesz. W tym przykładzie skomentowany kod można zastąpić logiką, aby obsłużyć brakującą właściwość, a właściwości, które nie istnieją, zostaną ustawione na Brak wartości. Dodatkowy parametr, wartość $, jest przekazywany do __set ().

__isset i __unset

Metoda __isset () jest wyzwalana przez wywołanie funkcji isset() lub empty() dla niedostępnej właściwości.
Metoda __unset () jest wyzwalana przez wywołanie funkcji unset() dla niedostępnej właściwości. Obie metody akceptują parametr ciągu, który zawiera nazwę właściwości przekazywanej jako parametr funkcji. Możesz użyć tych magicznych metod, aby umożliwić funkcjom isset(), empty() i unset() działanie na prywatnych i chronionych właściwościach

__call i __callStatic

Te magiczne metody są wywoływane, jeśli próbujesz wywołać nieistniejącą metodę na obiekcie. Jedyna różnica polega na tym, że __callStatic() odpowiada na wywołania statyczne, a __call() odpowiada na wywołania niestatyczne.

< ?php
class Politician {
public function __call($method, $arguments) {
echo __CLASS__ . ' has no ' . $method . ' method';
}
}
$jacob = new Politician();
$jacob->honesty(); // Politician nie ma metody uczciwości

W obu przypadkach do magicznej metody przekazywany jest ciąg znaków zawierający nazwę metody, którą próbuje znaleźć wywołanie, oraz tablicę przekazanych argumentów.

__invoke

Magiczna metoda __invoke() jest wywoływana, gdy próbujesz wykonać obiekt jako funkcję.

< ?php
class Square
{
public function __invoke($var) {
return $var ** 2;
}
}
$callableObject = new Square;
echo $callableObject(10); // 100

Uwaga : Ta składnia może być mylona z nazwami funkcji zmiennych, więc uważaj na to.

__debugInfo

Ta magiczna metoda jest wywoływana przez var_dump () podczas zrzutu obiektu w celu ustalenia, które właściwości powinny zostać wyświetlone. Domyślnie var_dump () wyświetli wszystkie publiczne, chronione i prywatne właściwości obiektu.

< ?php
class Dictatorship {
private $wmd = 'Nuke';
public $oil = 'Lots';
// będziemy ukrywać naszą wmd
public function __debugInfo() {
return [
'oil' => $this->oil
];
}
}
$country = new Dictatorship();
var_dump($country);
/*
object(Dictatorship)#1 (1) {
["oil"] ⇒
string(4) "Lots"
}
*/

Ten przykład zapobiega włączeniu zmiennej $wmd do var_dump().

Standardowa biblioteka PHP (SPL)

Standardowa biblioteka PHP to zbiór klas i interfejsów, które są receptami na rozwiązywanie typowych problemów programistycznych. Jest dostępny i skompilowany w PHP od wersji 5.0.0. Klasy dzielą się na kategorie. Pełna lista klas znajduje się w podręczniku PHP dotyczącym języka SPL.

Kategoria : Używane dla

Struktury danych : Standardowe struktury danych, takie jak listy połączone, podwójnie powiązane listy, kolejki, stosy, stosy itp.
Iteratory
Wyjątki
Obsługa plików
ArrayObject : Dostęp do obiektu za pomocą funkcji tablicowych.
SplObserver i SplSubject : Implementacja wzorca obserwatora.

SPL zapewnia również kilka funkcji. Przeważnie należą do szerokich kategorii refleksji i automatycznego ładowania.

Struktury danych

Pierwszą kategorią funkcji są struktury danych. Jeśli znasz już struktury danych, z przyjemnością dowiesz się, że SPL implementuje wiele z nich. Należą do nich podwójnie połączone listy, stosy, tablice i mapy. Struktury danych są bardzo przydatne w algorytmach programowania.

Iteratory

Iteratory umożliwiają przechodzenie po obiektach i kolekcjach. Iteratory utrzymują kursor wskazujący element. Iteratory PHP umożliwiają przesuwanie lub przewijanie kursora przez wszystkie elementy w kontenerze. Umożliwiają również wykonywanie innych działań, na przykład ArrayIterator pozwala wykonywać sortowania na tablicach. Bez klas zapewnianych przez PHP trzeba by zaimplementować te iteratory samemu, ale na szczęście cała ta ciężka praca została wykonana przez dobrych autorów PHP. Istnieje dość obszerna lista iteratorów. Nie wyobrażam sobie, że musisz umieć je wymienić, ale powinieneś wiedzieć, że są częścią SPL. Zapewnią przynajmniej możliwości poruszania kursorem i ewentualnie dodatkową funkcjonalność.

Wyjątki

SPL zawiera również standardowe klasy wyjątków. Dobrą praktyką jest zgłaszanie wyjątków specyficznych dla rodzaju popełnionego błędu. Ułatwia to kodowanie bloków przechwytujących, które odpowiednio poradzą sobie z wyjątkiem. SPL wprowadza pewne klasy wyjątków, które znacznie ułatwiają zgłaszanie określonych wyjątków. Wyjątki SPL dzielą się na dwie kategorie - wyjątki logiczne i wyjątki czasu wykonywania. Każda z tych kategorii ma szereg klas wyjątków, które koncentrują się na określonych rodzajach błędów, które mogą wystąpić. Powinieneś być przynajmniej w stanie je rozpoznać, jeśli pojawią się w pytaniu.

Wyjątki Logiczne

•  LogicException (extends Exception)
•  BadFunctionCallException
•  BadMethodCallException
•  DomainException
•  InvalidArgumentException
•  LengthException
•  OutOfRangeException
•  Runtime exceptions
•  RuntimeException (extends Exception)
•  OutOfBoundsException
•  OverflowException
•  RangeException
•  UnderflowException
•  UnexpectedValueException

Obsługa plików

SPL oferuje również klasy ułatwiające obsługę plików. Klasa SplFileInfo oferuje obiektowy interfejs wysokiego poziomu do informacji dla pojedynczego pliku. Ma metody, za pomocą których można znaleźć nazwę, rozmiar, uprawnienia i inne atrybuty pliku. Możesz także stwierdzić, czy plik jest katalogiem, czy jest wykonywalny i wiele innych funkcji. Klasa SplFileObject oferuje zorientowany obiektowo interfejs dla pliku. Możesz go użyć, aby otworzyć i odczytać z pliku. Istnieją metody przewijania pliku do przodu lub do tyłu, wyszukiwania określonych pozycji oraz inne funkcje przydatne podczas przetwarzania pliku. Klasa SplTempFileObject oferuje zorientowany obiektowo interfejs dla pliku tymczasowego. Możesz użyć tego pliku, tak jak każdego innego pliku wyjściowego, ale jest on usuwany po zakończeniu skryptu. Możesz tego użyć na przykład podczas przetwarzania obrazu lub weryfikacji przesyłania plików.

ArrayObject

SPL obejmuje również różne klasy i interfejsy. Pierwszy z nich, ArrayObject, pozwala obiektom działać jako tablice. Podczas konstruowania obiektu ArrayObject można przekazać tablicę jako jego parametr. Wynikowy obiekt będzie miał na nim metody, które naśladują funkcje tablicy PHP. ArrayObject ma wiele ograniczeń, ale jedną z jego zalet jest to, że możesz zdefiniować swój własny sposób iteracji.

Wzór obserwatora

Na koniec spójrzmy na dwa interfejsy zawarte w SPL - SplObserver i SplSubject. Pamiętaj, że są to interfejsy, a nie klasy, więc musisz wdrożyć rzeczywiste zachowanie. Te dwa interfejsy razem implementują wzorzec obserwatora. Wzorzec obserwatora to wzorzec projektowania oprogramowania, w którym obiekt, zwany podmiotem, przechowuje listę swoich osób zależnych, zwanych obserwatorami, i powiadamia ich automatycznie o wszelkich zmianach stanu, zwykle przez wywołanie jednej z ich metod. Ten wzorzec służy głównie do wdrażania rozproszonych systemów obsługi zdarzeń. Korzystanie z tych interfejsów sprawi, że Twój kod będzie bardziej przenośny, ponieważ inne biblioteki będą mogły wchodzić w interakcje z twoim przedmiotem i obserwatorami.

Generatory

Generatory zapewniają łatwy sposób tworzenia obiektów iteratora. Zaletą korzystania z iteratora z generatorem jest to, że można zbudować obiekt, który można przechodzić bez konieczności obliczania całego zestawu danych. Oszczędza to czas przetwarzania i pamięć. Przypadkiem użycia może być zastąpienie funkcji, która normalnie zwraca tablicę. Funkcja obliczy wszystkie wartości, przydzieli zmienną tablicową do ich przechowywania i zwróci tablicę. Generator oblicza i przechowuje tylko jedną wartość i przekazuje ją do iteratora. Gdy iterator wymaga następnej wartości, wywołuje generator. Kiedy zabraknie wartości generatora , może po prostu wyjść lub zwrócić wartość końcową. Generatory można iterować tak jak w przypadku dowolnego iteratora, jak w tym przykładzie:

< ?php
function generator() {
for ($i = 0; $i < 99; $i++) {
yield $i;
}
}
foreach (generator() as $value) {
echo $value . " ";
}

Słowo kluczowe yield

Słowo kluczowe yield jest jak funkcja return, z tą różnicą, że służy do zwrócenia wartości iteratora z jednoczesnym wstrzymaniem wykonania generatora. Zakres generatora jest utrzymywany między rozmowami. Zmienne nie stracą swojej wartości po uzyskaniu wydajności generatora.

< ?php
function exampleGenerator() {
// jakieś funkcje
$data = yield $value;
}

Yielding za pomocą kluczy

Możliwe jest uzyskanie par klucz-wartość, które działają jako tablice asocjacyjne dla funkcji korzystających z generatora. Jeśli nie podasz jawnie kluczy, wówczas PHP sparuje uzyskane wartości z rosnącymi kluczami sekwencyjnymi, tak jak w przypadku tablicy wyliczeniowej. Składnia do uzyskania pary klucz-wartość jest podobna do deklarowania tablic asocjacyjnych:

< ?php
function myGenerator() {
// jakieś funkcje
yield $key => $value;
}

Yielding NULL

Wywołanie yield bez argumentu powoduje, że zwraca wartość NULL z automatycznie rosnącym kluczem sekwencyjnym.

Yielding przez odniesienie

Funkcje generatora mogą generować zmienne przez odniesienie, a składnia, aby to zrobić, polega na dodaniu znaku ampersand do nazwy funkcji.

< ?php
function &referenceGenerator() {
// jakieś funkcje
yield $value;
}

Wracając z generatora

Po zakończeniu przetwarzania generatora możesz zwrócić z niego wartość. To czyni bardziej wyraźnym, jaka była końcowa wartość generatora.

function sowCrops() { return 'wheat'; }
function millWheat() { return 'flour'; }
function bake($flour) { return 'cupcake'; }
function generator() {
$wheat = yield sowCrops();
$flour = yield millWheat();
return bake($flour);
};
$gen = generator();
foreach ($gen as $key => $value) {
echo $key . '⇒ ' . $value . PHP_EOL;
}
echo $gen->getReturn();
/*
0 ⇒ wheat
1 ⇒ flour
cupcake
*/
Ta składnia wyjaśnia, jaka była wartość zwracana przez generator. Bez tego należy założyć, że ostatnią uzyskaną wartością była wartość zwracana.

Delegacja generatora

Delegowanie generatora pozwala nam przekazać odpowiedzialność za przetwarzanie wartości na inny obiekt lub tablicę, którą można przejść. Aby to zrobić, uzyskaj wynik z < expression >:

< ?php
function generator() {
$a = [1,2,3];
yield from $a;
yield from range(4,6);
yield from sevenAteNine();
}
function sevenAteNine() {
for ($i=7; $i<10;$i++) {
yield $i;
}
}
$gen = generator();
foreach ($gen as $value) {
echo $value . PHP_EOL;
}

W tym przykładzie używamy trzech sposobów na przekazanie generacji do innego możliwego do przejścia obiektu lub tablicy. Wynikiem uruchomienia tego kodu jest policzenie od 1 do 9.

Traity

Traity zostały wprowadzone w PHP 5.4.0 i mają na celu złagodzenie niektórych ograniczeń jednego języka dziedziczenia.

Uwaga : Traity nie spełniają związku prawdziwego dziedzictwa "is-a". Jeśli znasz mixiny z innych języków, są one bardziej podobne do tych.

Trait zawiera zestaw metod i właściwości, podobnie jak klasa, ale sam nie może być utworzony. Zamiast tego trait jest zawarta w klasie, a klasa może następnie używać swoich metod i właściwości tak, jakby były zadeklarowane w samej klasie. Innymi słowy, traity są spłaszczane do klasy i nie ma znaczenia, czy metoda jest zdefiniowana w cechy, czy w klasie, która używa traita. Możesz skopiować i wkleić kod z traita do klasy i zostanie on użyty w ten sam sposób. Kod zawarty w trait ma za zadanie obudować właściwości wielokrotnego użytku i metody, które można zastosować do wielu klas.

Deklarowanie i używanie trait

Używamy słowa kluczowego trait do deklarowania trait; aby uwzględnić go w klasie, używamy słowa kluczowego use. Klasa może wykorzystywać wiele cech.

< ?php
trait Singleton
{
private static $instance;
public static function getInstance() {
if (!(self::$instance instanceof self)) {
self::$instance = new self;
}
return self::$instance;
}
}
class UsingTraitExample
{
use Singleton;
}
$object = UsingTraitExample::getInstance();
var_dump($object instanceof UsingTraitExample); // true

W tym przykładzie deklarujemy trait która obejmuje metody i właściwości potrzebne do wdrożenia wzorca singletonu. Kiedy chcemy, aby nowa klasa była zgodna ze wzorem singletonów, możemy to zrobić, używając tylko tej cechy. Nie musimy implementować wzorca w klasie i nie musimy uwzględniać wzorca w hierarchii dziedziczenia.

Przestrzenie nazw trait

PHP wygeneruje błąd krytyczny, jeśli traity mają sprzeczne nazwy, ale cechy mogą być zdefiniowane w przestrzeniach nazw. Jeśli próbujesz użyć tej cechy w klasie, która nie znajduje się w tej samej hierarchii przestrzeni nazw, musisz podać w pełni kwalifikowaną nazwę, jeśli ją uwzględnisz.

Dziedziczenie i pierwszeństwo

Traity nie mogą rozszerzać innych cech ani klas, ale możesz po prostu użyć cechy wewnątrz innej. Metody zadeklarowane w klasie za pomocą cechy mają pierwszeństwo przed metodami zadeklarowanymi w tej cechy. Jednak metody w cechy zastąpią metody dziedziczone przez klasę. Mówiąc prościej, pierwszeństwo cech i klas przedstawia się następująco: Członkowie klasy > metody cechy> metody dziedziczone

Rozwiązanie konfliktu

PHP wygeneruje błąd krytyczny, jeśli dwa traity spróbują wstawić metodę o tej samej nazwie, chyba że jawnie rozwiążesz konflikt. PHP pozwala na użycie operatora replaceof do określenia, które z metod powodujących konflikty mają być używane. Pozwala to wykluczyć jedną z metod cechy, ale jeśli chcesz zachować obie metody, musisz użyć operatora as. Operator as umożliwia dołączenie jednej ze sprzecznych metod, ale można użyć innej nazwy, aby się do niej odwoływać. Oto dość długi przykład pokazujący to użycie:

< ?php
trait Dog {
public function makeNoise() {
echo "Woof";
}
public function wantWalkies() {
echo "Yes please!";
}
}
trait Cat {
public function makeNoise() {
echo "Purr";
}
public function wantWalkies() {
echo "No thanks!";
}
}
class DomesticPet
{
use Dog, Cat {
Cat::makeNoise insteadof Dog;
Cat::wantWalkies as kittyWalk;
Dog::wantWalkies insteadof Cat;
}
}
$obj = new DomesticPet();
$obj->makeNoise(); // Purr
$obj->wantWalkies(); // Yes please!
$obj->kittyWalk(); // No thanks!

Uwaga : Nie wystarczy użyć samego as. Nadal musisz użyć insteadof, aby wykluczyć metodę, której nie chcesz używać, i dopiero wtedy możesz użyć as, aby utworzyć nowy sposób odwoływania się do starej metody.

Widoczność

Możesz zastosować modyfikator widoczności do funkcji, rozszerzając słowo kluczowe use, jak w tym przykładzie:

< ?php
trait Example {
public function myFunction() {
// zrób coś
}
}
class VisbilityExample {
use Example {
myFunction as protected;
}
}
$obj = new VisbilityExample();
$obj->myFunction(); // PHP Błąd krytyczny: wywołanie metody chronionej

Określamy, że metoda powinna być chroniona w klasie, nawet jeśli w tej funkcji jest zadeklarowana jako publiczna. Można zawierać wiele funkcji w bloku, z których każdy może mieć swoją widoczność.



QUIZ



P1: Który z nich NIE jest prawidłową nazwą klasy PHP?

------------------------------
exampleClass
Example_Class
Example_1_Class
1_Example_Class
Wszystkie są poprawnymi nazwami klas

P2: Co będzie zawierać właściwość $name po uruchomieniu tego kodu?

< ?php
class SleepyHead {
protected $name = "Dozy";
public function __serialize() {
$this->name = "Asleep";
}
public function __unserialize() {
$this->name = "Rested";
}
}
$obj = unserialize(serialize(new SleepyHead()));

------------------------------
Dozy
Asleep
Rested
Ten kod nie działa

P3: Powiedzmy, że obiekt A ma właściwość, która jest instancją obiektu B. Jeśli sklonujemy obiekt A, to czy PHP również sklonuje B, która jest jedną z jego właściwości?

------------------------------
Tak
Nie
Nie można klonować obiektów zawierających odniesienia do innych obiektów>

P4: Nie można zadeklarować dwóch funkcji o tej samej nazwie. Wybierz tyle, ile dotyczy.

------------------------------
Prawdziwe
Fałszywy; możesz zadeklarować je w różnych przestrzeniach nazw
Fałszywy; możesz zadeklarować je z różną liczbą parametrów
Konstruktor i PHP wybiorą definicję pasującą do Twojej instancji
Fałszywy; możesz zadeklarować je w różnych zakresach

P5: Kiedy wywołasz funkcję json_encode na obiekcie, aby go serializować, jaką magiczną metodę wywoła PHP?

------------------------------
__sleep
__wake
__get
__clone
Żaden z tych

P6: Prawda czy fałsz: interfejsy mogą określać tylko metody publiczne, ale twoja klasa może je implementować w dowolny sposób.

------------------------------
Prawdziwe
Fałszywy; interfejsy mogą określać dowolną widoczność
Fałszywy; nie możesz zmienić widoczności, kiedy w ogóle ją wdrażasz
Fałszywy; możesz zmienić widoczność tylko na mniej widoczną

P7: Jakie będą wyniki tego kodu?

< ?php
klasa Świat {
publiczna funkcja statyczna hello () {
echo "Cześć". __KLASA__;
}
}
klasa Meek rozszerza świat {
funkcja publiczna __call (metoda $, $ argumenty) {
echo "Mam świat";
}
}
Meek :: hello ();

------------------------------
Hello World
I have the world
An error
Żadne z powyższych

P8: Pierwszeństwo funkcji zadeklarowanych w cechach, klasach i metodach dziedziczonych jest następujące?

------------------------------
metody dziedziczone → metody trait → składowe klasy
składowe klasy → metody trait → metody dziedziczone
metody trait 0 → składowe klasy → metody odziedziczone

P9: Prawda czy fałsz: chroniona metoda nie może wywoływać metod prywatnych, nawet jeśli należą do tej samej klasy.

------------------------------
Prawdziwe
Fałszywy


ODPOWIEDZI



• Example_Class

• Ten kod nie działa

•  Nie

• Fałsz; możesz zadeklarować je w różnych przestrzeniach nazw; Fałsz; możesz zadeklarować je w różnych zakresach

•  Żadną z nich

• Fałsz; nie możesz zmienić widoczności, kiedy w ogóle ją wdrażasz

•  Hello World

•  składowe klasy → metody trait → metody dziedziczone

•  Fałsz