SmartObject és StaticClass
A SmartObject a PHP osztályok tulajdonságok támogatását adja hozzá. A StaticClass a statikus osztályok jelölésére szolgál.
Telepítés:
composer require nette/utils
Tulajdonságok, Getterek és Setterek
A modern objektumorientált nyelvekben (pl. C#, Python, Ruby, JavaScript) a tulajdonság kifejezés az osztályok speciális tagjaira utal, amelyek változóknak tűnnek, de valójában metódusok képviselik őket. Amikor ennek a “változónak” az értékét hozzárendeljük vagy kiolvassuk, a megfelelő metódust (az úgynevezett gettert vagy settert) hívjuk meg. Ez egy nagyon praktikus dolog, teljes kontrollt biztosít számunkra a változókhoz való hozzáférés felett. Csak akkor tudjuk érvényesíteni a bemenetet, vagy csak akkor generálhatunk eredményt, ha a tulajdonságot kiolvassuk.
A PHP tulajdonságok nem támogatottak, de a Nette\SmartObject
trait képes utánozni őket. Hogyan
használjuk?
- Adjunk hozzá egy megjegyzést az osztályhoz a következő formában
@property <type> $xyz
- Hozzon létre egy
getXyz()
vagyisXyz()
nevű gettert, egy settert, amelynek a nevesetXyz()
- A getter és a setter nyilvános vagy védett kell, hogy legyen, és opcionális, tehát lehet read-only vagy write-only tulajdonság.
A Circle osztály tulajdonságát fogjuk használni annak biztosítására, hogy a $radius
változóba csak nem
negatív számok kerüljenek. Helyettesítsük a public $radius
címet a tulajdonsággal:
/**
* @property float $radius
* @property-read bool $visible
*/
class Circle
{
use Nette\SmartObject;
private float $radius = 0.0; // nem nyilvános
// getter a $radius tulajdonsághoz
protected function getRadius(): float
{
return $this->radius;
}
// setter a $radius tulajdonsághoz
protected function setRadius(float $radius): void
{
// az érték mentés előtti szanálása
$this->radius = max(0.0, $radius);
}
// getter a $visible tulajdonsághoz
protected function isVisible(): bool
{
return $this->radius > 0;
}
}
$circle = new Circle;
$circle->radius = 10; // valójában a setRadius(10) hívása.
echo $circle->radius; // meghívja a getRadius() függvényt.
echo $circle->visible; // hívja az isVisible() függvényt
A tulajdonságok elsősorban szintaktikai cukor, amelynek célja, hogy a kód egyszerűsítésével megédesítse a programozó életét. Ha nem akarod őket, nem kell használni őket.
Statikus osztályok
A statikus osztályokat, azaz azokat az osztályokat, amelyeket nem szándékoznak példányosítani, a
Nette\StaticClass
tulajdonsággal lehet jelölni:
class Strings
{
use Nette\StaticClass;
}
Amikor megpróbál egy példányt létrehozni, a Error
kivétel dobódik, jelezve, hogy az osztály statikus.
Egy pillantás a történelembe
A SmartObject korábban sokféleképpen javította és javította az osztályok viselkedését, de a PHP fejlődése az eredeti funkciók nagy részét feleslegessé tette. A következőkben tehát a dolgok fejlődésének történetébe pillantunk bele.
A PHP objektummodellje a kezdetektől fogva számos komoly hibától és hiányosságtól szenvedett. Ez volt az oka a
Nette\Object
osztály létrehozásának (2007-ben), amely megpróbálta ezeket orvosolni és javítani a PHP
használatának élményét. Ez elég volt ahhoz, hogy más osztályok is örököljenek belőle, és elnyerjék az általa
nyújtott előnyöket. Amikor a PHP 5.4-ben megjelent a trait-támogatás, a Nette\Object
osztályt a
Nette\SmartObject
osztály váltotta fel. Így már nem volt szükség arra, hogy egy közös őstől örököljenek.
Ráadásul a trait olyan osztályokban is használható volt, amelyek már egy másik osztálytól örököltek. A
Nette\Object
végleges végét a PHP 7.2 kiadása jelentette, amely megtiltotta az osztályok Object
nevének használatát.
A PHP fejlesztésének előrehaladtával az objektummodell és a nyelvi képességek tovább fejlődtek. A
SmartObject
osztály egyes függvényei feleslegessé váltak. A PHP 8.2 kiadása óta az egyetlen olyan funkció,
amely megmaradt, és amelyet a PHP még nem támogat közvetlenül, az úgynevezett tulajdonságok használatának lehetősége.
Milyen funkciókat kínált egykor a Nette\Object
és a Nette\Object
? Íme egy áttekintés.
(A példák a Nette\Object
osztályt használják, de a tulajdonságok többsége a Nette\SmartObject
tulajdonságra is vonatkozik).
Inkonzisztens hibák
A PHP következetlenül viselkedett a nem deklarált tagok elérésekor. Az állapot a Nette\Object
idején a
következő volt:
echo $obj->undeclared; // E_NOTICE, később E_WARNING
$obj->undeclared = 1; // csendben átmegy jelentés nélkül.
$obj->unknownMethod(); // Végzetes hiba (try/catch-el nem fogható)
A végzetes hiba mindenféle reagálási lehetőség nélkül megszakította az alkalmazást. A nem létező tagokba való
csendes írás figyelmeztetés nélkül súlyos, nehezen észlelhető hibákhoz vezethetett. Nette\Object
Minden
ilyen esetet elkaptunk, és a MemberAccessException
címen egy kivételt dobtunk.
echo $obj->undeclared; // throw Nette\MemberAccessException
$obj->undeclared = 1; // throw Nette\MemberAccessException
$obj->unknownMethod(); // throw Nette\MemberAccessException
A PHP 7.0 óta a PHP már nem okoz nem fogható halálos hibákat, a nem deklarált tagokhoz való hozzáférés pedig a PHP 8.2 óta hiba.
Úgy értetted?
Ha egy Nette\MemberAccessException
hiba lépett fel, például egy objektumváltozó elérésekor vagy egy
metódus hívásakor elírás miatt, a Nette\Object
megpróbált a hibaüzenetben egy tippet adni a hiba
kijavítására, az ikonikus “did you mean?” kiegészítés formájában.
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()?"
A mai PHP-ben talán nincs a “did you mean?” semmilyen formája, de Tracy ezt a kiegészítést hozzáadja a hibákhoz. És még maga is képes kijavítani az ilyen hibákat.
Bővítési módszerek
A C# bővítési metódusok által inspirálva. Lehetőséget adtak új metódusok hozzáadására a meglévő osztályokhoz.
Például a addDateTime()
metódust hozzáadhatta egy űrlaphoz, hogy saját DateTimePickert adjon hozzá.
Form::extensionMethod(
'addDateTime',
fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);
$form = new Form;
$form->addDateTime('date');
A kiterjesztési metódusok nem bizonyultak praktikusnak, mert a nevüket a szerkesztők nem töltötték ki automatikusan, hanem azt jelentették, hogy a metódus nem létezik. Ezért a támogatásuk megszűnt.
Az osztály nevének megadása
$class = $obj->getClass(); // using Nette\Object
$class = $obj::class; // PHP 8.0 óta
Hozzáférés a reflexióhoz és a megjegyzésekhez
Nette\Object
a getReflection()
és a getAnnotation()
metódusok segítségével kínált
hozzáférést a reflexióhoz és a megjegyzésekhez:
/**
* @author John Doe
*/
class Foo extends Nette\Object
{
}
$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // visszaadja 'John Doe'.
A PHP 8.0-tól kezdve a metainformációkat attribútumok formájában is elérhetjük:
#[Author('John Doe')]
class Foo
{
}
$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];
Metódus Getterek
Nette\Object
elegáns módot kínált arra, hogy a módszereket úgy kezeljük, mintha változók lennének:
class Foo extends Nette\Object
{
public function adder($a, $b)
{
return $a + $b;
}
}
$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5
A PHP 8.1-től kezdve használhatod az úgynevezett első osztályú hívható szintaxist:
$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5
Események
Nette\Object
szintaktikai cukrot kínált az esemény
kiváltásához:
class Circle extends Nette\Object
{
public array $onChange = [];
public function setRadius(float $radius): void
{
$this->onChange($this, $radius);
$this->radius = $radius
}
}
A $this->onChange($this, $radius)
kód a következővel egyenértékű:
foreach ($this->onChange as $callback) {
$callback($this, $radius);
}
Az áttekinthetőség kedvéért javasoljuk, hogy kerüljük a $this->onChange()
varázsmódszert. Ennek
praktikus helyettesítője a Nette\Utils\Arrays::invoke
függvény:
Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);