Uvod v objektno usmerjeno programiranje

Izraz “OOP” pomeni objektno usmerjeno programiranje, ki je način organiziranja in strukturiranja kode. OOP nam omogoča, da na program gledamo kot na zbirko predmetov, ki med seboj komunicirajo, in ne kot na zaporedje ukazov in funkcij.

V OOP je “objekt” enota, ki vsebuje podatke in funkcije, ki delujejo na teh podatkih. Objekti so ustvarjeni na podlagi “razredov”, ki jih lahko razumemo kot načrte ali predloge za objekte. Ko imamo razred, lahko ustvarimo njegov “primerek”, ki je poseben objekt, narejen iz tega razreda.

Oglejmo si, kako lahko ustvarimo preprost razred v PHP. Pri definiranju razreda uporabimo ključno besedo “class”, ki ji sledi ime razreda, nato pa še oglati oklepaji, ki zapirajo funkcije razreda (imenovane “metode”) in spremenljivke razreda (imenovane “lastnosti” ali “atributi”):

class Car
{
	function honk()
	{
		echo 'Beep beep!';
	}
}

V tem primeru smo ustvarili razred z imenom Car z eno funkcijo (ali “metodo”) z imenom honk.

Vsak razred mora rešiti le eno glavno nalogo. Če razred opravlja preveč stvari, ga je morda primerno razdeliti na manjše, specializirane razrede.

Razredi so običajno shranjeni v ločenih datotekah, da bi bila koda urejena in bi bilo po njej lažje krmariti. Ime datoteke se mora ujemati z imenom razreda, tako da bi bilo ime datoteke za razred Car Car.php .

Pri poimenovanju razredov je dobro upoštevati konvencijo “PascalCase”, kar pomeni, da se vsaka beseda v imenu začne z veliko črko in ni podčrtank ali drugih ločil. Za metode in lastnosti velja konvencija “camelCase”, kar pomeni, da se začnejo z malo črko.

Nekatere metode v PHP imajo posebne vloge in imajo predpono __ (dva podčrtaja). Ena najpomembnejših posebnih metod je “konstruktor”, ki je označen kot __construct. Konstruktor je metoda, ki se samodejno pokliče pri ustvarjanju novega primerka razreda.

Konstruktor pogosto uporabljamo za določanje začetnega stanja predmeta. Na primer, ko ustvarjate objekt, ki predstavlja osebo, lahko konstruktor uporabite za nastavitev njene starosti, imena ali drugih atributov.

Oglejmo si, kako uporabiti konstruktor v PHP:

class Person
{
	private $age;

	function __construct($age)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

$person = new Person(25);
echo $person->howOldAreYou(); // Outputs: 25

V tem primeru ima razred Person lastnost (spremenljivko) $age in konstruktor, ki to lastnost nastavi. Metoda howOldAreYou() nato omogoča dostop do starosti osebe.

Psevdopremenljivka $this se uporablja znotraj razreda za dostop do lastnosti in metod objekta.

Ključna beseda new se uporablja za ustvarjanje novega primerka razreda. V zgornjem primeru smo ustvarili novo osebo, staro 25 let.

Nastavite lahko tudi privzete vrednosti za parametre konstruktorja, če niso določeni pri ustvarjanju predmeta. Na primer:

class Person
{
	private $age;

	function __construct($age = 20)
	{
		$this->age = $age;
	}

