SmartObject
SmartObject je v preteklosti na različne načine popravljal obnašanje predmetov, vendar današnji PHP večino teh izboljšav že vključuje. Vendar pa še vedno dodaja podporo za lastnost.
Namestitev:
composer require nette/utils
Lastnosti, nastavljalci in pridobitelji
V sodobnih objektno usmerjenih jezikih (npr. C#, Python, Ruby, JavaScript) se izraz lastnost nanaša na posebne člane razredov, ki so videti kot spremenljivke, v resnici pa jih predstavljajo metode. Ko se vrednost te „spremenljivke“ dodeli ali prebere, se pokliče ustrezna metoda (imenovana getter ali setter). To je zelo priročno, saj nam omogoča popoln nadzor nad dostopom do spremenljivk. Potrdimo lahko vnos ali ustvarimo rezultate samo takrat, ko je lastnost prebrana.
Lastnosti PHP niso podprte, vendar jih lahko posnemamo z lastnostmi Nette\SmartObject
. Kako jo uporabiti?
- V razred dodajte opombo v obliki
@property <type> $xyz
- Ustvarite getter z imenom
getXyz()
aliisXyz()
, setter z imenomsetXyz()
- Getter in setter morata biti javna ali zaščitena in sta neobvezna, zato je lahko lastnost samo za branje ali samo za pisanje
Lastnost za razred Circle bomo uporabili za zagotovitev, da se v spremenljivko $radius
vnesejo samo nenegativna
števila. Zamenjajte public $radius
z lastnostjo:
/**
* @property float $radius
* @property-read bool $visible
*/
class Circle
{
use Nette\SmartObject;
private float $radius = 0.0; // ni javno
// getter za lastnost $radius
protected function getRadius(): float
{
return $this->radius;
}
// setter za lastnost $radius
protected function setRadius(float $radius): void
{
// sanitizacija vrednosti pred shranjevanjem
$this->radius = max(0.0, $radius);
}
// getter za lastnost $visible
protected function isVisible(): bool
{
return $this->radius > 0;
}
}
$circle = new Circle;
$circle->radius = 10; // dejansko pokliče setRadius(10)
echo $circle->radius; // pokliče getRadius()
echo $circle->visible; // pokliče isVisible()
Lastnosti so predvsem sintaktični sladkor, ki je namenjen temu, da programerju s poenostavitvijo kode osladi življenje. Če jih ne želite, vam jih ni treba uporabljati.
Pogled v zgodovino
SmartObject je v preteklosti na številne načine izboljševal obnašanje predmetov, vendar današnji PHP večino teh izboljšav že vključuje. Naslednje besedilo je nostalgičen pogled v zgodovino, ki nas spominja na razvoj stvari.
Objektni model PHP je že od samega začetka trpel zaradi številnih resnih pomanjkljivosti in pomanjkljivosti. To je privedlo
do oblikovanja razreda Nette\Object
(leta 2007), katerega cilj je bil odpraviti te težave in povečati udobje pri
uporabi PHP. Vse, kar je bilo potrebno, je bilo, da so drugi razredi podedovali razred in pridobili prednosti, ki jih je ponujal.
Ko je PHP 5.4 uvedel podporo za lastnosti, je bil razred Nette\Object
nadomeščen z lastnostjo
Nette\SmartObject
. S tem se je odpravila potreba po dedovanju od skupnega prednika. Poleg tega je bilo mogoče
lastnost uporabiti v razredih, ki so že dedovali od drugega razreda. Dokončen konec Nette\Object
se je zgodil
z izdajo PHP 7.2, ki je prepovedal, da bi se razredi imenovali Object
.
Z nadaljnjim razvojem PHP sta se izboljšala njegov objektni model in jezikovne zmožnosti. Različne funkcije razreda
SmartObject
so postale nepotrebne. Od izdaje PHP 8.2 je ostala le ena funkcija, ki ni neposredno podprta v PHP:
možnost uporabe tako imenovanih lastnosti.
Katere funkcije sta ponujala Nette\Object
in posledično Nette\SmartObject
? Tukaj je pregled.
(V primerih je uporabljen razred Nette\Object
, vendar večina lastnosti velja tudi za lastnost
Nette\SmartObject
).
Nedosledne napake
PHP se je nedosledno obnašal pri dostopu do nereklariranih članov. Stanje v času Nette\Object
je bilo
naslednje:
echo $obj->undeclared; // E_NOTICE, pozneje E_WARNING
$obj->undeclared = 1; // poteka tiho, brez poročanja
$obj->unknownMethod(); // usodna napaka (ki je ni mogoče ujeti s funkcijo try/catch)
Usodna napaka je prekinila aplikacijo brez možnosti odziva. Tiho pisanje v neobstoječe člene brez opozorila bi lahko
privedlo do resnih napak, ki bi jih bilo težko odkriti. Nette\Object
Vsi ti primeri so bili ujeti in zavržena je
bila izjema MemberAccessException
.
echo $obj->undeclared; // vrgel Izjemo Nette\MemberAccessException
$obj->undeclared = 1; // throw Nette\MemberAccessException
$obj->unknownMethod(); // throw Nette\MemberAccessException
Od PHP 7.0 PHP ne povzroča več usodnih napak, ki jih ni mogoče ujeti, dostop do nedeklariranih članov pa je napaka od PHP 8.2.
Ali ste mislili?
Če se je vrgla napaka Nette\MemberAccessException
, morda zaradi tiskarske napake pri dostopu do predmetne
spremenljivke ali klicu metode, je Nette\Object
v sporočilu o napaki poskušal podati namig, kako napako
odpraviti, in sicer v obliki ikoničnega dodatka „Ali ste mislili?“.
class Foo extends Nette\Object
{
public static function from($var)
{
}
}
$foo = Foo::form($var);
// throw Nette\MemberAccessException
// "Call to undefined static method Foo::form(), did you mean from()?"
Čeprav današnji PHP nima funkcije „Ali ste mislili?“, lahko Tracy napakam doda ta stavek. Takšne napake lahko celo samodejno popravi.
Metode razširitve
Navdih za razširitvene metode iz C#. Omogočale so dodajanje novih metod obstoječim razredom. Na primer, obrazcu lahko dodate
metodo addDateTime()
in tako dodate svoj DateTimePicker.
Form::extensionMethod(
'addDateTime',
fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);
$form = new Form;
$form->addDateTime('date');
Razširitvene metode so se izkazale za nepraktične, saj urejevalniki njihovih imen niso samodejno dopolnili, temveč so sporočili, da metoda ne obstaja. Zato je bila njihova podpora ukinjena.
Pridobivanje imena razreda
$class = $obj->getClass(); // z uporabo Nette\Object
$class = $obj::class; // od PHP 8.0
Dostop do razmisleka in opomb
Nette\Object
ponujen dostop do refleksije in anotacij z metodama getReflection()
in
getAnnotation()
:
/**
* @author John Doe
*/
class Foo extends Nette\Object
{
}
$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // vrne 'John Doe'
Od različice PHP 8.0 je mogoče dostopati do metainformacij v obliki atributov:
#[Author('John Doe')]
class Foo
{
}
$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];
Metode, ki pridobivajo
Nette\Object
je ponujal eleganten način za ravnanje z metodami, kot da bi bile spremenljivke:
class Foo extends Nette\Object
{
public function adder($a, $b)
{
return $a + $b;
}
}
$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5
Od različice PHP 8.1 lahko uporabljate tako imenovano sintakso „first-class callable syntax“::https://www.php.net/…lable_syntax
$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5
Dogodki
Nette\Object
ponujen sintaktični sladkor za sprožitev dogodka:
class Circle extends Nette\Object
{
public array $onChange = [];
public function setRadius(float $radius): void
{
$this->onChange($this, $radius);
$this->radius = $radius;
}
}
Koda $this->onChange($this, $radius)
je enakovredna naslednjemu:
foreach ($this->onChange as $callback) {
$callback($this, $radius);
}
Zaradi jasnosti priporočamo, da se izognete čarobni metodi $this->onChange()
. Praktično nadomestilo je
funkcija Nette\Utils\Arrays::invoke:
Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);