Nette Documentation Preview

syntax
Uvod v objektno usmerjeno programiranje
***************************************

.[perex]
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"):

```php
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:

```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 `$age` in konstruktor, ki to lastnost nastavi. Metoda `howOldAreYou()` nato omogoča dostop do starosti osebe.

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:

```php
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.

Nazadnje lahko opredelitev lastnosti z njeno inicializacijo prek konstruktorja skrajšate in poenostavite na naslednji način:

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


Prostori imen .[#toc-namespaces]
--------------------------------

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:

```php
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:

```php
$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:

```php
use Transport\Car;

$car = new Car;
```


Dedovanje .[#toc-inheritance]
-----------------------------

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:

```php
class Person
{
	private $age;

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

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

class Student extends Person
{
	private $fieldOfStudy;

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

	function printInformation()
	{
		echo 'Age of student: ', $this->howOldAreYou();
		echo 'Field of study: ', $this->fieldOfStudy;
	}
}

$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, da kličemo metode iz nadrejenega razreda. V tem primeru smo poklicali konstruktor iz razreda `Person`, preden smo v razred `Student` dodali svojo funkcionalnost.

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.


Sestavljanje .[#toc-composition]
--------------------------------

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.

```php
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 .[#toc-visibility]
--------------------------

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:

```php
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:

```php
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 .[#toc-final-keyword]
-------------------------------------------

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.

```php
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 .[#toc-static-properties-and-methods]
------------------------------------------------------------------

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 |dependency-injection:global-state], zato jih nikoli ne smete uporabljati in tu ne bomo prikazali primera. Po drugi strani pa so statične metode uporabne. Tukaj je primer:

```php
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 .[#toc-class-constants]
-----------------------------------------

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.

```php
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 .[#toc-object-interfaces]
-------------------------------------------

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`.

```php
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.


Abstraktni razredi .[#toc-abstract-classes]
-------------------------------------------

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`.

```php
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.


Preverjanje tipa .[#toc-type-checking]
--------------------------------------

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:

```php
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.


Primerjava in identiteta .[#toc-comparison-and-identity]
--------------------------------------------------------

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.

```php
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` .[#toc-the-instanceof-operator]
-----------------------------------------------------------------------

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`:

```php
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 .[#toc-fluent-interfaces]
-------------------------------------------

"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:

```php
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` .[#toc-copying-with-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.

```php
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 .[#toc-traits]
------------------------

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:

```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 obravnavo in upravljanje napak, ki se lahko pojavijo med izvajanjem naše kode. V bistvu so predmeti, namenjeni beleženju napak ali nepričakovanih situacij v vašem programu.

V PHP je za te predmete vgrajen razred `Exception`. Ima več metod, ki nam omogočajo, da pridobimo več informacij o izjemi, kot so sporočilo o napaki, datoteka in vrstica, v kateri se je napaka pojavila, itd.

Ko se pojavi težava, lahko "vržemo" izjemo (z uporabo razreda `throw`). Če želimo to izjemo "ujeti" in obdelati, uporabimo bloka `try` in `catch`.

Oglejmo si, kako to deluje:

```php
try {
	throw new Exception('Message explaining the reason for the exception');

	// This code won't execute
	echo 'I am a message that nobody will read';

} catch (Exception $e) {
	echo 'Exception caught: '. $e->getMessage();
}
```

Pomembno je opozoriti, da se izjema lahko vrže globlje, med klicem drugih metod.

Za en blok `try` lahko določite več blokov `catch`, če pričakujete različne vrste izjem.

Ustvarimo lahko tudi hierarhijo izjem, kjer vsak razred izjem podeduje od prejšnjega. Kot primer si oglejmo preprosto bančno aplikacijo, ki omogoča vplačila in izplačila:

```php
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;
	}
}

$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.


Najboljše prakse .[#toc-best-practices]
---------------------------------------

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 |dependency-injection:introduction].

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 $age in konstruktor, ki to lastnost nastavi. Metoda howOldAreYou() nato omogoča dostop do starosti osebe.

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.

Nazadnje lahko opredelitev lastnosti z njeno inicializacijo prek konstruktorja skrajšate in poenostavite na naslednji način:

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

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 howOldAreYou()
	{
		return $this->age;
	}
}

class Student extends Person
{
	private $fieldOfStudy;

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

	function printInformation()
	{
		echo 'Age of student: ', $this->howOldAreYou();
		echo 'Field of study: ', $this->fieldOfStudy;
	}
}

$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, da kličemo metode iz nadrejenega razreda. V tem primeru smo poklicali konstruktor iz razreda Person, preden smo v razred Student dodali svojo funkcionalnost.

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.

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.

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.

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.

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 obravnavo in upravljanje napak, ki se lahko pojavijo med izvajanjem naše kode. V bistvu so predmeti, namenjeni beleženju napak ali nepričakovanih situacij v vašem programu.

V PHP je za te predmete vgrajen razred Exception. Ima več metod, ki nam omogočajo, da pridobimo več informacij o izjemi, kot so sporočilo o napaki, datoteka in vrstica, v kateri se je napaka pojavila, itd.

Ko se pojavi težava, lahko „vržemo“ izjemo (z uporabo razreda throw). Če želimo to izjemo „ujeti“ in obdelati, uporabimo bloka try in catch.

Oglejmo si, kako to deluje:

try {
	throw new Exception('Message explaining the reason for the exception');

	// This code won't execute
	echo 'I am a message that nobody will read';

} catch (Exception $e) {
	echo 'Exception caught: '. $e->getMessage();
}

Pomembno je opozoriti, da se izjema lahko vrže globlje, med klicem drugih metod.

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

Ustvarimo lahko tudi hierarhijo izjem, kjer vsak razred izjem podeduje od prejšnjega. 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;
	}
}

$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.

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.