	function howOldAreYou()
	{
		return $this->age;
	}
}

$person = new Person;  // if no argument is passed, parentheses can be omitted
echo $person->howOldAreYou(); // Outputs: 20

Če pri ustvarjanju predmeta Person ne določite starosti, bo uporabljena privzeta vrednost 20.

Lepo je, da lahko definicijo lastnosti z njeno inicializacijo prek konstruktorja skrajšamo in poenostavimo na naslednji način:

class Person
{
	function __construct(
		private $age = 20,
	) {
	}
}

Za popolnost dodajmo, da imajo lahko objekti poleg konstruktorjev tudi destruktorje (metoda __destruct), ki se kličejo, preden se objekt sprosti iz pomnilnika.

Prostori imen

Prostori imen nam omogočajo organiziranje in združevanje sorodnih razredov, funkcij in konstant, pri čemer se izognemo konfliktom pri poimenovanju. Predstavljamo si jih lahko kot mape v računalniku, kjer vsaka mapa vsebuje datoteke, povezane z določenim projektom ali temo.

Prostori imen so še posebej uporabni v večjih projektih ali pri uporabi knjižnic tretjih oseb, kjer lahko pride do sporov v poimenovanju razredov.

Predstavljajte si, da imate v svojem projektu razred z imenom Car in ga želite postaviti v imenski prostor z imenom Transport. To bi storili takole:

namespace Transport;

class Car
{
	function honk()
	{
		echo 'Beep beep!';
	}
}

Če želite razred Car uporabiti v drugi datoteki, morate navesti, iz katerega imenskega prostora razred izhaja:

$car = new Transport\Car;

Za poenostavitev lahko na začetku datoteke navedete, kateri razred iz določenega imenskega prostora želite uporabiti, kar vam omogoča ustvarjanje primerkov brez navajanja celotne poti:

use Transport\Car;

$car = new Car;

Dedovanje

Dedovanje je orodje objektno usmerjenega programiranja, ki omogoča ustvarjanje novih razredov na podlagi obstoječih, dedovanje njihovih lastnosti in metod ter njihovo razširitev ali redefiniranje po potrebi. Dedovanje zagotavlja ponovno uporabnost kode in hierarhijo razredov.

Preprosto povedano, če imamo en razred in želimo ustvariti drugega, ki izhaja iz njega, vendar z nekaterimi spremembami, lahko novi razred “podedujemo” od prvotnega.

V jeziku PHP se dedovanje izvaja s ključno besedo extends.

Naš razred Person shranjuje informacije o starosti. Imamo lahko še en razred, Student, ki razširja Person in dodaja informacije o področju študija.

Oglejmo si primer:

class Person
{
	private $age;

	function __construct($age)
	{
		$this->age = $age;
	}

	function printInformation()
	{
		echo "Age: {$this->age} years\n";
	}
}

class Student extends Person
{
	private $fieldOfStudy;

	function __construct($age, $fieldOfStudy)
	{
		parent::__construct($age);
		$this->fieldOfStudy = $fieldOfStudy;
	}

	function printInformation()
	{
		parent::printInformation();
		echo "Field of study: {$this->fieldOfStudy} \n";
	}
}

$student = new Student(20, 'Computer Science');
$student->printInformation();

Kako deluje ta koda?

  • S ključno besedo extends smo razširili razred Person, kar pomeni, da razred Student podeduje vse metode in lastnosti od razreda Person.
  • Ključna beseda parent:: nam omogoča klicanje metod iz nadrejenega razreda. V tem primeru smo poklicali konstruktor iz razreda Person, preden smo v razred Student dodali svojo funkcionalnost. In podobno tudi metodo razreda printInformation() prednika, preden smo izpisali podatke o učencu.

Dedovanje je namenjeno situacijam, v katerih med razredi obstaja razmerje “is a”. Na primer, razred Student je razred Person. Mačka je žival. Omogoča nam, da v primerih, ko v kodi pričakujemo en objekt (npr. “Oseba”), namesto njega uporabimo izpeljani objekt (npr. “Študent”).

Bistveno se je zavedati, da glavni namen dedovanja ni preprečevanje podvajanja kode. Ravno nasprotno, napačna uporaba dedovanja lahko privede do zapletene in težko vzdrževane kode. Če med razredi ni razmerja “je a”, moramo namesto dedovanja razmisliti o kompoziciji.

Upoštevajte, da metode printInformation() v razredih Person in Student prikazujejo nekoliko drugačne informacije. Dodamo lahko tudi druge razrede (na primer Employee), ki bodo zagotovili druge izvedbe te metode. Sposobnost objektov različnih razredov, da se na isto metodo odzivajo na različne načine, se imenuje polimorfizem:

$people = [
	new Person(30),
	new Student(20, 'Computer Science'),
	new Employee(45, 'Director'),
];

foreach ($people as $person) {
	$person->printInformation();
}

Sestavljanje

Kompozicija je tehnika, pri kateri namesto dedovanja lastnosti in metod iz drugega razreda preprosto uporabimo njegov primerek v svojem razredu. Tako lahko združimo funkcionalnosti in lastnosti več razredov, ne da bi ustvarjali zapletene strukture dedovanja.

Na primer, imamo razred Engine in razred Car. Namesto da bi rekli “Avto je motor”, rečemo “Avto ima motor”, kar je tipično kompozicijsko razmerje.

class Engine
{
	function start()
	{
		echo 'Engine is running.';
	}
}

class Car
{
	private $engine;

