You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: utils/cs/smartobject.texy
+73-69
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@ SmartObject
2
2
***********
3
3
4
4
.[perex]
5
-
SmartObject opravovala chování objektů v mnoha směrech, ale dnešní PHP již obsahuje většinu vylepšení nativně. Stále však přidává podporu pro tzv. *property*.
5
+
SmartObject po léta vylepšoval chování objektů v PHP. Od verze PHP 8.4 jsou již všechny jeho funkce součástí samotného PHP, čímž završil svou historickou misi být průkopníkem moderního objektového přístupu v PHP.
6
6
7
7
8
8
Instalace:
@@ -11,19 +11,64 @@ Instalace:
11
11
composer require nette/utils
12
12
```
13
13
14
+
SmartObject vznikl v roce 2007 jako revoluční řešení nedostatků tehdejšího objektového modelu PHP. V době, kdy PHP trpělo řadou problémů s objektovým návrhem, přinesl výrazné vylepšení a zjednodušení práce pro vývojáře. Stal se legendární součástí frameworku Nette. Nabízel funkcionalitu, kterou PHP získalo až o mnoho let později - od kontrolu přístupu k vlastnostem objektů až po sofistikované syntaktické cukrátka. S příchodem PHP 8.4 završil svou historickou misi, protože všechny jeho funkce se staly nativní součástí jazyka. Předběhl vývoj PHP o pozoruhodných 17 let.
14
15
15
-
Properties, gettery a settery
16
-
=============================
16
+
Technicky prošel SmartObject zajímavým vývojem. Původně byl implementován jako třída `Nette\Object`, od které ostatní třídy dědily potřebnou funkcionalitu. Zásadní změna přišla s PHP 5.4, které přineslo podporu trait. To umožnilo transformaci do podoby traity `Nette\SmartObject`, což přineslo větší flexibilitu - vývojáři mohli funkcionalitu využít i ve třídách, které již dědily od jiné třídy. Zatímco původní třída `Nette\Object` zanikla s příchodem PHP 7.2 (které zakázalo pojmenování tříd slovem `Object`), traita `Nette\SmartObject` žije dál.
17
17
18
-
Termínem *property* (česky vlastnost) se v moderních objektově orientovaných jazycích (např. C#, Python, Ruby, JavaScript) označují [speciální členy tříd|https://en.wikipedia.org/wiki/Property_(programming)], které se tváří jako proměnné, ale ve skutečnosti jsou reprezentovány metodami. Při přiřazení nebo čtení hodnoty této „proměnné“ se zavolá příslušná metoda (tzv. getter nebo setter). Jde o velice šikovnou věc, díky ní máme přístup k proměnným plně pod kontrolou. Můžeme tak validovat vstupy nebo generovat výsledky až ve chvíli, kdy se property čte.
18
+
Pojďme si projít vlastnosti, které kdysi `Nette\Object` a později `Nette\SmartObject` nabízeli. Každá z těchto funkcí ve své době představovala významný krok vpřed v oblasti objektově orientovaného programování v PHP.
19
19
20
-
PHP property nepodporují, ale traita `Nette\SmartObject` je umí imitovat. Jak na to?
21
20
22
-
- Přidejte třídě anotaci ve tvaru `@property <type> $xyz`
23
-
- Vytvořte getter s názvem `getXyz()` nebo `isXyz()`, setter s názvem `setXyz()`
24
-
- Getter a setter musí být *public* nebo *protected* a jsou volitelné, mohou tedy existovat *read-only* nebo *write-only* property
21
+
Konzistentní chybové stavy
22
+
--------------------------
23
+
Jedním z nejpalčivějších problémů raného PHP bylo nekonzistentní chování při práci s objekty. `Nette\Object` přinesl do tohoto chaosu řád a předvídatelnost. Podívejme se, jak vypadalo původní chování PHP:
25
24
26
-
Property využijeme u třídy Circle, abychom zajistili, že do proměnné `$radius` se dostanou jen nezáporná čísla. Nahradíme `public $radius` za property:
25
+
```php
26
+
echo $obj->undeclared; // E_NOTICE, později E_WARNING
27
+
$obj->undeclared = 1; // projde tiše bez hlášení
28
+
$obj->unknownMethod(); // Fatal error (nezachytitelný pomocí try/catch)
29
+
```
30
+
31
+
Fatal error ukončil aplikaci bez možnosti jakkoliv reagovat. Tichý zápis do neexistujících členů bez upozornění mohl vést k závažným chybám, které šly obtížné odhalit. `Nette\Object` všechny tyto případy zachytával a vyhazoval výjimku `MemberAccessException`, což umožnilo programátorům na chyby reagovat a řešit je.
Od PHP 7.0 již jazyk nezpůsobuje nezachytitelné fatal error a od PHP 8.2 je přístup k nedeklarovaným členům považován za chybu.
40
+
41
+
42
+
Nápověda "Did you mean?"
43
+
------------------------
44
+
`Nette\Object` přišel s velmi příjemnou funkcí: inteligentní nápovědou při překlepech. Když vývojář udělal chybu v názvu metody nebo proměnné, nejen oznámil chybu, ale také nabídl pomocnou ruku v podobě návrhu správného názvu. Tato ikonická hláška, známá jako "did you mean?", ušetřila programátorům hodiny hledání překlepů:
45
+
46
+
```php
47
+
class Foo extends Nette\Object
48
+
{
49
+
public static function from($var)
50
+
{
51
+
}
52
+
}
53
+
54
+
$foo = Foo::form($var);
55
+
// vyhodí Nette\MemberAccessException
56
+
// "Call to undefined static method Foo::form(), did you mean from()?"
57
+
```
58
+
59
+
Dnešní PHP sice nemá žádnou podobu „did you mean?“, ale tento dovětek umí do chyb doplňovat [Tracy|tracy:]. A dokonce takové chyby i [samo opravovat |tracy:open-files-in-ide#Ukázky].
60
+
61
+
62
+
Properties s kontrolovaným přístupem
63
+
------------------------------------
64
+
Významnou inovací, kterou SmartObject přinesl do PHP, byly properties s kontrolovaným přístupem. Tento koncept, běžný v jazycích jako C# nebo Python, umožnil vývojářům elegantně kontrolovat přístup k datům objektu a zajistit jejich konzistenci. Properties jsou mocným nástrojem objektově orientovaného programování. Fungují jako proměnné, ale ve skutečnosti jsou reprezentovány metodami (gettery a settery). To umožňuje validovat vstupy nebo generovat hodnoty až v momentě čtení.
65
+
66
+
Pro používání properties musíte:
67
+
- Přidat třídě anotaci ve tvaru `@property <type> $xyz`
68
+
- Vytvořit getter s názvem `getXyz()` nebo `isXyz()`, setter s názvem `setXyz()`
69
+
- Zajistit, aby getter a setter byly *public* nebo *protected*. Jsou volitelné - mohou tedy existovat jako *read-only* nebo *write-only* property
70
+
71
+
Ukažme si praktický příklad na třídě Circle, kde properties využijeme k zajištění, že poloměr bude vždy nezáporné číslo. Nahradíme původní `public $radius` za property:
27
72
28
73
```php
29
74
/**
@@ -62,64 +107,25 @@ echo $circle->radius; // volá getRadius()
62
107
echo $circle->visible; // volá isVisible()
63
108
```
64
109
65
-
Properties jsou především "syntaktickým cukříkem"((syntactic sugar)), jehož smyslem je zpřehlednit kód a osladit tak programátorovi život. Pokud nechcete, nemusíte je používat.
66
-
67
-
68
-
Pohled do historie
69
-
==================
70
-
71
-
SmartObject opravovala chování objektů v mnoha směrech, ale dnešní PHP již obsahuje většinu vylepšení nativně. Následující text je tak nostalgickým pohledem do historie a připomínkou toho, jak se věci vyvíjely.
72
-
73
-
Objektový model PHP trpěl od počátku celou řadou vážných nedostatků a necnostní. To byl důvod vzniku třídy `Nette\Object` (v roce 2007), která se je pokoušela napravovat a zlepšit komfort při používání PHP. Stačilo, aby ostatní třídy od ní dědily, a získaly výhody, které přinášela. Když PHP 5.4 přišlo s podporou trait, byla třída `Nette\Object` nahrazena traitou `Nette\SmartObject`. Nebylo tak nutné už dědit od společného předka. Navíc traita se dala použít i ve třídách, které již dědily od jiné třídy. Definitivní konec `Nette\Object` přišel s vydáním PHP 7.2, které zakázalo třídám jmenovat se `Object`.
74
-
75
-
Jak šel vývoj PHP dál, objektový model a schopnosti jazyka se vylepšovaly. Jednotlivé funkce třídy `SmartObject` se stávaly zbytečnými. Od vydání PHP 8.2 zůstala jediná feature, která ještě není v PHP přímo podporována, a to možnost používat tzv. [property|#Properties, gettery a settery].
76
-
77
-
Jaké vlastnosti kdysi `Nette\Object` a potažmo `Nette\Object` nabízely? Nabízíme přehled. (V ukázkách se používá třída `Nette\Object`, ale většina vlastnosti se týká i traity `Nette\SmartObject`).
78
-
79
-
80
-
Nekonzistentní chyby
81
-
--------------------
82
-
PHP mělo nekonzistentní chování při přístupu k nedeklarovaným členům. Stav v době vzniku `Nette\Object` byl následující:
83
-
84
-
```php
85
-
echo $obj->undeclared; // E_NOTICE, později E_WARNING
86
-
$obj->undeclared = 1; // projde tiše bez hlášení
87
-
$obj->unknownMethod(); // Fatal error (nezachytitelný pomocí try/catch)
88
-
```
89
-
90
-
Fatal error ukončil aplikaci bez možnosti jakkoliv reagovat. Tichý zápis do neexistujících členů bez upozornění mohl vést k závažným chybám, které šly obtížné odhalit. `Nette\Object` všechny tyto případy zachytával a vyhazoval výjimku `MemberAccessException`.
110
+
Od PHP 8.4 lze dosáhnout stejné funkcionality pomocí property hooks, které nabízí mnohem elegantnější a stručnější syntaxi:
PHP od verze PHP 7.0 už nezachytitelné fatal error nezpůsobuje a přístup k nedeklarovaným členům se stává chybou od PHP 8.2.
98
-
99
-
100
-
Did you mean?
101
-
-------------
102
-
Pokud došlo k vyhození chyby `Nette\MemberAccessException`, třeba z důvodu překlepu při přístupu k proměnné objektu nebo volání metody, pokusilo se `Nette\Object` v chybové hlášce napovědět, jak chybu opravit, a to v podobě ikonického dovětku „did you mean?“.
103
-
104
-
```php
105
-
class Foo extends Nette\Object
113
+
class Circle
106
114
{
107
-
public static function from($var)
108
-
{
115
+
public float $radius = 0.0 {
116
+
set => max(0.0, $value);
109
117
}
110
-
}
111
118
112
-
$foo = Foo::form($var);
113
-
// vyhodí Nette\MemberAccessException
114
-
// "Call to undefined static method Foo::form(), did you mean from()?"
119
+
public bool $visible {
120
+
get => $this->radius > 0;
121
+
}
122
+
}
115
123
```
116
124
117
-
Dnešní PHP sice nemá žádnou podobu „did you mean?“, ale tento dovětek umí do chyb doplňovat [Tracy|tracy:]. A dokonce takové chyby i [samo opravovat |tracy:open-files-in-ide#Ukázky].
118
-
119
125
120
126
Extension methods
121
127
-----------------
122
-
Inspirací byly extension methods z jazyka C#. Dávaly možnost do existujících tříd přidávat nové metody. Třeba jste si mohli do formuláře přidat metodu `addDateTime()`, která přidá vlastní DateTimePicker.
128
+
`Nette\Object` přinesl do PHP další zajímavý koncept inspirovaný moderními programovacími jazyky - extension methods. Tato funkce, převzatá z C#, umožnila vývojářům elegantně rozšiřovat existující třídy o nové metody bez nutnosti je upravovat nebo od nich dědit. Třeba jste si mohli do formuláře přidat metodu `addDateTime()`, která přidá vlastní DateTimePicker:
123
129
124
130
```php
125
131
Form::extensionMethod(
@@ -131,22 +137,22 @@ $form = new Form;
131
137
$form->addDateTime('date');
132
138
```
133
139
134
-
Extension metody se ukázaly jako nepraktické, protože jejich názvy nenapovídaly editory, naopak hlásily, že metoda neexistuje. Proto byla jejich podpora ukončena.
140
+
Extension metody se ukázaly jako nepraktické, protože jejich názvy nenapovídaly editory, naopak hlásily, že metoda neexistuje. Proto byla jejich podpora ukončena. Dnes je běžnější využívat kompozici nebo dědičnost pro rozšíření funkcionality tříd.
135
141
136
142
137
-
Zjištění názvu třídy:
138
-
---------------------
143
+
Zjištění názvu třídy
144
+
--------------------
145
+
Pro zjištění názvu třídy nabízel SmartObject jednoduchou metodu:
139
146
140
147
```php
141
148
$class = $obj->getClass(); // pomocí Nette\Object
142
149
$class = $obj::class; // od PHP 8.0
143
150
```
144
151
145
152
146
-
Přístup k reflexi a anotacem
153
+
Přístup k reflexi a anotacím
147
154
----------------------------
148
-
149
-
`Nette\Object` nabízel přístup k reflexi a anotacím pomocí metod `getReflection()` a `getAnnotation()`:
155
+
`Nette\Object` nabízel přístup k reflexi a anotacím pomocí metod `getReflection()` a `getAnnotation()`. Tento přístup významně zjednodušil práci s metainformacemi tříd:
`Nette\Object` nabízel elegantní způsob, jak předávat metody jako kdyby šlo o proměnné:
182
187
183
188
```php
@@ -194,7 +199,7 @@ $method = $obj->adder;
194
199
echo $method(2, 3); // 5
195
200
```
196
201
197
-
Od PHP 8.1 je možné využít tzv. "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax:
202
+
Od PHP 8.1 je možné využít tzv. "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, která tento koncept posouvá ještě dál:
198
203
199
204
```php
200
205
$obj = new Foo;
@@ -205,8 +210,7 @@ echo $method(2, 3); // 5
205
210
206
211
Události
207
212
--------
208
-
209
-
`Nette\Object` nabízel syntaktický cukr pro vyvolání [události|nette:glossary#události]:
213
+
SmartObject nabízí zjednodušenou syntax pro práci s [událostmi|nette:glossary#události]. Události umožňují objektům informovat ostatní části aplikace o změnách svého stavu:
210
214
211
215
```php
212
216
class Circle extends Nette\Object
@@ -221,7 +225,7 @@ class Circle extends Nette\Object
221
225
}
222
226
```
223
227
224
-
Kód `$this->onChange($this, $radius)` je ekvivalentní následujícímu:
228
+
Kód `$this->onChange($this, $radius)` je ekvivalentní následujícímu cyklu:
0 commit comments