	function __construct()
	{
		$this->engine = new Engine;
	}

	function start()
	{
		$this->engine->start();
		echo 'The car is ready to drive!';
	}
}

$car = new Car;
$car->start();

V tem primeru razred Car nima vseh lastnosti in metod razreda Engine, vendar ima do njih dostop prek lastnosti razreda $engine.

Prednost sestave je večja prilagodljivost načrtovanja in boljša prilagodljivost za prihodnje spremembe.

Vidnost

V jeziku PHP lahko za lastnosti, metode in konstante razreda določite vidnost. Vidnost določa, kje lahko dostopate do teh elementov.

  1. Public: Če je element označen kot public, to pomeni, da lahko do njega dostopate od koder koli, tudi zunaj razreda.
  2. Zaščiten: Element, označen kot protected, je dostopen le znotraj razreda in vseh njegovih potomcev (razredov, ki dedujejo od njega).
  3. Zasebno: Če je element označen kot private, lahko do njega dostopate le znotraj razreda, v katerem je bil opredeljen.

Če ne določite vidnosti, PHP samodejno nastavi vidnost na public.

Oglejmo si vzorec kode:

class VisibilityExample
{
	public $publicProperty = 'Public';
	protected $protectedProperty = 'Protected';
	private $privateProperty = 'Private';

	public function printProperties()
	{
		echo $this->publicProperty;     // Works
		echo $this->protectedProperty;  // Works
		echo $this->privateProperty;    // Works
	}
}

$object = new VisibilityExample;
$object->printProperties();
echo $object->publicProperty;        // Works
// echo $object->protectedProperty;   // Throws an error
// echo $object->privateProperty;     // Throws an error

Nadaljujemo z dedovanjem razredov:

class ChildClass extends VisibilityExample
{
	public function printProperties()
	{
		echo $this->publicProperty;     // Works
		echo $this->protectedProperty;  // Works
		// echo $this->privateProperty;   // Throws an error
	}
}

V tem primeru lahko metoda printProperties() v razredu ChildClass dostopa do javnih in zaščitenih lastnosti, ne more pa dostopati do zasebnih lastnosti nadrejenega razreda.

Podatki in metode morajo biti čim bolj skriti in dostopni le prek določenega vmesnika. Tako lahko spremenite notranjo implementacijo razreda, ne da bi to vplivalo na preostalo kodo.

Končna ključna beseda

V jeziku PHP lahko ključno besedo final uporabimo, če želimo preprečiti, da bi razred, metodo ali konstanto podedovali ali prepisali. Če je razred označen kot final, ga ni mogoče razširiti. Če je metoda označena kot final, je ni mogoče prepisati v podrazredu.

Če se zavedamo, da določenega razreda ali metode ne bomo več spreminjali, lahko lažje izvajamo spremembe, ne da bi nas skrbelo za morebitne konflikte. Tako lahko na primer dodamo novo metodo, ne da bi se bali, da ima potomec že metodo z istim imenom, kar bi povzročilo kolizijo. Ali pa lahko spremenimo parametre metode, spet brez tveganja, da bi povzročili neskladje s prepisano metodo v potomcu.

final class FinalClass
{
}

// The following code will throw an error because we cannot inherit from a final class.
class ChildOfFinalClass extends FinalClass
{
}

V tem primeru bo poskus dedovanja iz končnega razreda FinalClass povzročil napako.

Statične lastnosti in metode

Ko v jeziku PHP govorimo o “statičnih” elementih razreda, imamo v mislih metode in lastnosti, ki pripadajo samemu razredu in ne določenemu primerku razreda. To pomeni, da vam za dostop do njih ni treba ustvariti primerka razreda. Namesto tega jih pokličete ali do njih dostopate neposredno prek imena razreda.

Upoštevajte, da ker statični elementi pripadajo razredu in ne njegovim instancam, znotraj statičnih metod ne morete uporabljati psevdo-premenljivke $this.

Uporaba statičnih lastnosti vodi v obskurno kodo, polno pasti, zato jih nikoli ne smete uporabljati in tu ne bomo prikazali primera. Po drugi strani pa so statične metode uporabne. Tukaj je primer:

class Calculator
{
	public static function add($a, $b)
	{
		return $a + $b;
	}

	public static function subtract($a, $b)
	{
		return $a - $b;
	}
}

// Using the static method without creating an instance of the class
echo Calculator::add(5, 3); // Output: 8
echo Calculator::subtract(5, 3); // Output: 2

V tem primeru smo ustvarili razred Calculator z dvema statičnima metodama. Ti metodi lahko pokličemo neposredno, ne da bi ustvarili primerek razreda z uporabo operatorja ::. Statične metode so še posebej uporabne za operacije, ki niso odvisne od stanja določenega primerka razreda.

Konstante razreda

Znotraj razredov lahko določimo konstante. Konstante so vrednosti, ki se med izvajanjem programa nikoli ne spremenijo. V nasprotju s spremenljivkami ostane vrednost konstante enaka.

class Car
{
	public const NumberOfWheels = 4;

	public function displayNumberOfWheels(): int
	{
		echo self::NumberOfWheels;
	}
}

echo Car::NumberOfWheels;  // Output: 4

V tem primeru imamo razred Car s konstanto NumberOfWheels. Pri dostopu do konstante znotraj razreda lahko namesto imena razreda uporabimo ključno besedo self.

Objektni vmesniki

Objektni vmesniki delujejo kot “pogodbe” za razrede. Če naj razred implementira objektni vmesnik, mora vsebovati vse metode, ki jih vmesnik opredeljuje. To je odličen način za zagotavljanje, da se določeni razredi držijo iste “pogodbe” ali strukture.

V PHP so vmesniki opredeljeni s ključno besedo interface. Vse metode, opredeljene v vmesniku, so javne (public). Ko razred implementira vmesnik, uporabi ključno besedo implements.

interface Animal
{
	function makeSound();
}

class Cat implements Animal
{
	public function makeSound()
	{
		echo 'Meow';
	}
}

$cat = new Cat;
$cat->makeSound();

Če razred implementira vmesnik, vendar niso definirane vse pričakovane metode, PHP vrže napako.

Razred lahko implementira več vmesnikov hkrati, kar se razlikuje od dedovanja, pri katerem lahko razred deduje samo od enega razreda:

interface Guardian
{
	function guardHouse();
}

class Dog implements Animal, Guardian
{
	public function makeSound()
	{
		echo 'Bark';
	}

	public function guardHouse()
	{
		echo 'Dog diligently guards the house';
	}
}

Abstraktni razredi

Abstraktni razredi služijo kot osnovne predloge za druge razrede, vendar njihovih primerkov ne morete ustvariti neposredno. Vsebujejo mešanico popolnih metod in abstraktnih metod, ki nimajo določene vsebine. Razredi, ki dedujejo od abstraktnih razredov, morajo zagotoviti definicije za vse abstraktne metode iz starševskega razreda.

Za opredelitev abstraktnega razreda uporabimo ključno besedo abstract.

abstract class AbstractClass
{
	public function regularMethod()
	{
		echo 'This is a regular method';
	}

	abstract public function abstractMethod();
}

class Child extends AbstractClass
{
	public function abstractMethod()
	{
		echo 'This is the implementation of the abstract method';
	}
}

$instance = new Child;
$instance->regularMethod();
$instance->abstractMethod();

V tem primeru imamo abstraktni razred z eno redno in eno abstraktno metodo. Nato imamo razred Child, ki podeduje od AbstractClass in zagotavlja implementacijo abstraktne metode.

V čem se vmesniki in abstraktni razredi razlikujejo? Abstraktni razredi lahko vsebujejo tako abstraktne kot konkretne metode, medtem ko vmesniki le določajo, katere metode mora razred implementirati, ne zagotavljajo pa implementacije. Razred lahko podeduje le en abstraktni razred, implementira pa lahko poljubno število vmesnikov.

Preverjanje tipa

Pri programiranju je ključnega pomena, da zagotovimo, da so podatki, s katerimi delamo, pravilnega tipa. V jeziku PHP imamo na voljo orodja, ki to zagotavljajo. Preverjanje, ali so podatki pravilnega tipa, se imenuje “preverjanje tipa”.

Tipi, ki jih lahko srečamo v PHP:

  1. Osnovne vrste: To so int (cela števila), float (števila s plavajočo vejico), bool (logične vrednosti), string (nizi), array (polja) in null.
  2. Tredi: Kadar želimo, da je vrednost primerek določenega razreda.
  3. Vmesniki: Opredeljuje nabor metod, ki jih mora razred implementirati. Vrednost, ki ustreza vmesniku, mora imeti te metode.
  4. Mešani tipi: Določimo lahko, da ima lahko spremenljivka več dovoljenih tipov.
  5. Void: Ta posebni tip označuje, da funkcija ali metoda ne vrača nobene vrednosti.

Oglejmo si, kako spremeniti kodo, da bo vključevala tipe:

class Person
{
	private int $age;

	public function __construct(int $age)
	{
		$this->age = $age;
	}

	public function printAge(): void
	{
		echo "This person is {$this->age} years old.";
	}
}

/**
 * A function that accepts a Person object and prints the person's age.
 */
function printPersonAge(Person $person): void
{
	$person->printAge();
}

Na ta način zagotovimo, da naša koda pričakuje in dela s podatki pravilne vrste, kar nam pomaga preprečiti morebitne napake.

Nekaterih vrst ni mogoče neposredno zapisati v PHP. V tem primeru so navedeni v komentarju phpDoc, ki je standardna oblika za dokumentiranje kode PHP in se začne s /** in konča s */. Omogoča dodajanje opisov razredov, metod itd. Prav tako pa tudi za navajanje kompleksnih tipov s tako imenovanimi opombami @var, @param in @return. Te vrste nato uporabljajo orodja za statično analizo kode, vendar jih PHP sam ne preverja.

class Registry
{
	/** @var array<Person>  indicates that it's an array of Person objects */
	private array $persons = [];

	public function addPerson(Person $person): void
	{
		$this->persons[] = $person;
	}
}

Primerjava in identiteta

V PHP lahko predmete primerjate na dva načina:

  1. Primerjava vrednosti ==: Preveri, ali sta objekta istega razreda in imata v svojih lastnostih enake vrednosti.
  2. Primerjava identitete ===: Preveri, ali gre za isti primerek objekta.
class Car
{
	public string $brand;

	public function __construct(string $brand)
	{
		$this->brand = $brand;
	}
}

$car1 = new Car('Skoda');
$car2 = new Car('Skoda');
$car3 = $car1;

var_dump($car1 == $car2);   // true, because they have the same value
var_dump($car1 === $car2);  // false, because they are not the same instance
var_dump($car1 === $car3);  // true, because $car3 is the same instance as $car1

Upravljavec spletne strani instanceof

Operator instanceof omogoča ugotavljanje, ali je dani predmet primerek določenega razreda, potomec tega razreda ali pa implementira določen vmesnik.

Predstavljajte si, da imamo razred Person in še en razred Student, ki je potomec razreda Person:

class Person
{
	private int $age;

	public function __construct(int $age)
	{
		$this->age = $age;
	}
}

class Student extends Person
{
	private string $major;

	public function __construct(int $age, string $major)
	{
		parent::__construct($age);
		$this->major = $major;
	}
}

$student = new Student(20, 'Computer Science');

// Check if $student is an instance of the Student class
var_dump($student instanceof Student);  // Output: bool(true)

// Check if $student is an instance of the Person class (because Student is a descendant of Person)
var_dump($student instanceof Person);   // Output: bool(true)

Iz izpisov je razvidno, da objekt $student velja za primerek obeh razredov Student in Person.

Fluentni vmesniki

“Fluentni vmesnik” je tehnika v OOP, ki omogoča veriženje metod v enem samem klicu. To pogosto poenostavi in razjasni kodo.

Ključni element tekočega vmesnika je, da vsaka metoda v verigi vrne referenco na trenutni objekt. To dosežemo z uporabo return $this; na koncu metode. Ta slog programiranja je pogosto povezan z metodami, imenovanimi “nastavljalci”, ki nastavljajo vrednosti lastnosti objekta.

Oglejmo si, kako bi lahko bil videti tekoči vmesnik za pošiljanje e-pošte:

public function sendMessage()
{
	$email = new Email;
	$email->setFrom('sender@example.com')
		  ->setRecipient('admin@example.com')
		  ->setMessage('Hello, this is a message.')
		  ->send();
}

V tem primeru so metode setFrom(), setRecipient() in setMessage() uporabljene za nastavitev ustreznih vrednosti (pošiljatelj, prejemnik, vsebina sporočila). Po nastavitvi vsake od teh vrednosti metode vrnejo trenutni objekt ($email), kar nam omogoča, da za njo verižno priključimo drugo metodo. Na koncu pokličemo metodo send(), ki dejansko pošlje elektronsko sporočilo.

Zahvaljujoč tekočim vmesnikom lahko pišemo kodo, ki je intuitivna in lahko berljiva.

Kopiranje s clone

V jeziku PHP lahko ustvarimo kopijo predmeta z uporabo operatorja clone. Tako dobimo nov primerek z enako vsebino.

Če moramo pri kopiranju objekta spremeniti nekatere njegove lastnosti, lahko v razredu definiramo posebno metodo __clone(). Ta metoda se samodejno pokliče, ko je objekt kloniran.

class Sheep
{
	public string $name;

	public function __construct(string $name)
	{
		$this->name = $name;
	}

	public function __clone()
	{
		$this->name = 'Clone of ' . $this->name;
	}
}

$original = new Sheep('Dolly');
echo $original->name . "\n";  // Outputs: Dolly

$clone = clone $original;
echo $clone->name . "\n";     // Outputs: Clone of Dolly

V tem primeru imamo razred Sheep z eno lastnostjo $name. Ko kloniramo primerek tega razreda, metoda __clone() poskrbi, da ime klonirane ovce dobi predpono “Clone of”.

Lastnosti

Značilnosti v PHP so orodje, ki omogoča souporabo metod, lastnosti in konstant med razredi ter preprečuje podvajanje kode. Lahko si jih predstavljate kot mehanizem “kopiraj in prilepi” (Ctrl-C in Ctrl-V), pri katerem se vsebina lastnosti “prilepi” v razrede. To vam omogoča ponovno uporabo kode, ne da bi morali ustvarjati zapletene hierarhije razredov.

Oglejmo si preprost primer uporabe lastnosti v jeziku PHP:

trait Honking
{
	public function honk()
	{
		echo 'Beep beep!';
	}
}

class Car
{
	use Honking;
}

class Truck
{
	use Honking;
}

$car = new Car;
$car->honk(); // Outputs 'Beep beep!'

$truck = new Truck;
$truck->honk(); // Also outputs 'Beep beep!'

V tem primeru imamo lastnost z imenom Honking, ki vsebuje eno metodo honk(). Nato imamo dva razreda: Car in Truck, ki oba uporabljata lastnost Honking. Posledično oba razreda “imata” metodo honk() in jo lahko kličemo na predmetih obeh razredov.

Značilnosti omogočajo enostavno in učinkovito izmenjavo kode med razredi. Ne vstopajo v hierarhijo dedovanja, tj. $car instanceof Honking bo vrnil false.

Izjeme

Izjeme v OOP nam omogočajo elegantno obravnavanje napak in nepričakovanih situacij v naši kodi. So predmeti, ki nosijo informacije o napaki ali nenavadni situaciji.

V jeziku PHP imamo vgrajen razred Exception, ki služi kot osnova za vse izjeme. Ta ima več metod, s katerimi lahko pridobimo več informacij o izjemi, kot so sporočilo o napaki, datoteka in vrstica, v kateri se je napaka pojavila, itd.

Ko se v kodi pojavi napaka, lahko izjemo “vržemo” z uporabo ključne besede throw.

function division(float $a, float $b): float
{
	if ($b === 0) {
		throw new Exception('Division by zero!');
	}
	return $a / $b;
}

Ko funkcija division() kot drugi argument prejme ničlo, vrže izjemo s sporočilom o napaki 'Division by zero!'. Da bi preprečili sesutje programa, ko se vrže izjema, jo ujamemo v blok try/catch:

try {
	echo division(10, 0);
} catch (Exception $e) {
	echo 'Exception caught: '. $e->getMessage();
}

Koda, ki lahko vrže izjemo, je zavita v blok try. Če je izjema vržena, se izvajanje kode premakne v blok catch, kjer lahko obravnavamo izjemo (npr. napišemo sporočilo o napaki).

Za blokoma try in catch lahko dodamo neobvezni blok finally, ki se vedno izvede ne glede na to, ali je bila izjema vržena ali ne (tudi če v bloku try ali catch uporabimo return, break ali continue ):

try {
	echo division(10, 0);
} catch (Exception $e) {
	echo 'Exception caught: '. $e->getMessage();
} finally {
	// Code that is always executed whether the exception has been thrown or not
}

Ustvarimo lahko tudi lastne razrede izjem (hierarhijo), ki dedujejo po razredu Exception. Kot primer si oglejmo preprosto bančno aplikacijo, ki omogoča vplačila in izplačila:

class BankingException extends Exception {}
class InsufficientFundsException extends BankingException {}
class ExceededLimitException extends BankingException {}

class BankAccount
{
	private int $balance = 0;
	private int $dailyLimit = 1000;

	public function deposit(int $amount): int
	{
		$this->balance += $amount;
		return $this->balance;
	}

	public function withdraw(int $amount): int
	{
		if ($amount > $this->balance) {
			throw new InsufficientFundsException('Not enough funds in the account.');
		}

		if ($amount > $this->dailyLimit) {
			throw new ExceededLimitException('Daily withdrawal limit exceeded.');
		}

		$this->balance -= $amount;
		return $this->balance;
	}
}

Če pričakujete različne vrste izjem, lahko za en sam blok try določite več blokov catch.

$account = new BankAccount;
$account->deposit(500);

try {
	$account->withdraw(1500);
} catch (ExceededLimitException $e) {
	echo $e->getMessage();
} catch (InsufficientFundsException $e) {
	echo $e->getMessage();
} catch (BankingException $e) {
	echo 'An error occurred during the operation.';
}

V tem primeru je pomembno upoštevati vrstni red blokov catch. Ker vse izjeme dedujejo od BankingException, bi se, če bi imeli ta blok prvi, vse izjeme ujele v njem, ne da bi koda dosegla naslednje bloke catch. Zato je pomembno, da so bolj specifične izjeme (tj. tiste, ki dedujejo od drugih) višje v vrstnem redu blokov catch kot njihove nadrejene izjeme.

Iteracije

V jeziku PHP lahko z zanko foreach krožite po predmetih, podobno kot krožite po polju. Da bi to delovalo, mora objekt implementirati poseben vmesnik.

Prva možnost je implementacija vmesnika Iterator, ki ima metode current(), ki vrača trenutno vrednost, key(), ki vrača ključ, next(), ki se premakne na naslednjo vrednost, rewind(), ki se premakne na začetek, in valid(), ki preveri, ali smo že na koncu.

Druga možnost je, da implementiramo vmesnik IteratorAggregate, ki ima samo eno metodo getIterator(). Ta bodisi vrne nadomestni objekt, ki bo zagotovil prehod, bodisi je lahko generator, ki je posebna funkcija, ki uporablja yield za zaporedno vračanje ključev in vrednosti:

class Person
{
	public function __construct(
		public int $age,
	) {
	}
}

class Registry implements IteratorAggregate
{
	private array $people = [];

	public function addPerson(Person $person): void
	{
		$this->people[] = $person;
	}

	public function getIterator(): Generator
	{
		foreach ($this->people as $person) {
			yield $person;
		}
	}
}

$list = new Registry;
$list->addPerson(new Person(30));
$list->addPerson(new Person(25));

foreach ($list as $person) {
	echo "Age: {$person->age} years\n";
}

Najboljše prakse

Ko obvladate osnovna načela objektno usmerjenega programiranja, se je treba osredotočiti na najboljše prakse v OOP. Te vam bodo pomagale pri pisanju kode, ki ni le funkcionalna, temveč tudi berljiva, razumljiva in jo je mogoče zlahka vzdrževati.

  1. Oddelitev interesov: Vsak razred mora imeti jasno opredeljeno odgovornost in mora obravnavati le eno primarno nalogo. Če razred počne preveč stvari, je morda primerno, da ga razdelite na manjše, specializirane razrede.
  2. Enkapsulacija: Podatki in metode morajo biti čim bolj skriti in dostopni le prek opredeljenega vmesnika. To omogoča spreminjanje notranje implementacije razreda, ne da bi to vplivalo na preostalo kodo.
  3. Vključevanje odvisnosti: Namesto da bi odvisnosti ustvarjali neposredno v razredu, bi jih morali “vbrizgati” od zunaj. Za globlje razumevanje tega načela priporočamo poglavja o vbrizgavanju odvisnosti.
različica: 4.0