From cfaa617f6424b05062c4201ec2b7ba923d7ac8ae Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 24 Nov 2024 15:36:05 +0100 Subject: [PATCH] smartobject: info about PHP 8.4 --- utils/bg/smartobject.texy | 180 +++++++++++++++++++------------------ utils/cs/smartobject.texy | 142 +++++++++++++++-------------- utils/de/smartobject.texy | 182 +++++++++++++++++++------------------ utils/el/smartobject.texy | 174 ++++++++++++++++++------------------ utils/en/smartobject.texy | 158 ++++++++++++++++---------------- utils/es/smartobject.texy | 182 +++++++++++++++++++------------------ utils/fr/smartobject.texy | 182 +++++++++++++++++++------------------ utils/hu/smartobject.texy | 176 ++++++++++++++++++------------------ utils/it/smartobject.texy | 170 ++++++++++++++++++----------------- utils/pl/smartobject.texy | 174 ++++++++++++++++++------------------ utils/pt/smartobject.texy | 180 +++++++++++++++++++------------------ utils/ro/smartobject.texy | 174 ++++++++++++++++++------------------ utils/ru/smartobject.texy | 183 +++++++++++++++++++------------------- utils/sl/smartobject.texy | 176 ++++++++++++++++++------------------ utils/tr/smartobject.texy | 180 +++++++++++++++++++------------------ utils/uk/smartobject.texy | 180 +++++++++++++++++++------------------ 16 files changed, 1428 insertions(+), 1365 deletions(-) diff --git a/utils/bg/smartobject.texy b/utils/bg/smartobject.texy index 9e32435002..76466e6529 100644 --- a/utils/bg/smartobject.texy +++ b/utils/bg/smartobject.texy @@ -2,28 +2,73 @@ SmartObject *********** .[perex] -SmartObject поправяше поведението на обектите по много начини, но днешният PHP вече включва повечето от тези подобрения. Въпреки това той все още добавя поддръжка за *собственост*. +SmartObject подобряваше поведението на обектите в PHP в продължение на много години. От версия PHP 8.4 всички негови функции са вече вградена част от самия PHP, с което завършва историческата му мисия да бъде пионер на модерния обектно-ориентиран подход в PHP. -Настройка: +Инсталация: ```shell composer require nette/utils ``` +SmartObject се появи през 2007 година като революционно решение на недостатъците на обектния модел на PHP по това време. Когато PHP страдаше от редица проблеми с обектно-ориентирания дизайн, той донесе значителни подобрения и опрости работата на разработчиците. Превърна се в легендарна част от Nette framework. Предлагаше функционалност, която PHP щеше да получи едва години по-късно - от валидация на достъпа до свойствата на обектите до усъвършенствана обработка на грешки. С появата на PHP 8.4 завърши своята историческа мисия, тъй като всичките му функции станаха вградена част от езика. Изпревари развитието на PHP със забележителни 17 години. -Свойства, getteri и setteri .[#toc-properties-getters-and-setters] -================================================================== +SmartObject премина през интересна техническа еволюция. Първоначално беше реализиран като клас `Nette\Object`, от който другите класове наследяваха необходимата функционалност. Значителна промяна настъпи с PHP 5.4, който въведе поддръжка на traits. Това позволи трансформацията в trait `Nette\SmartObject`, което донесе по-голяма гъвкавост - разработчиците можеха да използват функционалността дори в класове, които вече наследяваха от друг клас. Докато оригиналният клас `Nette\Object` престана да съществува с PHP 7.2 (който забрани именуването на класове с думата 'Object'), trait-ът `Nette\SmartObject` продължава да съществува. -В съвременните обектно-ориентирани езици (напр. C#, Python, Ruby, JavaScript) терминът *собственост* се отнася до [специални членове на класа |https://en.wikipedia.org/wiki/Property_(programming)], които изглеждат като променливи, но всъщност са представени като методи. Когато се присвоява или чете стойността на такава "променлива", се извиква съответният метод (наречен getter или setter). Това е много удобно, тъй като ни дава пълен контрол върху достъпа до променливите. Можем да проверяваме входни данни или да генерираме резултати само когато свойството е прочетено. +Нека разгледаме функциите, които `Nette\Object` и по-късно `Nette\SmartObject` предлагаха. Всяка от тези функции представляваше значителна стъпка напред в областта на обектно-ориентираното програмиране в PHP. -PHP свойствата не се поддържат, но чертите `Nette\SmartObject` могат да ги симулират. Как да го използвате? -- Добавяне на анотация към класа под формата на `@property $xyz` -- Създайте getter с име `getXyz()` или `isXyz()`, setter с име `setXyz()` -- Getter и setter трябва да са *публични* или *защитени* и незадължителни, така че може да има свойство *само за четене* или *само за писане*. +Последователни състояния на грешките +------------------------------------ +Един от най-сериозните проблеми на ранния PHP беше непоследователното поведение при работа с обекти. `Nette\Object` внесе ред и предвидимост в този хаос. Нека видим как се държеше PHP първоначално: -Ще използваме свойството за класа Circle, за да гарантираме, че в променливата `$radius` се поставят само неотрицателни числа. Заменете `public $radius` с имота: +```php +echo $obj->undeclared; // E_NOTICE, по-късно E_WARNING +$obj->undeclared = 1; // минава тихо без предупреждение +$obj->unknownMethod(); // Fatal error (неуловим с try/catch) +``` + +Fatal error прекратяваше приложението без възможност за реакция. Тихото записване в несъществуващи членове без предупреждение можеше да доведе до сериозни грешки, които бяха трудни за откриване. `Nette\Object` улавяше всички тези случаи и хвърляше изключение `MemberAccessException`, което позволяваше на програмистите да реагират и да обработват тези грешки: + +```php +echo $obj->undeclared; // хвърля Nette\MemberAccessException +$obj->undeclared = 1; // хвърля Nette\MemberAccessException +$obj->unknownMethod(); // хвърля Nette\MemberAccessException +``` + +От PHP 7.0 езикът вече не причинява неуловими fatal error, а от PHP 8.2 достъпът до недекларирани членове се счита за грешка. + + +"Did you mean?" Помощник +------------------------ +`Nette\Object` дойде с много удобна функция: интелигентни предложения при печатни грешки. Когато разработчик допуснеше грешка в името на метод или променлива, той не само съобщаваше за грешката, но и предлагаше помощ чрез предложение за правилното име. Това иконично съобщение, известно като "did you mean?", спести на програмистите часове търсене на печатни грешки: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// хвърля Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Въпреки че PHP няма собствена форма на "did you mean?", тази функция сега се предоставя от [Tracy|tracy:]. Той може дори да [поправя автоматично|tracy:open-files-in-ide#demos] такива грешки. + + +Properties с контролиран достъп +------------------------------- +Значителна иновация, която SmartObject донесе в PHP, бяха properties с контролиран достъп. Този концепт, обичаен в езици като C# или Python, позволи на разработчиците елегантно да контролират достъпа до данните на обекта и да осигуряват тяхната консистентност. Properties са мощен инструмент на обектно-ориентираното програмиране. Те функционират като променливи, но всъщност са представени чрез методи (getters и setters). Това позволява валидация на входа или генериране на стойности в момента на четене. + +За да използвате properties, трябва да: +- Добавите анотация `@property $xyz` към класа +- Създадете getter с име `getXyz()` или `isXyz()`, setter с име `setXyz()` +- Осигурите getter и setter да са *public* или *protected*. Те са опционални - следователно могат да съществуват като *read-only* или *write-only* properties + +Нека разгледаме практически пример с класа Circle, където ще използваме properties, за да гарантираме, че радиусът винаги е неотрицателно число. Ще заменим `public $radius` с property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // не е публичен + private float $radius = 0.0; // не е public! - // getter за свойството $radius + // getter за property $radius protected function getRadius(): float { return $this->radius; } - // setter за свойството $radius + // setter за property $radius protected function setRadius(float $radius): void { - // обработване на стойността, преди да я запишем + // санитизираме стойността преди записване $this->radius = max(0.0, $radius); } - // getter за свойството $visible + // getter за property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // всъщност се извиква setRadius(10) -echo $circle->radius; // извиква getRadius() +$circle->radius = 10; // всъщност извиква setRadius(10) +echo $circle->radius; // извиква getRadius() echo $circle->visible; // извиква isVisible() ``` -Свойствата са преди всичко "синтактична захар" и са предназначени да направят живота на програмиста по-сладък, като опростят кода. Ако нямате нужда от тях, не е нужно да ги използвате. - - -Поглед към историята .[#toc-a-glimpse-into-history] -=================================================== - -SmartObject усъвършенстваше поведението на обектите по множество начини, но днес PHP вече включва повечето от тези подобрения на роден език. Следващият текст е носталгичен поглед назад към историята, който ни напомня как са се развивали нещата. - -От самото си създаване обектният модел на PHP страдаше от множество сериозни недостатъци и пропуски. Това доведе до създаването на класа `Nette\Object` (през 2007 г.), който имаше за цел да поправи тези проблеми и да повиши удобството при използването на PHP. Всичко, което беше необходимо, беше други класове да наследят от него и да получат предимствата, които той предлагаше. Когато PHP 5.4 въведе поддръжка на черти, класът `Nette\Object` беше заменен с чертата `Nette\SmartObject`. Това премахна необходимостта от наследяване от общ предшественик. Освен това чертата можеше да се използва в класове, които вече наследяваха от друг клас. Окончателният край на `Nette\Object` настъпи с излизането на PHP 7.2, който забрани на класовете да се наричат `Object`. - -С развитието на PHP обектният модел и възможностите на езика се усъвършенстваха. Различните функции на класа `SmartObject` станаха излишни. След излизането на PHP 8.2 остана само една функция, която не се поддържа директно в PHP: възможността да се използват така наречените [свойства |#Properties, getters, and setters]. - -Какви функции предлагаха `Nette\Object` и, като следствие, `Nette\SmartObject`? Ето един преглед. (В примерите е използван класът `Nette\Object`, но повечето функции се отнасят и за признака `Nette\SmartObject` ). - - -Непоследователни грешки .[#toc-inconsistent-errors] ---------------------------------------------------- -PHP имаше непоследователно поведение при достъп до недекларирани членове. Към момента на публикуване на `Nette\Object` статусът е следният: - -```php -echo $obj->undeclared; // E_NOTICE, по-късно E_WARNING -$obj->undeclared = 1; // преминава безшумно, без да съобщава -$obj->unknownMethod(); // Фатална грешка (която не може да бъде уловена чрез try/catch) -``` - -Фатална грешка ще прекрати приложението без възможност за реакция. Тихото записване на несъществуващи членове без предупреждение можеше да доведе до сериозни грешки, които трудно се откриваха. `Nette\Object` Всички тези случаи бяха уловени и беше хвърлено изключение на адрес `MemberAccessException`. - -```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException -``` -От PHP 7.0 PHP вече не причинява фатални грешки, които не могат да бъдат прихванати, а достъпът до необявени членове е грешка от PHP 8.2 насам. - - -Какво имаш предвид? .[#toc-did-you-mean] ----------------------------------------- -Ако се появи грешка в `Nette\MemberAccessException`, вероятно поради печатна грешка при достъп до променлива на обект или извикване на метод, `Nette\Object` се опитва да подскаже в съобщението за грешка как да се поправи грешката под формата на емблематичното допълнение "Искахте ли да кажете?". +От PHP 8.4 същата функционалност може да бъде постигната чрез property hooks, които предлагат много по-елегантен и кратък синтаксис: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Въпреки че днешният PHP не разполага с функция "Искахте ли да кажете?", тази фраза може да бъде добавена към грешките от [Tracy |tracy:]. Тя дори може да [коригира такива грешки автоматично |tracy:open-files-in-ide#toc-demos]. - -Методи за разширение .[#toc-extension-methods] ----------------------------------------------- -Вдъхновено от методите за разширение от C#. Те ви позволяват да добавяте нови методи към съществуващи класове. Например можете да добавите метод `addDateTime()` към формуляр, за да добавите свой собствен DateTimePicker. +Extension Methods +----------------- +`Nette\Object` донесе в PHP друг интересен концепт, вдъхновен от модерните програмни езици - extension methods. Тази функция, заимствана от C#, позволи на разработчиците елегантно да разширяват съществуващите класове с нови методи без да ги модифицират или наследяват. Например, можехте да добавите метод `addDateTime()` към формуляр, който добавя персонализиран DateTimePicker: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Методите за разширение се оказаха непрактични, тъй като имената им не се попълваха автоматично от редакторите, а вместо това те съобщаваха, че методът не съществува. Поради това тяхната подкрепа беше преустановена. +Extension методите се оказаха непрактични, защото редакторите не предлагаха техните имена и вместо това съобщаваха, че методът не съществува. Затова тяхната поддръжка беше прекратена. Днес е по-често срещано да се използва композиция или наследяване за разширяване на функционалността на класовете. -Получаване на името на класа .[#toc-getting-the-class-name] ------------------------------------------------------------ +Получаване на името на класа +---------------------------- +SmartObject предлагаше прост метод за получаване на името на класа: ```php -$class = $obj->getClass(); // използване на Nette\Object -$class = $obj::class; // от PHP 8.0 +$class = $obj->getClass(); // чрез Nette\Object +$class = $obj::class; // от PHP 8.0 ``` -Достъп до рефлексии и анотации .[#toc-access-to-reflection-and-annotations] ---------------------------------------------------------------------------- - -`Nette\Object` Достъп до размишленията и анотациите чрез методите `getReflection()` и `getAnnotation()`: +Достъп до рефлексия и анотации +------------------------------ +`Nette\Object` предоставяше достъп до рефлексия и анотации чрез методите `getReflection()` и `getAnnotation()`. Този подход значително опрости работата с мета-информация на класовете: ```php /** @@ -161,7 +167,7 @@ $reflection = $obj->getReflection(); $reflection->getAnnotation('author'); // връща 'John Doe' ``` -От версия 8.0 на PHP вече е възможен достъп до метаинформация под формата на атрибути: +От PHP 8.0 е възможно да се достъпва до мета-информация чрез атрибути, които предлагат още повече възможности и по-добра проверка на типовете: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Получатели на методи .[#toc-method-getters] -------------------------------------------- - -`Nette\Object` предлагат елегантен начин за работа с методите, сякаш са променливи: +Method Getters +-------------- +`Nette\Object` предлагаше елегантен начин за предаване на методи, като че ли са променливи: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -От версия 8.1 на PHP можете да използвате така наречения "синтаксис на извикване на метод от първи клас":https://www.php.net/manual/en/functions.first_class_callable_syntax: +От PHP 8.1 можете да използвате така наречения "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, който развива този концепт още по-нататък: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Събития .[#toc-events] ----------------------- - -`Nette\Object` предлага синтактична захар за задействане на [събитие |nette:glossary#events]: +Събития +------- +SmartObject предлага опростен синтаксис за работа със [събития|nette:glossary#events]. Събитията позволяват на обектите да информират други части на приложението за промени в тяхното състояние: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Кодът `$this->onChange($this, $radius)` е еквивалентен на следното: +Кодът `$this->onChange($this, $radius)` е еквивалентен на следния цикъл: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -За по-голяма яснота препоръчваме да избягвате магическия метод `$this->onChange()`. Добър заместител би бил [Nette\Utils\Arrays::invoke: |arrays#invoke] +За по-голяма яснота препоръчваме да избягвате магическия метод `$this->onChange()`. Практична замяна е функцията [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/cs/smartobject.texy b/utils/cs/smartobject.texy index 7aeca5b7b2..93526bd1b5 100644 --- a/utils/cs/smartobject.texy +++ b/utils/cs/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -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*. +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. Instalace: @@ -11,19 +11,64 @@ Instalace: composer require nette/utils ``` +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. -Properties, gettery a settery -============================= +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. -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. +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. -PHP property nepodporují, ale traita `Nette\SmartObject` je umí imitovat. Jak na to? -- Přidejte třídě anotaci ve tvaru `@property $xyz` -- Vytvořte getter s názvem `getXyz()` nebo `isXyz()`, setter s názvem `setXyz()` -- Getter a setter musí být *public* nebo *protected* a jsou volitelné, mohou tedy existovat *read-only* nebo *write-only* property +Konzistentní chybové stavy +-------------------------- +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: -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: +```php +echo $obj->undeclared; // E_NOTICE, později E_WARNING +$obj->undeclared = 1; // projde tiše bez hlášení +$obj->unknownMethod(); // Fatal error (nezachytitelný pomocí try/catch) +``` + +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. + +```php +echo $obj->undeclared; // vyhodí Nette\MemberAccessException +$obj->undeclared = 1; // vyhodí Nette\MemberAccessException +$obj->unknownMethod(); // vyhodí Nette\MemberAccessException +``` + +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. + + +Nápověda "Did you mean?" +------------------------ +`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ů: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// vyhodí Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +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]. + + +Properties s kontrolovaným přístupem +------------------------------------ +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í. + +Pro používání properties musíte: +- Přidat třídě anotaci ve tvaru `@property $xyz` +- Vytvořit getter s názvem `getXyz()` nebo `isXyz()`, setter s názvem `setXyz()` +- Zajistit, aby getter a setter byly *public* nebo *protected*. Jsou volitelné - mohou tedy existovat jako *read-only* nebo *write-only* property + +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: ```php /** @@ -62,64 +107,25 @@ echo $circle->radius; // volá getRadius() echo $circle->visible; // volá isVisible() ``` -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. - - -Pohled do historie -================== - -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. - -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`. - -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]. - -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`). - - -Nekonzistentní chyby --------------------- -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í: - -```php -echo $obj->undeclared; // E_NOTICE, později E_WARNING -$obj->undeclared = 1; // projde tiše bez hlášení -$obj->unknownMethod(); // Fatal error (nezachytitelný pomocí try/catch) -``` - -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`. +Od PHP 8.4 lze dosáhnout stejné funkcionality pomocí property hooks, které nabízí mnohem elegantnější a stručnější syntaxi: ```php -echo $obj->undeclared; // vyhodí Nette\MemberAccessException -$obj->undeclared = 1; // vyhodí Nette\MemberAccessException -$obj->unknownMethod(); // vyhodí Nette\MemberAccessException -``` -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. - - -Did you mean? -------------- -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?“. - -```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// vyhodí Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -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]. - Extension methods ----------------- -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. +`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: ```php Form::extensionMethod( @@ -131,11 +137,12 @@ $form = new Form; $form->addDateTime('date'); ``` -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. +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. -Zjištění názvu třídy: ---------------------- +Zjištění názvu třídy +-------------------- +Pro zjištění názvu třídy nabízel SmartObject jednoduchou metodu: ```php $class = $obj->getClass(); // pomocí Nette\Object @@ -143,10 +150,9 @@ $class = $obj::class; // od PHP 8.0 ``` -Přístup k reflexi a anotacem +Přístup k reflexi a anotacím ---------------------------- - -`Nette\Object` nabízel přístup k reflexi a anotacím pomocí metod `getReflection()` a `getAnnotation()`: +`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: ```php /** @@ -161,7 +167,7 @@ $reflection = $obj->getReflection(); $reflection->getAnnotation('author'); // vrátí 'John Doe' ``` -Od PHP 8.0 je možné přistupovat k metainformacím v podobě atributů: +Od PHP 8.0 je možné přistupovat k metainformacím v podobě atributů, které nabízí ještě větší možnosti a lepší typovou kontrolu: ```php #[Author('John Doe')] @@ -177,7 +183,6 @@ $reflection->getAttributes(Author::class)[0]; Method gettery -------------- - `Nette\Object` nabízel elegantní způsob, jak předávat metody jako kdyby šlo o proměnné: ```php @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -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: +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: ```php $obj = new Foo; @@ -205,8 +210,7 @@ echo $method(2, 3); // 5 Události -------- - -`Nette\Object` nabízel syntaktický cukr pro vyvolání [události|nette:glossary#události]: +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: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Kód `$this->onChange($this, $radius)` je ekvivalentní následujícímu: +Kód `$this->onChange($this, $radius)` je ekvivalentní následujícímu cyklu: ```php foreach ($this->onChange as $callback) { diff --git a/utils/de/smartobject.texy b/utils/de/smartobject.texy index 65643bc2e0..a8edf977a8 100644 --- a/utils/de/smartobject.texy +++ b/utils/de/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject hat das Verhalten von Objekten in vielerlei Hinsicht verbessert, aber das heutige PHP enthält die meisten dieser Verbesserungen bereits von Haus aus. Es fügt jedoch noch Unterstützung für *Property* hinzu. +SmartObject verbesserte jahrelang das Verhalten von Objekten in PHP. Seit PHP 8.4 sind alle seine Funktionen nativ in PHP selbst integriert, womit er seine historische Mission als Wegbereiter des modernen objektorientierten Ansatzes in PHP erfüllt hat. Installation: @@ -11,19 +11,64 @@ Installation: composer require nette/utils ``` +SmartObject entstand 2007 als revolutionäre Lösung für die Mängel des damaligen PHP-Objektmodells. In einer Zeit, als PHP unter zahlreichen Problemen mit objektorientiertem Design litt, brachte er bedeutende Verbesserungen und vereinfachte die Arbeit der Entwickler. Er wurde zu einem legendären Bestandteil des Nette Frameworks. Er bot Funktionalitäten, die PHP erst Jahre später erhielt - von der Validierung des Eigenschaftszugriffs bis hin zur ausgefeilten Fehlerbehandlung. Mit der Einführung von PHP 8.4 hat er seine historische Mission erfüllt, da alle seine Funktionen zu nativen Bestandteilen der Sprache wurden. SmartObject war der PHP-Entwicklung um bemerkenswerte 17 Jahre voraus. -Eigenschaften, Getters und Setters .[#toc-properties-getters-and-setters] -========================================================================= +SmartObject durchlief eine interessante technische Entwicklung. Ursprünglich wurde er als Klasse `Nette\Object` implementiert, von der andere Klassen die benötigte Funktionalität erbten. Eine wesentliche Änderung kam mit PHP 5.4, das Trait-Unterstützung einführte. Dies ermöglichte die Umwandlung in den `Nette\SmartObject` Trait, was größere Flexibilität brachte - Entwickler konnten die Funktionalität auch in Klassen nutzen, die bereits von einer anderen Klasse erbten. Während die ursprüngliche `Nette\Object`-Klasse mit PHP 7.2 verschwand (das die Benennung von Klassen mit dem Wort 'Object' verbot), lebt der `Nette\SmartObject` Trait weiter. -In modernen objektorientierten Sprachen (z. B. C#, Python, Ruby, JavaScript) bezieht sich der Begriff *Eigenschaft* auf [spezielle Mitglieder von Klassen |https://en.wikipedia.org/wiki/Property_(programming)], die wie Variablen aussehen, aber eigentlich durch Methoden repräsentiert werden. Wenn der Wert dieser "Variablen" zugewiesen oder gelesen wird, wird die entsprechende Methode (Getter oder Setter genannt) aufgerufen. Das ist sehr praktisch, denn es gibt uns die volle Kontrolle über den Zugriff auf Variablen. Wir können die Eingabe validieren oder Ergebnisse nur dann erzeugen, wenn die Eigenschaft gelesen wird. +Schauen wir uns die Funktionen an, die `Nette\Object` und später `Nette\SmartObject` anboten. Jede dieser Funktionen stellte zu ihrer Zeit einen bedeutenden Fortschritt in der objektorientierten Programmierung in PHP dar. -PHP-Eigenschaften werden nicht unterstützt, aber Trait `Nette\SmartObject` kann sie imitieren. Wie verwendet man es? -- Fügen Sie der Klasse eine Annotation in der Form `@property $xyz` -- Erstellen Sie einen Getter namens `getXyz()` oder `isXyz()`, einen Setter namens `setXyz()` -- Die Getter und Setter müssen *public* oder *protected* sein und sind optional, d.h. es kann eine *read-only* oder *write-only* Eigenschaft geben +Konsistente Fehlerzustände +-------------------------- +Eines der größten Probleme des frühen PHP war das inkonsistente Verhalten bei der Arbeit mit Objekten. `Nette\Object` brachte Ordnung und Vorhersehbarkeit in dieses Chaos. Betrachten wir das ursprüngliche PHP-Verhalten: -Wir werden die Eigenschaft für die Klasse Circle verwenden, um sicherzustellen, dass nur nicht-negative Zahlen in die Variable `$radius` eingegeben werden. Ersetzen Sie `public $radius` durch property: +```php +echo $obj->undeclared; // E_NOTICE, später E_WARNING +$obj->undeclared = 1; // läuft still durch ohne Warnung +$obj->unknownMethod(); // Fatal error (nicht abfangbar mit try/catch) +``` + +Fatal Error beendete die Anwendung ohne Möglichkeit zu reagieren. Stilles Schreiben in nicht existierende Mitglieder ohne Warnung konnte zu schwerwiegenden Fehlern führen, die schwer zu erkennen waren. `Nette\Object` fing all diese Fälle ab und warf eine `MemberAccessException`, was Programmierern ermöglichte, auf Fehler zu reagieren: + +```php +echo $obj->undeclared; // wirft Nette\MemberAccessException +$obj->undeclared = 1; // wirft Nette\MemberAccessException +$obj->unknownMethod(); // wirft Nette\MemberAccessException +``` + +Seit PHP 7.0 verursacht die Sprache keine nicht abfangbaren Fatal Errors mehr, und seit PHP 8.2 wird der Zugriff auf nicht deklarierte Mitglieder als Fehler betrachtet. + + +"Did you mean?" Hilfe +--------------------- +`Nette\Object` kam mit einer sehr praktischen Funktion: intelligenten Vorschlägen bei Tippfehlern. Wenn ein Entwickler einen Fehler im Namen einer Methode oder Variable machte, wurde nicht nur der Fehler gemeldet, sondern auch eine Hilfestellung in Form eines Vorschlags für den richtigen Namen angeboten. Diese ikonische Meldung, bekannt als "did you mean?", ersparte Programmierern stundenlange Suche nach Tippfehlern: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// wirft Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Während PHP selbst keine Form von "did you mean?" hat, wird diese Funktion jetzt von [Tracy|tracy:] bereitgestellt. Es kann solche Fehler sogar [automatisch korrigieren|tracy:open-files-in-ide#demos]. + + +Properties mit kontrolliertem Zugriff +------------------------------------- +Eine bedeutende Innovation, die SmartObject in PHP einführte, waren Properties mit kontrolliertem Zugriff. Dieses Konzept, das in Sprachen wie C# oder Python üblich ist, ermöglichte Entwicklern eine elegante Kontrolle über den Zugriff auf Objektdaten und deren Konsistenz. Properties sind ein mächtiges Werkzeug der objektorientierten Programmierung. Sie funktionieren wie Variablen, werden aber tatsächlich durch Methoden (Getter und Setter) repräsentiert. Dies ermöglicht die Validierung von Eingaben oder die Generierung von Werten zum Zeitpunkt des Lesens. + +Für die Verwendung von Properties müssen Sie: +- Der Klasse die Annotation `@property $xyz` hinzufügen +- Einen Getter mit Namen `getXyz()` oder `isXyz()`, einen Setter mit Namen `setXyz()` erstellen +- Sicherstellen, dass Getter und Setter *public* oder *protected* sind. Sie sind optional - können also als *read-only* oder *write-only* Properties existieren + +Schauen wir uns ein praktisches Beispiel an der Circle-Klasse an, wo wir Properties verwenden, um sicherzustellen, dass der Radius immer nicht-negativ ist. Wir ersetzen `public $radius` durch eine Property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // not public + private float $radius = 0.0; // nicht public! - // getter for property $radius + // Getter für Property $radius protected function getRadius(): float { return $this->radius; } - // setter for property $radius + // Setter für Property $radius protected function setRadius(float $radius): void { - // sanitizing value before saving it + // Wert vor dem Speichern bereinigen $this->radius = max(0.0, $radius); } - // getter for property $visible + // Getter für Property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // actually calls setRadius(10) -echo $circle->radius; // calls getRadius() -echo $circle->visible; // calls isVisible() -``` - -Eigenschaften sind in erster Linie "syntaktischer Zucker" ((syntactic sugar)), der das Leben des Programmierers durch Vereinfachung des Codes versüßen soll. Wenn Sie sie nicht wollen, müssen Sie sie nicht verwenden. - - -Ein Blick in die Geschichte .[#toc-a-glimpse-into-history] -========================================================== - -SmartObject verfeinerte das Verhalten von Objekten auf zahlreiche Arten, aber das heutige PHP enthält die meisten dieser Erweiterungen bereits von Haus aus. Der folgende Text ist ein nostalgischer Blick zurück in die Geschichte, der uns daran erinnert, wie sich die Dinge entwickelt haben. - -Seit seinen Anfängen litt das Objektmodell von PHP unter einer Vielzahl von ernsthaften Mängeln und Unzulänglichkeiten. Dies führte zur Schaffung der Klasse `Nette\Object` (im Jahr 2007), die diese Probleme beheben und den Komfort bei der Verwendung von PHP erhöhen sollte. Andere Klassen brauchten nur von dieser Klasse zu erben, um die Vorteile zu nutzen, die sie bot. Als mit PHP 5.4 die Unterstützung für Traits eingeführt wurde, wurde die Klasse `Nette\Object` durch den Trait `Nette\SmartObject` ersetzt. Damit entfiel die Notwendigkeit, von einem gemeinsamen Vorfahren zu erben. Außerdem konnte der Trait in Klassen verwendet werden, die bereits von einer anderen Klasse geerbt hatten. Das endgültige Ende von `Nette\Object` kam mit der Veröffentlichung von PHP 7.2, die es verbot, Klassen den Namen `Object` zu geben. - -Mit der weiteren Entwicklung von PHP wurden das Objektmodell und die Sprachfähigkeiten verbessert. Verschiedene Funktionen der Klasse `SmartObject` wurden überflüssig. Seit der Veröffentlichung von PHP 8.2 gibt es nur noch eine Funktion, die in PHP nicht direkt unterstützt wird: die Möglichkeit, so genannte [Properties |#Properties, getters, and setters] zu verwenden. - -Welche Funktionen boten `Nette\Object` und in der Folge `Nette\SmartObject`? Hier ist ein Überblick. (In den Beispielen wird die Klasse `Nette\Object` verwendet, aber die meisten Funktionen gelten auch für den Trait `Nette\SmartObject` ). - - -Inkonsistente Fehler .[#toc-inconsistent-errors] ------------------------------------------------- -PHP hatte ein inkonsistentes Verhalten beim Zugriff auf nicht deklarierte Mitglieder. Der Zustand zum Zeitpunkt von `Nette\Object` war wie folgt: - -```php -echo $obj->undeclared; // E_NOTICE, later E_WARNING -$obj->undeclared = 1; // passes silently without reporting -$obj->unknownMethod(); // Fatal error (not catchable by try/catch) +$circle->radius = 10; // ruft tatsächlich setRadius(10) auf +echo $circle->radius; // ruft getRadius() auf +echo $circle->visible; // ruft isVisible() auf ``` -Ein schwerwiegender Fehler beendete die Anwendung, ohne dass eine Möglichkeit zur Reaktion bestand. Das stille Schreiben auf nicht existierende Mitglieder ohne Warnung konnte zu schwerwiegenden Fehlern führen, die schwer zu erkennen waren. `Nette\Object` Alle diese Fälle wurden abgefangen und eine Ausnahme `MemberAccessException` wurde ausgelöst. - -```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException -``` -Seit PHP 7.0 verursacht PHP keine nicht abfangbaren fatalen Fehler mehr, und der Zugriff auf nicht deklarierte Member ist seit PHP 8.2 ein Fehler. - - -Haben Sie gemeint? .[#toc-did-you-mean] ---------------------------------------- -Wenn ein `Nette\MemberAccessException` -Fehler ausgelöst wurde, etwa aufgrund eines Tippfehlers beim Zugriff auf eine Objektvariable oder beim Aufruf einer Methode, versuchte `Nette\Object`, in der Fehlermeldung einen Hinweis darauf zu geben, wie der Fehler zu beheben ist, und zwar in Form des ikonischen Zusatzes "Meinten Sie?". +Seit PHP 8.4 kann die gleiche Funktionalität mit Property Hooks erreicht werden, die eine elegantere und kürzere Syntax bieten: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Das heutige PHP hat zwar keine "Meinten Sie?"-Funktion, aber dieser Satz kann von [Tracy |tracy:] zu Fehlern hinzugefügt werden. Es kann [solche Fehler |tracy:open-files-in-ide#toc-demos] sogar [automatisch korrigieren |tracy:open-files-in-ide#toc-demos]. - -Erweiterungsmethoden .[#toc-extension-methods] ----------------------------------------------- -Inspiriert von Erweiterungsmethoden aus C#. Sie bieten die Möglichkeit, neue Methoden zu bestehenden Klassen hinzuzufügen. Zum Beispiel könnten Sie die Methode `addDateTime()` zu einem Formular hinzufügen, um Ihren eigenen DateTimePicker hinzuzufügen. +Extension Methods +----------------- +`Nette\Object` brachte ein weiteres interessantes Konzept nach PHP, inspiriert von modernen Programmiersprachen - Extension Methods. Diese Funktion, die von C# übernommen wurde, ermöglichte Entwicklern, bestehende Klassen elegant um neue Methoden zu erweitern, ohne sie zu modifizieren oder von ihnen zu erben. Zum Beispiel konnten Sie einem Formular eine `addDateTime()`-Methode hinzufügen, die einen benutzerdefinierten DateTimePicker hinzufügt: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Erweiterungsmethoden erwiesen sich als unpraktisch, da ihre Namen von Editoren nicht automatisch vervollständigt wurden, sondern sie meldeten, dass die Methode nicht existierte. Daher wurde ihre Unterstützung eingestellt. +Extension Methods erwiesen sich als unpraktisch, da Editoren ihre Namen nicht vorschlugen und stattdessen meldeten, dass die Methode nicht existiert. Daher wurde ihre Unterstützung eingestellt. Heute ist es üblicher, Komposition oder Vererbung zu verwenden, um Klassenfunktionalität zu erweitern. -Ermitteln des Klassennamens .[#toc-getting-the-class-name] ----------------------------------------------------------- +Klassennamen ermitteln +---------------------- +SmartObject bot eine einfache Methode zum Ermitteln des Klassennamens: ```php -$class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // since PHP 8.0 +$class = $obj->getClass(); // mit Nette\Object +$class = $obj::class; // seit PHP 8.0 ``` -Zugang zu Reflexion und Anmerkungen .[#toc-access-to-reflection-and-annotations] --------------------------------------------------------------------------------- - -`Nette\Object` bietet den Zugang zu Reflexion und Kommentaren mit den Methoden `getReflection()` und `getAnnotation()`: +Zugriff auf Reflection und Annotationen +--------------------------------------- +`Nette\Object` bot Zugriff auf Reflection und Annotationen durch die Methoden `getReflection()` und `getAnnotation()`. Dieser Ansatz vereinfachte die Arbeit mit Klassen-Metainformationen erheblich: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // returns 'John Doe' +$reflection->getAnnotation('author'); // gibt 'John Doe' zurück ``` -Seit PHP 8.0 ist es möglich, auf Metainformationen in Form von Attributen zuzugreifen: +Seit PHP 8.0 ist es möglich, auf Metainformationen durch Attribute zuzugreifen, die noch mehr Möglichkeiten und bessere Typenprüfung bieten: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Methoden-Getter .[#toc-method-getters] --------------------------------------- - -`Nette\Object` boten eine elegante Möglichkeit, mit Methoden so umzugehen, als wären sie Variablen: +Method Getters +-------------- +`Nette\Object` bot eine elegante Möglichkeit, Methoden wie Variablen zu übergeben: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Seit PHP 8.1 können Sie die sogenannte "First-Class-Callable-Syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax verwenden: +Seit PHP 8.1 können Sie die "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax verwenden, die dieses Konzept noch weiter führt: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Ereignisse .[#toc-events] -------------------------- - -`Nette\Object` bietet syntaktischen Zucker zum Auslösen des [Ereignisses |nette:glossary#events]: +Events +------ +SmartObject bietet eine vereinfachte Syntax für die Arbeit mit [Events|nette:glossary#events]. Events ermöglichen es Objekten, andere Teile der Anwendung über Änderungen ihres Zustands zu informieren: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Der Code `$this->onChange($this, $radius)` entspricht folgendem: +Der Code `$this->onChange($this, $radius)` ist äquivalent zu folgender Schleife: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Aus Gründen der Übersichtlichkeit wird empfohlen, die magische Methode `$this->onChange()` zu vermeiden. Ein guter Ersatz ist [Nette\Utils\Arrays::invoke |arrays#invoke]: +Der Übersichtlichkeit halber empfehlen wir, die magische Methode `$this->onChange()` zu vermeiden. Ein praktischer Ersatz ist die Funktion [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/el/smartobject.texy b/utils/el/smartobject.texy index e73cc73d15..d824b22108 100644 --- a/utils/el/smartobject.texy +++ b/utils/el/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -Το SmartObject διόρθωνε τη συμπεριφορά των αντικειμένων με πολλούς τρόπους, αλλά η σημερινή PHP περιλαμβάνει ήδη τις περισσότερες από αυτές τις βελτιώσεις εγγενώς. Ωστόσο, εξακολουθεί να προσθέτει υποστήριξη για *property*. +Το SmartObject βελτίωνε για χρόνια τη συμπεριφορά των αντικειμένων στην PHP. Από την έκδοση PHP 8.4, όλες οι λειτουργίες του έχουν ενσωματωθεί στην ίδια την PHP, ολοκληρώνοντας έτσι την ιστορική του αποστολή ως πρωτοπόρος του σύγχρονου αντικειμενοστρεφούς προγραμματισμού στην PHP. Εγκατάσταση: @@ -11,19 +11,64 @@ SmartObject composer require nette/utils ``` +Το SmartObject δημιουργήθηκε το 2007 ως μια επαναστατική λύση στις ελλείψεις του τότε μοντέλου αντικειμένων της PHP. Σε μια εποχή που η PHP αντιμετώπιζε πολλά προβλήματα με τον αντικειμενοστρεφή σχεδιασμό, έφερε σημαντικές βελτιώσεις και απλοποίησε την εργασία των προγραμματιστών. Έγινε θρυλικό μέρος του framework Nette. Πρόσφερε λειτουργικότητα που η PHP απέκτησε πολλά χρόνια αργότερα - από την επικύρωση πρόσβασης σε ιδιότητες αντικειμένων μέχρι τον εξελιγμένο χειρισμό σφαλμάτων. Με την έλευση της PHP 8.4, ολοκλήρωσε την ιστορική του αποστολή, καθώς όλες οι λειτουργίες του έγιναν εγγενή μέρη της γλώσσας. Προηγήθηκε της εξέλιξης της PHP κατά αξιοσημείωτα 17 χρόνια. -Ιδιότητες, Getters και Setters .[#toc-properties-getters-and-setters] -===================================================================== +Τεχνικά, το SmartObject πέρασε από μια ενδιαφέρουσα εξέλιξη. Αρχικά υλοποιήθηκε ως κλάση `Nette\Object`, από την οποία άλλες κλάσεις κληρονομούσαν την απαιτούμενη λειτουργικότητα. Μια σημαντική αλλαγή ήρθε με την PHP 5.4, η οποία έφερε την υποστήριξη για traits. Αυτό επέτρεψε τη μετατροπή του σε trait `Nette\SmartObject`, προσφέροντας μεγαλύτερη ευελιξία - οι προγραμματιστές μπορούσαν να χρησιμοποιήσουν τη λειτουργικότητα ακόμη και σε κλάσεις που ήδη κληρονομούσαν από άλλη κλάση. Ενώ η αρχική κλάση `Nette\Object` έπαψε να υπάρχει με την PHP 7.2 (η οποία απαγόρευσε την ονομασία κλάσεων με τη λέξη 'Object'), το trait `Nette\SmartObject` συνεχίζει να υπάρχει. -Στις σύγχρονες αντικειμενοστραφείς γλώσσες (π.χ. C#, Python, Ruby, JavaScript), ο όρος *ιδιότητα* αναφέρεται σε [ειδικά μέλη των κλάσεων |https://en.wikipedia.org/wiki/Property_(programming)] που μοιάζουν με μεταβλητές αλλά στην πραγματικότητα αντιπροσωπεύονται από μεθόδους. Όταν η τιμή αυτής της "μεταβλητής" εκχωρείται ή διαβάζεται, καλείται η αντίστοιχη μέθοδος (που ονομάζεται getter ή setter). Αυτό είναι κάτι πολύ βολικό, μας δίνει πλήρη έλεγχο της πρόσβασης στις μεταβλητές. Μπορούμε να επικυρώσουμε την είσοδο ή να δημιουργήσουμε αποτελέσματα μόνο όταν διαβάζεται η ιδιότητα. +Ας εξερευνήσουμε τις λειτουργίες που προσέφεραν το `Nette\Object` και αργότερα το `Nette\SmartObject`. Κάθε μία από αυτές τις λειτουργίες αποτελούσε ένα σημαντικό βήμα προόδου στον αντικειμενοστρεφή προγραμματισμό της PHP. -Οι ιδιότητες της PHP δεν υποστηρίζονται, αλλά το trait `Nette\SmartObject` μπορεί να τις μιμηθεί. Πώς να το χρησιμοποιήσετε; -- Προσθέστε ένα σχόλιο στην κλάση με τη μορφή `@property $xyz` -- Δημιουργήστε έναν getter με όνομα `getXyz()` ή `isXyz()`, έναν setter με όνομα `setXyz()` -- Ο getter και ο setter πρέπει να είναι *δημόσιος* ή *προστατευμένος* και είναι προαιρετικοί, οπότε μπορεί να υπάρχει μια ιδιότητα *read-only* ή *write-only*. +Συνεπής Διαχείριση Σφαλμάτων +---------------------------- +Ένα από τα πιο επείγοντα προβλήματα της πρώιμης PHP ήταν η ασυνεπής συμπεριφορά κατά την εργασία με αντικείμενα. Το `Nette\Object` έφερε τάξη και προβλεψιμότητα σε αυτό το χάος. Ας δούμε πώς συμπεριφερόταν αρχικά η PHP: -Θα χρησιμοποιήσουμε την ιδιότητα για την κλάση Circle για να διασφαλίσουμε ότι μόνο μη αρνητικοί αριθμοί μπαίνουν στη μεταβλητή `$radius`. Αντικαταστήστε το `public $radius` με το property: +```php +echo $obj->undeclared; // E_NOTICE, αργότερα E_WARNING +$obj->undeclared = 1; // περνάει σιωπηλά χωρίς προειδοποίηση +$obj->unknownMethod(); // Fatal error (μη πιάσιμο με try/catch) +``` + +Το Fatal error τερμάτιζε την εφαρμογή χωρίς δυνατότητα αντίδρασης. Η σιωπηλή εγγραφή σε μη υπάρχοντα μέλη χωρίς προειδοποίηση μπορούσε να οδηγήσει σε σοβαρά σφάλματα που ήταν δύσκολο να εντοπιστούν. Το `Nette\Object` έπιανε όλες αυτές τις περιπτώσεις και έριχνε μια εξαίρεση `MemberAccessException`, επιτρέποντας στους προγραμματιστές να αντιδρούν και να χειρίζονται αυτά τα σφάλματα: + +```php +echo $obj->undeclared; // ρίχνει Nette\MemberAccessException +$obj->undeclared = 1; // ρίχνει Nette\MemberAccessException +$obj->unknownMethod(); // ρίχνει Nette\MemberAccessException +``` + +Από την PHP 7.0, η γλώσσα δεν προκαλεί πλέον μη πιάσιμα fatal errors και από την PHP 8.2, η πρόσβαση σε μη δηλωμένα μέλη θεωρείται σφάλμα. + + +Βοήθεια "Did you mean?" +----------------------- +Το `Nette\Object` ήρθε με μια πολύ βολική λειτουργία: έξυπνες προτάσεις για τυπογραφικά λάθη. Όταν ένας προγραμματιστής έκανε λάθος στο όνομα μιας μεθόδου ή μεταβλητής, όχι μόνο ανέφερε το σφάλμα αλλά πρόσφερε και βοήθεια προτείνοντας το σωστό όνομα. Αυτό το εμβληματικό μήνυμα, γνωστό ως "did you mean?", εξοικονόμησε στους προγραμματιστές ώρες αναζήτησης τυπογραφικών λαθών: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// ρίχνει Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Ενώ η PHP δεν έχει καμία μορφή "did you mean?", αυτή η λειτουργία παρέχεται τώρα από το [Tracy|tracy:]. Μπορεί ακόμη και να [διορθώνει αυτόματα|tracy:open-files-in-ide#demos] τέτοια σφάλματα. + + +Ιδιότητες με Ελεγχόμενη Πρόσβαση +-------------------------------- +Μια σημαντική καινοτομία που έφερε το SmartObject στην PHP ήταν οι ιδιότητες με ελεγχόμενη πρόσβαση (properties). Αυτή η έννοια, κοινή σε γλώσσες όπως η C# ή η Python, επέτρεψε στους προγραμματιστές να ελέγχουν κομψά την πρόσβαση στα δεδομένα αντικειμένων και να διασφαλίζουν τη συνέπειά τους. Οι ιδιότητες είναι ένα ισχυρό εργαλείο αντικειμενοστρεφούς προγραμματισμού. Λειτουργούν ως μεταβλητές αλλά στην πραγματικότητα αντιπροσωπεύονται από μεθόδους (getters και setters). Αυτό επιτρέπει την επικύρωση εισόδων ή τη δημιουργία τιμών τη στιγμή της ανάγνωσης. + +Για να χρησιμοποιήσετε τις ιδιότητες, πρέπει να: +- Προσθέσετε στην κλάση την επισήμανση `@property $xyz` +- Δημιουργήσετε getter με όνομα `getXyz()` ή `isXyz()`, setter με όνομα `setXyz()` +- Διασφαλίσετε ότι ο getter και ο setter είναι *public* ή *protected*. Είναι προαιρετικοί - άρα μπορούν να υπάρχουν ως ιδιότητες *μόνο-ανάγνωσης* ή *μόνο-εγγραφής* + +Ας δούμε ένα πρακτικό παράδειγμα χρησιμοποιώντας την κλάση Circle, όπου θα χρησιμοποιήσουμε ιδιότητες για να διασφαλίσουμε ότι η ακτίνα είναι πάντα μη αρνητική. Θα αντικαταστήσουμε το `public $radius` με μια ιδιότητα: ```php /** @@ -34,7 +79,7 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // όχι δημόσια + private float $radius = 0.0; // όχι public! // getter για την ιδιότητα $radius protected function getRadius(): float @@ -45,7 +90,7 @@ class Circle // setter για την ιδιότητα $radius protected function setRadius(float $radius): void { - // εξυγίανση της τιμής πριν από την αποθήκευση + // καθαρίζουμε την τιμή πριν την αποθήκευση $this->radius = max(0.0, $radius); } @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // στην πραγματικότητα καλεί την setRadius(10) -echo $circle->radius; // καλεί την getRadius() -echo $circle->visible; // καλεί την isVisible() -``` - -Οι ιδιότητες είναι κατά κύριο λόγο "συντακτική ζάχαρη"((syntactic sugar)), η οποία έχει σκοπό να κάνει τη ζωή του προγραμματιστή πιο γλυκιά απλοποιώντας τον κώδικα. Αν δεν τις θέλετε, δεν χρειάζεται να τις χρησιμοποιήσετε. - - -Μια ματιά στην ιστορία .[#toc-a-glimpse-into-history] -===================================================== - -Το SmartObject χρησιμοποιείται για να βελτιώσει τη συμπεριφορά των αντικειμένων με πολλούς τρόπους, αλλά η σημερινή PHP ενσωματώνει ήδη τις περισσότερες από αυτές τις βελτιώσεις εγγενώς. Το κείμενο που ακολουθεί είναι μια νοσταλγική αναδρομή στην ιστορία, υπενθυμίζοντάς μας πώς εξελίχθηκαν τα πράγματα. - -Από την αρχή της δημιουργίας του, το μοντέλο αντικειμένων της PHP υπέφερε από μυριάδες σοβαρές ελλείψεις και αδυναμίες. Αυτό οδήγησε στη δημιουργία της κλάσης `Nette\Object` (το 2007), η οποία είχε ως στόχο να διορθώσει αυτά τα προβλήματα και να βελτιώσει την άνεση της χρήσης της PHP. Το μόνο που χρειαζόταν ήταν άλλες κλάσεις να κληρονομήσουν από αυτήν και θα αποκτούσαν τα οφέλη που προσέφερε. Όταν η PHP 5.4 εισήγαγε την υποστήριξη για γνωρίσματα, η κλάση `Nette\Object` αντικαταστάθηκε από το γνώρισμα `Nette\SmartObject`. Αυτό εξάλειψε την ανάγκη να κληρονομήσετε από έναν κοινό πρόγονο. Επιπλέον, το γνώρισμα μπορούσε να χρησιμοποιηθεί σε κλάσεις που ήδη κληρονομούσαν από μια άλλη κλάση. Το οριστικό τέλος του `Nette\Object` ήρθε με την κυκλοφορία της PHP 7.2, η οποία απαγόρευσε στις κλάσεις να ονομάζονται `Object`. - -Καθώς η ανάπτυξη της PHP συνεχιζόταν, το μοντέλο αντικειμένων και οι δυνατότητες της γλώσσας βελτιώνονταν. Διάφορες λειτουργίες της κλάσης `SmartObject` έγιναν περιττές. Από την έκδοση της PHP 8.2, παραμένει μόνο ένα χαρακτηριστικό που δεν υποστηρίζεται άμεσα στην PHP: η δυνατότητα χρήσης των λεγόμενων [ιδιοτήτων |#Properties, getters, and setters]. - -Ποια χαρακτηριστικά προσέφερε η `Nette\Object` και, κατ' επέκταση, η `Nette\SmartObject`; Ακολουθεί μια επισκόπηση. (Στα παραδείγματα χρησιμοποιείται η κλάση `Nette\Object`, αλλά τα περισσότερα χαρακτηριστικά ισχύουν και για την ιδιότητα `Nette\SmartObject` ). - - -Ασυνεπή σφάλματα .[#toc-inconsistent-errors] --------------------------------------------- -Η PHP είχε ασυνεπή συμπεριφορά κατά την πρόσβαση σε μη δηλωμένα μέλη. Η κατάσταση κατά τη στιγμή του `Nette\Object` ήταν η εξής: - -```php -echo $obj->undeclared; // E_NOTICE, αργότερα E_WARNING -$obj->undeclared = 1; // περνάει σιωπηλά χωρίς αναφορά -$obj->unknownMethod(); // Μοιραίο σφάλμα (δεν μπορεί να πιαστεί από try/catch) +$circle->radius = 10; // στην πραγματικότητα καλεί το setRadius(10) +echo $circle->radius; // καλεί το getRadius() +echo $circle->visible; // καλεί το isVisible() ``` -Το μοιραίο σφάλμα τερμάτισε την εφαρμογή χωρίς καμία δυνατότητα αντίδρασης. Η σιωπηλή εγγραφή σε ανύπαρκτα μέλη χωρίς προειδοποίηση θα μπορούσε να οδηγήσει σε σοβαρά σφάλματα που ήταν δύσκολο να εντοπιστούν. `Nette\Object` Όλες αυτές οι περιπτώσεις εντοπίστηκαν και απορρίφθηκε μια εξαίρεση `MemberAccessException`. +Από την PHP 8.4, η ίδια λειτουργικότητα μπορεί να επιτευχθεί χρησιμοποιώντας property hooks, που προσφέρουν πολύ πιο κομψή και συνοπτική σύνταξη: ```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException -``` -Από την PHP 7.0, η PHP δεν προκαλεί πλέον μη πιασμένα μοιραία σφάλματα, ενώ η πρόσβαση σε μη δηλωμένα μέλη αποτελεί σφάλμα από την PHP 8.2. - - -Εννοείτε? .[#toc-did-you-mean] ------------------------------- -Εάν εμφανιζόταν ένα σφάλμα `Nette\MemberAccessException`, ίσως λόγω τυπογραφικού λάθους κατά την προσπέλαση μιας μεταβλητής αντικειμένου ή την κλήση μιας μεθόδου, το `Nette\Object` προσπαθούσε να δώσει μια υπόδειξη στο μήνυμα σφάλματος για το πώς να διορθωθεί το σφάλμα, με τη μορφή της εικονικής προσθήκης "εννοούσες;". - -```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Αν και η σημερινή PHP δεν διαθέτει τη δυνατότητα "εννοούσατε;", αυτή η φράση μπορεί να προστεθεί στα σφάλματα από [την Tracy |tracy:]. Μπορεί ακόμη και να [διορθώσει αυτόματα τέτοια σφάλματα |tracy:open-files-in-ide#toc-demos]. - -Μέθοδοι επέκτασης .[#toc-extension-methods] -------------------------------------------- -Εμπνευσμένο από τις μεθόδους επέκτασης της C#. Έδιναν τη δυνατότητα προσθήκης νέων μεθόδων σε υπάρχουσες κλάσεις. Για παράδειγμα, θα μπορούσατε να προσθέσετε τη μέθοδο `addDateTime()` σε μια φόρμα για να προσθέσετε το δικό σας DateTimePicker. +Extension Methods +----------------- +Το `Nette\Object` έφερε στην PHP άλλη μια ενδιαφέρουσα έννοια εμπνευσμένη από σύγχρονες γλώσσες προγραμματισμού - τις extension methods. Αυτή η λειτουργία, δανεισμένη από τη C#, επέτρεψε στους προγραμματιστές να επεκτείνουν κομψά υπάρχουσες κλάσεις με νέες μεθόδους χωρίς να τις τροποποιούν ή να κληρονομούν από αυτές. Για παράδειγμα, θα μπορούσατε να προσθέσετε μια μέθοδο `addDateTime()` σε μια φόρμα που προσθέτει ένα προσαρμοσμένο DateTimePicker: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Οι μέθοδοι επέκτασης αποδείχθηκαν ανεφάρμοστες επειδή τα ονόματά τους δεν συμπληρώνονταν αυτόματα από τους συντάκτες, αλλά ανέφεραν ότι η μέθοδος δεν υπήρχε. Ως εκ τούτου, η υποστήριξή τους διακόπηκε. +Οι extension methods αποδείχθηκαν μη πρακτικές επειδή οι επεξεργαστές δεν πρότειναν τα ονόματά τους και αντίθετα ανέφεραν ότι η μέθοδος δεν υπάρχει. Επομένως, η υποστήριξή τους διακόπηκε. Σήμερα, είναι πιο συνηθισμένο να χρησιμοποιείται σύνθεση ή κληρονομικότητα για την επέκταση της λειτουργικότητας των κλάσεων. -Λήψη του ονόματος της κλάσης .[#toc-getting-the-class-name] ------------------------------------------------------------ +Λήψη Ονόματος Κλάσης +-------------------- +Το SmartObject πρόσφερε μια απλή μέθοδο για τη λήψη του ονόματος της κλάσης: ```php -$class = $obj->getClass(); // χρησιμοποιώντας το Nette\Object +$class = $obj->getClass(); // χρησιμοποιώντας Nette\Object $class = $obj::class; // από την PHP 8.0 ``` -Πρόσβαση στον Αναστοχασμό και τις Σημειώσεις .[#toc-access-to-reflection-and-annotations] ------------------------------------------------------------------------------------------ - -`Nette\Object` προσφέρεται πρόσβαση στον προβληματισμό και τον σχολιασμό με τη χρήση των μεθόδων `getReflection()` και `getAnnotation()`: +Πρόσβαση σε Reflection και Annotations +-------------------------------------- +Το `Nette\Object` παρείχε πρόσβαση σε reflection και annotations μέσω των μεθόδων `getReflection()` και `getAnnotation()`. Αυτή η προσέγγιση απλοποίησε σημαντικά την εργασία με μετα-πληροφορίες κλάσεων: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // returns 'John Doe' +$reflection->getAnnotation('author'); // επιστρέφει 'John Doe' ``` -Από την PHP 8.0, είναι δυνατή η πρόσβαση σε μετα-πληροφορίες με τη μορφή χαρακτηριστικών: +Από την PHP 8.0, είναι δυνατή η πρόσβαση σε μετα-πληροφορίες μέσω attributes, τα οποία προσφέρουν ακόμη περισσότερες δυνατότητες και καλύτερο έλεγχο τύπων: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Μέθοδοι Getters .[#toc-method-getters] --------------------------------------- - -`Nette\Object` προσέφερε έναν κομψό τρόπο για να χειρίζεστε τις μεθόδους σαν να ήταν μεταβλητές: +Method Getters +-------------- +Το `Nette\Object` πρόσφερε έναν κομψό τρόπο για να περνάμε μεθόδους σαν να ήταν μεταβλητές: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Από την PHP 8.1, μπορείτε να χρησιμοποιήσετε τη λεγόμενη "σύνταξη κλήσης πρώτης κατηγορίας":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Από την PHP 8.1, μπορείτε να χρησιμοποιήσετε το "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, το οποίο προχωράει αυτήν την έννοια ακόμη περισσότερο: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Γεγονότα .[#toc-events] ------------------------ - -`Nette\Object` προσφέρεται συντακτική ζάχαρη για την ενεργοποίηση του [συμβάντος |nette:glossary#events]: +Συμβάντα (Events) +----------------- +Το SmartObject προσφέρει απλοποιημένη σύνταξη για εργασία με [συμβάντα|nette:glossary#events]. Τα συμβάντα επιτρέπουν στα αντικείμενα να ενημερώνουν άλλα μέρη της εφαρμογής για αλλαγές στην κατάστασή τους: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Ο κώδικας `$this->onChange($this, $radius)` είναι ισοδύναμος με τα ακόλουθα: +Ο κώδικας `$this->onChange($this, $radius)` είναι ισοδύναμος με τον ακόλουθο βρόχο: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Για λόγους σαφήνειας συνιστούμε να αποφύγετε τη μαγική μέθοδο `$this->onChange()`. Ένα πρακτικό υποκατάστατο είναι η συνάρτηση [Nette\Utils\Arrays::invoke |arrays#invoke]: +Για λόγους σαφήνειας, συνιστούμε να αποφεύγετε τη μαγική μέθοδο `$this->onChange()`. Μια πρακτική εναλλακτική είναι η συνάρτηση [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/en/smartobject.texy b/utils/en/smartobject.texy index 27ec6370bb..4a30efbf8b 100644 --- a/utils/en/smartobject.texy +++ b/utils/en/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject used to fix objects behavior in many ways, but today's PHP already includes most of these improvements natively. However, it still adds support for *properties*. +SmartObject enhanced PHP object behavior for many years. Since PHP 8.4, all of its features have become native parts of PHP itself, thus completing its historic mission as a pioneer of modern object-oriented approach in PHP. Installation: @@ -11,19 +11,64 @@ Installation: composer require nette/utils ``` +SmartObject was introduced in 2007 as a revolutionary solution to the shortcomings of PHP's object model at the time. In an era when PHP faced numerous issues with object-oriented design, it brought significant improvements and simplified developers' workflows. It became a legendary component of the Nette framework. SmartObject offered functionality that PHP only acquired many years later – from access control for object properties to sophisticated syntactic sugar. With the release of PHP 8.4, it fulfilled its historical mission, as all its features became a native part of the language. It was ahead of PHP's development by an impressive 17 years. -Properties, Getters and Setters -=============================== +SmartObject went through an interesting technical evolution. Initially, it was implemented as the `Nette\Object` class, from which other classes inherited the needed functionality. A significant change came with PHP 5.4, which introduced trait support. This enabled transformation into the `Nette\SmartObject` trait, bringing greater flexibility - developers could use the functionality even in classes that already inherited from another class. While the original `Nette\Object` class ceased to exist with PHP 7.2 (which prohibited naming classes with the word 'Object'), the `Nette\SmartObject` trait lives on. -In modern object-oriented languages (e.g. C#, Python, Ruby, JavaScript), the term *property* refers to [special members of classes |https://en.wikipedia.org/wiki/Property_(programming)] that look like variables but are actually represented by methods. When the value of this "variable" is assigned or read, the corresponding method (called getter or setter) is called. This is a very handy thing to do, it gives us full control over access to variables. We can validate the input or generate results only when the property is read. +Let's explore the features that `Nette\Object` and later `Nette\SmartObject` offered. Each of these functions represented a significant step forward in PHP object-oriented programming at the time. -PHP properties are not supported, but trait `Nette\SmartObject` can imitate them. How to use it? -- Add an annotation to the class in the form `@property $xyz` +Consistent Error States +----------------------- +One of the most pressing issues of early PHP was inconsistent behavior when working with objects. `Nette\Object` brought order and predictability to this chaos. Let's look at how PHP originally behaved: + +```php +echo $obj->undeclared; // E_NOTICE, later E_WARNING +$obj->undeclared = 1; // passes silently without warning +$obj->unknownMethod(); // Fatal error (uncatchable by try/catch) +``` + +Fatal error would terminate the application without any possibility to react. Silent writing to non-existent members without warning could lead to serious errors that were difficult to detect. `Nette\Object` caught all these cases and threw a `MemberAccessException`, allowing programmers to react to and handle these errors: + +```php +echo $obj->undeclared; // throws Nette\MemberAccessException +$obj->undeclared = 1; // throws Nette\MemberAccessException +$obj->unknownMethod(); // throws Nette\MemberAccessException +``` + +Since PHP 7.0, the language no longer causes uncatchable fatal errors, and since PHP 8.2, access to undeclared members is considered an error. + + +"Did you mean?" Helper +---------------------- +`Nette\Object` came with a very convenient feature: intelligent suggestions for typos. When a developer made a mistake in a method or variable name, it not only reported the error but also offered help by suggesting the correct name. This iconic message, known as "did you mean?", saved programmers hours of hunting for typos: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// throws Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +While PHP itself doesn't have any form of "did you mean?", this feature is now provided by [Tracy|tracy:]. It can even [auto-fix|tracy:open-files-in-ide#demos] such errors. + + +Properties with Controlled Access +--------------------------------- +A significant innovation that SmartObject brought to PHP was properties with controlled access. This concept, common in languages like C# or Python, allowed developers to elegantly control access to object data and ensure their consistency. Properties are a powerful tool of object-oriented programming. They function like variables but are actually represented by methods (getters and setters). This allows input validation or value generation at the time of reading. + +To use properties, you must: +- Add the annotation `@property $xyz` to the class - Create a getter named `getXyz()` or `isXyz()`, a setter named `setXyz()` -- The getter and setter must be *public* or *protected* and are optional, so there can be a *read-only* or *write-only* property +- Ensure the getter and setter are *public* or *protected*. They are optional - thus can exist as *read-only* or *write-only* properties -We will use the property for the Circle class to ensure that only non-negative numbers are put into the `$radius` variable. Replace `public $radius` with property: +Let's look at a practical example using the Circle class, where we'll use properties to ensure that the radius is always non-negative. We'll replace `public $radius` with a property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // not public + private float $radius = 0.0; // not public! - // getter for property $radius + // getter for $radius property protected function getRadius(): float { return $this->radius; } - // setter for property $radius + // setter for $radius property protected function setRadius(float $radius): void { - // sanitizing value before saving it + // sanitize the value before saving $this->radius = max(0.0, $radius); } - // getter for property $visible + // getter for $visible property protected function isVisible(): bool { return $this->radius > 0; @@ -62,64 +107,25 @@ echo $circle->radius; // calls getRadius() echo $circle->visible; // calls isVisible() ``` -Properties are primarily "syntactic sugar"((syntactic sugar)), which is intended to make the programmer's life sweeter by simplifying the code. If you don't want them, you don't have to use them. - - -A Glimpse into History -====================== - -SmartObject used to refine the behavior of objects in numerous ways, but today's PHP already incorporates most of these enhancements natively. The following text is a nostalgic look back at history, reminding us of how things evolved. - -From its inception, PHP's object model suffered from a myriad of serious shortcomings and deficiencies. This led to the creation of the `Nette\Object` class (in 2007), which aimed to rectify these issues and enhance the comfort of using PHP. All that was needed was for other classes to inherit from it, and they would gain the benefits it offered. When PHP 5.4 introduced support for traits, the `Nette\Object` class was replaced by the `Nette\SmartObject` trait. This eliminated the need to inherit from a common ancestor. Moreover, the trait could be used in classes that already inherited from another class. The definitive end of `Nette\Object` came with the release of PHP 7.2, which prohibited classes from being named `Object`. - -As PHP development continued, its object model and language capabilities improved. Various functions of the `SmartObject` class became redundant. Since the release of PHP 8.2, there remains only one feature not directly supported in PHP: the ability to use so-called [properties|#Properties, getters, and setters]. - -What features did `Nette\Object` and, by extension, `Nette\SmartObject` offer? Here's an overview. (In the examples, the `Nette\Object` class is used, but most features also apply to the `Nette\SmartObject` trait). - - -Inconsistent Errors -------------------- -PHP had inconsistent behavior when accessing undeclared members. The state at the time of `Nette\Object` was as follows: - -```php -echo $obj->undeclared; // E_NOTICE, later E_WARNING -$obj->undeclared = 1; // passes silently without reporting -$obj->unknownMethod(); // Fatal error (not catchable by try/catch) -``` - -A fatal error would terminate the application without any chance of response. Silently writing to non-existent members without warning could lead to serious errors that were hard to detect. `Nette\Object` caught all these cases and threw a `MemberAccessException` exception. - -```php -echo $obj->undeclared; // throws Nette\MemberAccessException -$obj->undeclared = 1; // throws Nette\MemberAccessException -$obj->unknownMethod(); // throws Nette\MemberAccessException -``` -From PHP version 7.0 onwards, uncatchable fatal errors no longer occur, and accessing undeclared members becomes an error from PHP 8.2. - - -Did you mean? -------------- -If an `Nette\MemberAccessException` error was thrown, perhaps due to a typo when accessing an object variable or calling a method, `Nette\Object` attempted to give a hint in the error message on how to fix the error, in the form of the iconic "did you mean?" addendum. +Since PHP 8.4, the same functionality can be achieved using property hooks, which offer a much more elegant and concise syntax: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throws Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -While today's PHP doesn't have a "did you mean?" feature, this phrase can be added to errors by [Tracy|tracy:]. It can even [auto-correct such errors|tracy:open-files-in-ide#toc-demos]. - Extension Methods ----------------- -Inspired by the extension methods from the C# language, they provided the ability to add new methods to existing classes. For instance, you could add a `addDateTime()` method to a form, which would introduce a custom DateTimePicker. +`Nette\Object` brought another interesting concept to PHP inspired by modern programming languages - extension methods. This feature, borrowed from C#, allowed developers to elegantly extend existing classes with new methods without modifying them or inheriting from them. For instance, you could add an `addDateTime()` method to a form that adds a custom DateTimePicker: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Extension methods turned out to be impractical because their names were not suggested by editors; on the contrary, they reported that the method did not exist. Therefore, their support was discontinued. +Extension methods proved impractical because editors wouldn't suggest their names and would instead report that the method doesn't exist. Therefore, their support was discontinued. Today, it's more common to use composition or inheritance to extend class functionality. -Determining the Class Name --------------------------- +Getting Class Name +------------------ +SmartObject offered a simple method for getting the class name: ```php $class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // from PHP 8.0 +$class = $obj::class; // since PHP 8.0 ``` -Access to Reflection and Annotations ------------------------------------- - -`Nette\Object` offered access to reflection and annotation using the methods `getReflection()` and `getAnnotation()`: +Reflection and Annotation Access +-------------------------------- +`Nette\Object` provided access to reflection and annotations through methods `getReflection()` and `getAnnotation()`. This approach significantly simplified working with class meta-information: ```php /** @@ -161,7 +167,7 @@ $reflection = $obj->getReflection(); $reflection->getAnnotation('author'); // returns 'John Doe' ``` -As of PHP 8.0, it is possible to access meta-information in the form of attributes: +Since PHP 8.0, it's possible to access meta-information through attributes, which offer even more possibilities and better type checking: ```php #[Author('John Doe')] @@ -177,8 +183,7 @@ $reflection->getAttributes(Author::class)[0]; Method Getters -------------- - -`Nette\Object` offered an elegant way to deal with methods as if they were variables: +`Nette\Object` offered an elegant way to pass methods as if they were variables: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -As of PHP 8.1, you can use the so-called "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Since PHP 8.1, you can use the "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, which takes this concept even further: ```php $obj = new Foo; @@ -205,8 +210,7 @@ echo $method(2, 3); // 5 Events ------ - -`Nette\Object` offered syntactic sugar to trigger the [event |nette:glossary#events]: +SmartObject offers simplified syntax for working with [events|nette:glossary#events]. Events allow objects to inform other parts of the application about changes in their state: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -The code `$this->onChange($this, $radius)` is equivalent to the following: +The code `$this->onChange($this, $radius)` is equivalent to the following loop: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -For clarity, we recommend avoiding the magic method `$this->onChange()`. A practical alternative is the [Nette\Utils\Arrays::invoke |arrays#invoke] function: +For clarity, we recommend avoiding the magic `$this->onChange()` method. A practical replacement is the [Nette\Utils\Arrays::invoke|arrays#invoke] function: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/es/smartobject.texy b/utils/es/smartobject.texy index b3b4cbc5b3..5da55a227a 100644 --- a/utils/es/smartobject.texy +++ b/utils/es/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject solía arreglar el comportamiento de los objetos de muchas maneras, pero el PHP actual ya incluye la mayoría de estas mejoras de forma nativa. Sin embargo, aún añade soporte para *property*. +SmartObject mejoró durante años el comportamiento de los objetos en PHP. Desde la versión PHP 8.4, todas sus funciones forman parte nativa del propio PHP, completando así su misión histórica como pionero del enfoque orientado a objetos moderno en PHP. Instalación: @@ -11,19 +11,64 @@ Instalación: composer require nette/utils ``` +SmartObject surgió en 2007 como una solución revolucionaria a las deficiencias del modelo de objetos de PHP de la época. En un momento en que PHP sufría numerosos problemas con el diseño orientado a objetos, aportó mejoras significativas y simplificó el trabajo de los desarrolladores. Se convirtió en una parte legendaria del framework Nette. Ofrecía funcionalidades que PHP no obtendría hasta muchos años después: desde la validación del acceso a propiedades hasta el manejo sofisticado de errores. Con la llegada de PHP 8.4, completó su misión histórica, ya que todas sus funciones se convirtieron en partes nativas del lenguaje. Se adelantó al desarrollo de PHP en notables 17 años. -Propiedades, getters y setters .[#toc-properties-getters-and-setters] -===================================================================== +SmartObject pasó por una interesante evolución técnica. Inicialmente, se implementó como la clase `Nette\Object`, de la cual otras clases heredaban la funcionalidad necesaria. Un cambio significativo llegó con PHP 5.4, que introdujo el soporte para traits. Esto permitió la transformación al trait `Nette\SmartObject`, aportando mayor flexibilidad - los desarrolladores podían utilizar la funcionalidad incluso en clases que ya heredaban de otra clase. Mientras que la clase original `Nette\Object` dejó de existir con PHP 7.2 (que prohibió nombrar clases con la palabra 'Object'), el trait `Nette\SmartObject` sigue vigente. -En los lenguajes modernos orientados a objetos (por ejemplo, C#, Python, Ruby, JavaScript), el término *propiedad* se refiere a [miembros especiales de las clases |https://en.wikipedia.org/wiki/Property_(programming)] que parecen variables pero que en realidad están representados por métodos. Cuando se asigna o se lee el valor de esta "variable", se llama al método correspondiente (llamado getter o setter). Esto es muy práctico, nos da un control total sobre el acceso a las variables. Podemos validar la entrada o generar resultados sólo cuando la propiedad es leída. +Veamos las características que `Nette\Object` y posteriormente `Nette\SmartObject` ofrecían. Cada una de estas funciones representó un paso significativo en la programación orientada a objetos en PHP de su época. -Las propiedades PHP no están soportadas, pero trait `Nette\SmartObject` puede imitarlas. ¿Cómo usarlo? -- Añade una anotación a la clase de la forma `@property $xyz` -- Crea un getter llamado `getXyz()` o `isXyz()`, un setter llamado `setXyz()` -- El getter y el setter deben ser *public* o *protected* y son opcionales, por lo que puede haber una propiedad *read-only* o *write-only +Estados de Error Consistentes +----------------------------- +Uno de los problemas más apremiantes del PHP temprano era el comportamiento inconsistente al trabajar con objetos. `Nette\Object` trajo orden y previsibilidad a este caos. Veamos cómo se comportaba PHP originalmente: -Utilizaremos la propiedad de la clase Circle para asegurarnos de que sólo se introducen números no negativos en la variable `$radius`. Sustituye `public $radius` por propiedad: +```php +echo $obj->undeclared; // E_NOTICE, después E_WARNING +$obj->undeclared = 1; // pasa silenciosamente sin advertencia +$obj->unknownMethod(); // Error fatal (no capturable con try/catch) +``` + +El error fatal terminaba la aplicación sin posibilidad de reaccionar. La escritura silenciosa en miembros inexistentes sin advertencia podía llevar a errores graves difíciles de detectar. `Nette\Object` capturaba todos estos casos y lanzaba una `MemberAccessException`, permitiendo a los programadores reaccionar y manejar estos errores: + +```php +echo $obj->undeclared; // lanza Nette\MemberAccessException +$obj->undeclared = 1; // lanza Nette\MemberAccessException +$obj->unknownMethod(); // lanza Nette\MemberAccessException +``` + +Desde PHP 7.0, el lenguaje ya no causa errores fatales no capturables, y desde PHP 8.2, el acceso a miembros no declarados se considera un error. + + +Ayudante "Did you mean?" +------------------------ +`Nette\Object` vino con una característica muy conveniente: sugerencias inteligentes para errores de escritura. Cuando un desarrollador cometía un error en el nombre de un método o variable, no solo informaba del error, sino que también ofrecía ayuda sugiriendo el nombre correcto. Este mensaje icónico, conocido como "did you mean?", ahorró a los programadores horas de búsqueda de errores tipográficos: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// lanza Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Aunque PHP en sí no tiene ninguna forma de "did you mean?", esta característica ahora la proporciona [Tracy|tracy:]. Incluso puede [auto-corregir|tracy:open-files-in-ide#demos] estos errores. + + +Propiedades con Acceso Controlado +--------------------------------- +Una innovación significativa que SmartObject trajo a PHP fueron las propiedades con acceso controlado. Este concepto, común en lenguajes como C# o Python, permitió a los desarrolladores controlar elegantemente el acceso a los datos del objeto y asegurar su consistencia. Las propiedades son una herramienta poderosa de la programación orientada a objetos. Funcionan como variables pero en realidad están representadas por métodos (getters y setters). Esto permite validar las entradas o generar valores en el momento de la lectura. + +Para usar propiedades, debes: +- Agregar la anotación `@property $xyz` a la clase +- Crear un getter llamado `getXyz()` o `isXyz()`, un setter llamado `setXyz()` +- Asegurar que el getter y setter sean *public* o *protected*. Son opcionales - por lo tanto pueden existir como propiedades de *solo lectura* o *solo escritura* + +Veamos un ejemplo práctico usando la clase Circle, donde usaremos propiedades para asegurar que el radio sea siempre no negativo. Reemplazaremos `public $radius` con una propiedad: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // not public + private float $radius = 0.0; // ¡no es public! - // getter for property $radius + // getter para la propiedad $radius protected function getRadius(): float { return $this->radius; } - // setter for property $radius + // setter para la propiedad $radius protected function setRadius(float $radius): void { - // sanitizing value before saving it + // sanitizamos el valor antes de guardarlo $this->radius = max(0.0, $radius); } - // getter for property $visible + // getter para la propiedad $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // actually calls setRadius(10) -echo $circle->radius; // calls getRadius() -echo $circle->visible; // calls isVisible() -``` - -Las propiedades son principalmente "azúcar sintáctico"((syntactic sugar)), que pretende hacer la vida del programador más dulce simplificando el código. Si no las quieres, no tienes por qué usarlas. - - -Un vistazo a la Historia .[#toc-a-glimpse-into-history] -======================================================= - -SmartObject solía refinar el comportamiento de los objetos de numerosas maneras, pero el PHP actual ya incorpora la mayoría de estas mejoras de forma nativa. El siguiente texto es una mirada nostálgica a la historia, recordándonos como evolucionaron las cosas. - -Desde sus inicios, el modelo de objetos de PHP sufrió de una miríada de serios defectos y deficiencias. Esto llevó a la creación de la clase `Nette\Object` (en 2007), que pretendía rectificar estos problemas y mejorar la comodidad de uso de PHP. Todo lo que se necesitaba era que otras clases heredaran de ella, y obtendrían los beneficios que ofrecía. Cuando PHP 5.4 introdujo el soporte para traits, la clase `Nette\Object` fue reemplazada por el trait `Nette\SmartObject`. Esto eliminó la necesidad de heredar de un ancestro común. Además, el trait podía ser usado en clases que ya heredaban de otra clase. El fin definitivo de `Nette\Object` llegó con el lanzamiento de PHP 7.2, que prohibió que las clases se llamaran `Object`. - -A medida que el desarrollo de PHP continuaba, su modelo de objetos y las capacidades del lenguaje mejoraron. Varias funciones de la clase `SmartObject` se volvieron redundantes. Desde el lanzamiento de PHP 8.2, sólo queda una característica no soportada directamente en PHP: la capacidad de usar las llamadas [propiedades |#Properties, getters, and setters]. - -¿Qué características ofrecían `Nette\Object` y, por extensión, `Nette\SmartObject`? He aquí un resumen. (En los ejemplos, se usa la clase `Nette\Object`, pero la mayoría de las características también se aplican al rasgo `Nette\SmartObject` ). - - -Errores incoherentes .[#toc-inconsistent-errors] ------------------------------------------------- -PHP tenía un comportamiento inconsistente al acceder a miembros no declarados. El estado en el momento de `Nette\Object` era el siguiente: - -```php -echo $obj->undeclared; // E_NOTICE, later E_WARNING -$obj->undeclared = 1; // passes silently without reporting -$obj->unknownMethod(); // Fatal error (not catchable by try/catch) -``` - -Un error fatal terminaba la aplicación sin posibilidad de reaccionar. La escritura silenciosa en miembros inexistentes sin previo aviso podía dar lugar a errores graves difíciles de detectar. `Nette\Object` Todos estos casos eran detectados y se lanzaba una excepción `MemberAccessException`. - -```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException +$circle->radius = 10; // en realidad llama a setRadius(10) +echo $circle->radius; // llama a getRadius() +echo $circle->visible; // llama a isVisible() ``` -Desde PHP 7.0, PHP ya no causa errores fatales no capturables, y acceder a miembros no declarados ha sido un error desde PHP 8.2. - -¿Te refieres a? .[#toc-did-you-mean] ------------------------------------- -Si se lanzaba un error `Nette\MemberAccessException`, quizás debido a un error tipográfico al acceder a una variable de objeto o al llamar a un método, `Nette\Object` intentaba dar una pista en el mensaje de error sobre cómo solucionar el error, en la forma del icónico apéndice "¿querías decir?". +Desde PHP 8.4, se puede lograr la misma funcionalidad usando property hooks, que ofrecen una sintaxis mucho más elegante y concisa: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Aunque el PHP actual no tiene la función "¿Querías decir?", esta frase puede ser añadida a los errores por [Tracy |tracy:]. Incluso puede [autocorregir dichos errores |tracy:open-files-in-ide#toc-demos]. - -Métodos de extensión .[#toc-extension-methods] ----------------------------------------------- -Inspirado en los métodos de extensión de C#. Ofrecen la posibilidad de añadir nuevos métodos a clases existentes. Por ejemplo, podrías añadir el método `addDateTime()` a un formulario para añadir tu propio DateTimePicker. +Métodos de Extensión +-------------------- +`Nette\Object` trajo otro concepto interesante a PHP inspirado en lenguajes de programación modernos - los métodos de extensión. Esta característica, tomada de C#, permitía a los desarrolladores extender elegantemente las clases existentes con nuevos métodos sin modificarlas ni heredar de ellas. Por ejemplo, podías agregar un método `addDateTime()` a un formulario que añade un DateTimePicker personalizado: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Los métodos de extensión resultaron ser poco prácticos porque sus nombres no eran autocompletados por los editores, sino que informaban de que el método no existía. Por lo tanto, su soporte fue descontinuado. +Los métodos de extensión resultaron poco prácticos porque los editores no sugerían sus nombres y, en cambio, informaban que el método no existía. Por lo tanto, se descontinuó su soporte. Hoy en día, es más común usar composición o herencia para extender la funcionalidad de las clases. -Obtención del nombre de la clase .[#toc-getting-the-class-name] ---------------------------------------------------------------- +Obtener el Nombre de la Clase +----------------------------- +SmartObject ofrecía un método simple para obtener el nombre de la clase: ```php -$class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // since PHP 8.0 +$class = $obj->getClass(); // usando Nette\Object +$class = $obj::class; // desde PHP 8.0 ``` -Acceso a la reflexión y a las anotaciones .[#toc-access-to-reflection-and-annotations] --------------------------------------------------------------------------------------- - -`Nette\Object` ofrece acceso a la reflexión y a las anotaciones mediante los métodos `getReflection()` y `getAnnotation()`: +Acceso a Reflexión y Anotaciones +-------------------------------- +`Nette\Object` proporcionaba acceso a la reflexión y anotaciones a través de los métodos `getReflection()` y `getAnnotation()`. Este enfoque simplificó significativamente el trabajo con la metainformación de las clases: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // returns 'John Doe' +$reflection->getAnnotation('author'); // devuelve 'John Doe' ``` -A partir de PHP 8.0, es posible acceder a meta-información en forma de atributos: +Desde PHP 8.0, es posible acceder a la metainformación a través de atributos, que ofrecen aún más posibilidades y mejor verificación de tipos: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Método getters .[#toc-method-getters] -------------------------------------- - -`Nette\Object` ofrecían una forma elegante de tratar los métodos como si fueran variables: +Getters de Métodos +------------------ +`Nette\Object` ofrecía una forma elegante de pasar métodos como si fueran variables: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -A partir de PHP 8.1, se puede utilizar la llamada "sintaxis callable de primera clase":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Desde PHP 8.1, puedes usar la "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, que lleva este concepto aún más lejos: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Eventos .[#toc-events] ----------------------- - -`Nette\Object` ofrece azúcar sintáctico para activar el [evento |nette:glossary#events]: +Eventos +------- +SmartObject ofrece una sintaxis simplificada para trabajar con [eventos|nette:glossary#events]. Los eventos permiten a los objetos informar a otras partes de la aplicación sobre cambios en su estado: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -El código `$this->onChange($this, $radius)` es equivalente al siguiente: +El código `$this->onChange($this, $radius)` es equivalente al siguiente bucle: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Por claridad, recomendamos evitar el método mágico `$this->onChange()`. Un buen sustituto es [Nette\Utils\Arrays::invoke |arrays#invoke]: +Por claridad, recomendamos evitar el método mágico `$this->onChange()`. Un reemplazo práctico es la función [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/fr/smartobject.texy b/utils/fr/smartobject.texy index 9458d913bb..35972fd95f 100644 --- a/utils/fr/smartobject.texy +++ b/utils/fr/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject corrigeait le comportement des objets de plusieurs façons, mais le PHP d'aujourd'hui inclut déjà la plupart de ces améliorations de façon native. Cependant, il ajoute encore le support des *propriétés*. +SmartObject a amélioré pendant des années le comportement des objets en PHP. Depuis PHP 8.4, toutes ses fonctionnalités font partie intégrante de PHP, accomplissant ainsi sa mission historique de pionnier de l'approche orientée objet moderne en PHP. Installation : @@ -11,19 +11,64 @@ Installation : composer require nette/utils ``` +SmartObject est né en 2007 comme une solution révolutionnaire aux limitations du modèle objet de PHP de l'époque. À une période où PHP souffrait de nombreux problèmes de conception orientée objet, il a apporté des améliorations significatives et simplifié le travail des développeurs. Il est devenu une partie légendaire du framework Nette. Il proposait des fonctionnalités que PHP n'obtiendrait que de nombreuses années plus tard - de la validation d'accès aux propriétés jusqu'à la gestion sophistiquée des erreurs. Avec l'arrivée de PHP 8.4, il a achevé sa mission historique, car toutes ses fonctionnalités sont devenues des parties natives du langage. Il était en avance sur le développement de PHP de 17 années remarquables. -Propriétés, getters et setters .[#toc-properties-getters-and-setters] -===================================================================== +SmartObject a connu une évolution technique intéressante. Initialement, il était implémenté comme une classe `Nette\Object`, dont les autres classes héritaient les fonctionnalités nécessaires. Un changement majeur est survenu avec PHP 5.4, qui a introduit le support des traits. Cela a permis sa transformation en trait `Nette\SmartObject`, apportant plus de flexibilité - les développeurs pouvaient utiliser ses fonctionnalités même dans les classes qui héritaient déjà d'une autre classe. Alors que la classe originale `Nette\Object` a cessé d'exister avec PHP 7.2 (qui interdisait de nommer les classes avec le mot 'Object'), le trait `Nette\SmartObject` continue d'exister. -Dans les langages modernes orientés objet (par exemple C#, Python, Ruby, JavaScript), le terme *propriété* fait référence aux [membres spéciaux des classes |https://en.wikipedia.org/wiki/Property_(programming)] qui ressemblent à des variables mais sont en fait représentés par des méthodes. Lorsque la valeur de cette "variable" est assignée ou lue, la méthode correspondante (appelée getter ou setter) est appelée. C'est très pratique, cela nous donne un contrôle total sur l'accès aux variables. Nous pouvons valider l'entrée ou générer des résultats uniquement lorsque la propriété est lue. +Examinons les fonctionnalités que `Nette\Object` et plus tard `Nette\SmartObject` offraient. Chacune de ces fonctions représentait à l'époque une avancée significative dans la programmation orientée objet en PHP. -Les propriétés PHP ne sont pas prises en charge, mais le trait `Nette\SmartObject` peut les imiter. Comment l'utiliser ? -- Ajoutez une annotation à la classe sous la forme `@property $xyz` -- Créez un getter nommé `getXyz()` ou `isXyz()`, un setter nommé `setXyz()` -- Le getter et le setter doivent être *public* ou *protégé* et sont optionnels, donc il peut y avoir une propriété *lire seulement* ou *écrire seulement*. +États d'erreur cohérents +------------------------ +L'un des problèmes les plus pressants du PHP initial était le comportement incohérent lors du travail avec les objets. `Nette\Object` a apporté ordre et prévisibilité dans ce chaos. Voici comment PHP se comportait à l'origine : -Nous utiliserons la propriété de la classe Circle pour nous assurer que seuls des nombres non négatifs sont placés dans la variable `$radius`. Remplacez `public $radius` par la propriété : +```php +echo $obj->undeclared; // E_NOTICE, plus tard E_WARNING +$obj->undeclared = 1; // passe silencieusement sans avertissement +$obj->unknownMethod(); // Fatal error (non capturable par try/catch) +``` + +Une erreur fatale arrêtait l'application sans possibilité de réaction. L'écriture silencieuse dans des membres non existants sans avertissement pouvait conduire à des erreurs graves difficiles à détecter. `Nette\Object` capturait tous ces cas et lançait une exception `MemberAccessException`, permettant aux programmeurs de réagir et de gérer ces erreurs : + +```php +echo $obj->undeclared; // lance Nette\MemberAccessException +$obj->undeclared = 1; // lance Nette\MemberAccessException +$obj->unknownMethod(); // lance Nette\MemberAccessException +``` + +Depuis PHP 7.0, le langage ne provoque plus d'erreurs fatales non capturables, et depuis PHP 8.2, l'accès aux membres non déclarés est considéré comme une erreur. + + +Assistant « Did you mean ? » +---------------------------- +`Nette\Object` est venu avec une fonctionnalité très pratique : des suggestions intelligentes pour les fautes de frappe. Quand un développeur faisait une erreur dans le nom d'une méthode ou d'une variable, il ne signalait pas seulement l'erreur mais proposait aussi une aide en suggérant le nom correct. Ce message iconique, connu sous le nom de « did you mean ? », a fait gagner aux programmeurs des heures de recherche de fautes de frappe : + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// lance Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Bien que PHP lui-même n'ait aucune forme de « did you mean ? », cette fonctionnalité est maintenant fournie par [Tracy|tracy:]. Il peut même [auto-corriger|tracy:open-files-in-ide#demos] ces erreurs. + + +Propriétés avec accès contrôlé +------------------------------ +Une innovation significative que SmartObject a apportée à PHP était les propriétés avec accès contrôlé. Ce concept, courant dans des langages comme C# ou Python, permettait aux développeurs de contrôler élégamment l'accès aux données d'objet et d'assurer leur cohérence. Les propriétés sont un outil puissant de la programmation orientée objet. Elles fonctionnent comme des variables mais sont en réalité représentées par des méthodes (getters et setters). Cela permet la validation des entrées ou la génération de valeurs au moment de la lecture. + +Pour utiliser les propriétés, vous devez : +- Ajouter l'annotation `@property $xyz` à la classe +- Créer un getter nommé `getXyz()` ou `isXyz()`, un setter nommé `setXyz()` +- Vous assurer que le getter et le setter sont *public* ou *protected*. Ils sont optionnels - peuvent donc exister comme propriétés *read-only* ou *write-only* + +Voyons un exemple pratique avec la classe Circle, où nous utiliserons les propriétés pour garantir que le rayon est toujours non négatif. Nous remplacerons `public $radius` par une propriété : ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // not public + private float $radius = 0.0; // pas public ! - // getter for property $radius + // getter pour la propriété $radius protected function getRadius(): float { return $this->radius; } - // setter for property $radius + // setter pour la propriété $radius protected function setRadius(float $radius): void { - // sanitizing value before saving it + // nous assainissons la valeur avant de la sauvegarder $this->radius = max(0.0, $radius); } - // getter for property $visible + // getter pour la propriété $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // actually calls setRadius(10) -echo $circle->radius; // calls getRadius() -echo $circle->visible; // calls isVisible() -``` - -Les propriétés sont principalement du "sucre syntaxique" ((sucre syntaxique)), destiné à rendre la vie du programmeur plus douce en simplifiant le code. Si vous n'en voulez pas, vous n'êtes pas obligé de les utiliser. - - -Un aperçu de l'histoire .[#toc-a-glimpse-into-history] -====================================================== - -SmartObject permettait d'affiner le comportement des objets de nombreuses manières, mais le PHP d'aujourd'hui incorpore déjà la plupart de ces améliorations de manière native. Le texte suivant est un regard nostalgique sur l'histoire, nous rappelant comment les choses ont évolué. - -Dès le début, le modèle objet de PHP a souffert d'une myriade de lacunes et de déficiences. Cela a conduit à la création de la classe `Nette\Object` (en 2007), qui visait à corriger ces problèmes et à améliorer le confort d'utilisation de PHP. Il suffisait que d'autres classes héritent de cette classe pour bénéficier des avantages qu'elle offrait. Lorsque PHP 5.4 a introduit le support des traits, la classe `Nette\Object` a été remplacée par le trait `Nette\SmartObject`. Cela a éliminé le besoin d'hériter d'un ancêtre commun. De plus, le trait pouvait être utilisé dans des classes qui héritaient déjà d'une autre classe. La fin définitive de `Nette\Object` est intervenue avec la publication de PHP 7.2, qui a interdit aux classes d'être nommées `Object`. - -Au fur et à mesure du développement de PHP, le modèle d'objet et les capacités du langage se sont améliorés. Plusieurs fonctions de la classe `SmartObject` sont devenues redondantes. Depuis la sortie de PHP 8.2, il ne reste plus qu'une seule fonctionnalité non directement supportée par PHP : la possibilité d'utiliser ce que l'on appelle des [propriétés |#Properties, getters, and setters]. - -Quelles sont les fonctionnalités offertes par `Nette\Object` et, par extension, par `Nette\SmartObject`? En voici un aperçu. (Dans les exemples, la classe `Nette\Object` est utilisée, mais la plupart des fonctionnalités s'appliquent également au trait `Nette\SmartObject` ). - - -Erreurs de cohérence .[#toc-inconsistent-errors] ------------------------------------------------- -PHP avait un comportement incohérent lors de l'accès aux membres non déclarés. L'état au moment de `Nette\Object` était le suivant : - -```php -echo $obj->undeclared; // E_NOTICE, later E_WARNING -$obj->undeclared = 1; // passes silently without reporting -$obj->unknownMethod(); // Fatal error (not catchable by try/catch) +$circle->radius = 10; // appelle en réalité setRadius(10) +echo $circle->radius; // appelle getRadius() +echo $circle->visible; // appelle isVisible() ``` -Une erreur fatale terminait l'application sans possibilité de réagir. L'écriture silencieuse dans des membres inexistants sans avertissement pouvait entraîner des erreurs graves difficiles à détecter. `Nette\Object` Tous ces cas ont été détectés et une exception `MemberAccessException` a été levée. - -```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException -``` -Depuis PHP 7.0, PHP ne provoque plus d'erreurs fatales non capturables, et l'accès à des membres non déclarés est un bug depuis PHP 8.2. - - -Voulez-vous dire ? .[#toc-did-you-mean] ---------------------------------------- -Si une erreur `Nette\MemberAccessException` était déclenchée, peut-être à cause d'une faute de frappe lors de l'accès à une variable d'objet ou de l'appel d'une méthode, `Nette\Object` tentait de donner un indice dans le message d'erreur sur la façon de corriger l'erreur, sous la forme de l'addendum emblématique "Did you mean ?". +Depuis PHP 8.4, la même fonctionnalité peut être réalisée en utilisant les property hooks, qui offrent une syntaxe beaucoup plus élégante et concise : ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Bien que le PHP d'aujourd'hui n'ait pas de fonction "did you mean ?", cette phrase peut être ajoutée aux erreurs par [Tracy |tracy:]. Il peut même [corriger automatiquement ces erreurs |tracy:open-files-in-ide#toc-demos]. - -Méthodes d'extension .[#toc-extension-methods] ----------------------------------------------- -Inspiré par les méthodes d'extension de C#. Elles donnaient la possibilité d'ajouter de nouvelles méthodes à des classes existantes. Par exemple, vous pouvez ajouter la méthode `addDateTime()` à un formulaire pour ajouter votre propre DateTimePicker. +Extension Methods +----------------- +`Nette\Object` a apporté un autre concept intéressant à PHP inspiré des langages de programmation modernes - les extension methods. Cette fonctionnalité, empruntée à C#, permettait aux développeurs d'étendre élégamment les classes existantes avec de nouvelles méthodes sans les modifier ni en hériter. Par exemple, vous pouviez ajouter une méthode `addDateTime()` à un formulaire qui ajoute un DateTimePicker personnalisé : ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Les méthodes d'extension se sont avérées peu pratiques car leurs noms n'étaient pas autocomplétés par les éditeurs, qui signalaient au contraire que la méthode n'existait pas. Par conséquent, leur prise en charge a été abandonnée. +Les extension methods se sont révélées peu pratiques car les éditeurs ne suggéraient pas leurs noms et signalaient au contraire que la méthode n'existait pas. Par conséquent, leur support a été abandonné. Aujourd'hui, il est plus courant d'utiliser la composition ou l'héritage pour étendre les fonctionnalités des classes. -Obtention du nom de la classe .[#toc-getting-the-class-name] ------------------------------------------------------------- +Obtention du nom de la classe +----------------------------- +SmartObject proposait une méthode simple pour obtenir le nom de la classe : ```php -$class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // since PHP 8.0 +$class = $obj->getClass(); // avec Nette\Object +$class = $obj::class; // depuis PHP 8.0 ``` -Accès à la réflexion et aux annotations .[#toc-access-to-reflection-and-annotations] ------------------------------------------------------------------------------------- - -`Nette\Object` a proposé un accès à la réflexion et aux annotations en utilisant les méthodes `getReflection()` et `getAnnotation()`: +Accès à la réflexion et aux annotations +--------------------------------------- +`Nette\Object` fournissait un accès à la réflexion et aux annotations via les méthodes `getReflection()` et `getAnnotation()`. Cette approche simplifiait significativement le travail avec les méta-informations des classes : ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // returns 'John Doe' +$reflection->getAnnotation('author'); // renvoie 'John Doe' ``` -Depuis PHP 8.0, il est possible d'accéder aux méta-informations sous forme d'attributs : +Depuis PHP 8.0, il est possible d'accéder aux méta-informations via les attributs, qui offrent encore plus de possibilités et un meilleur contrôle de type : ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Les récupérateurs de méthodes .[#toc-method-getters] ----------------------------------------------------- - -`Nette\Object` offraient un moyen élégant de traiter les méthodes comme s'il s'agissait de variables : +Getters de méthodes +------------------- +`Nette\Object` offrait une façon élégante de passer des méthodes comme s'il s'agissait de variables : ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Depuis PHP 8.1, vous pouvez utiliser la syntaxe:https://www.php.net/manual/en/functions.first_class_callable_syntax dite "first-class callable":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Depuis PHP 8.1, vous pouvez utiliser la "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, qui pousse ce concept encore plus loin : ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Événements .[#toc-events] -------------------------- - -`Nette\Object` a offert un sucre syntaxique pour déclencher l'[événement |nette:glossary#events]: +Événements +---------- +SmartObject offre une syntaxe simplifiée pour travailler avec les [événements|nette:glossary#events]. Les événements permettent aux objets d'informer les autres parties de l'application des changements de leur état : ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Le code `$this->onChange($this, $radius)` est équivalent à ce qui suit : +Le code `$this->onChange($this, $radius)` est équivalent à la boucle suivante : ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Pour des raisons de clarté, nous vous recommandons d'éviter la méthode magique `$this->onChange()`. Un bon substitut est [Nette\Utils\Arrays::invoke |arrays#invoke]: +Pour plus de clarté, nous recommandons d'éviter la méthode magique `$this->onChange()`. Un remplacement pratique est la fonction [Nette\Utils\Arrays::invoke|arrays#invoke] : ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/hu/smartobject.texy b/utils/hu/smartobject.texy index eb81664c98..7b1d52dbbe 100644 --- a/utils/hu/smartobject.texy +++ b/utils/hu/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -A SmartObject korábban sokféleképpen javította az objektumok viselkedését, de a mai PHP már natívan tartalmazza ezen fejlesztések nagy részét. Azonban még mindig hozzáadja a *tulajdonságok* támogatását. +A SmartObject éveken át fejlesztette a PHP objektumok viselkedését. A PHP 8.4 verziótól kezdve minden funkciója beépült magába a PHP nyelvbe, így teljesítve történelmi küldetését a modern objektumorientált megközelítés úttörőjeként. Telepítés: @@ -11,19 +11,64 @@ Telepítés: composer require nette/utils ``` +A SmartObject 2007-ben jött létre, mint forradalmi megoldás az akkori PHP objektummodell hiányosságaira. Amikor a PHP számos objektumorientált tervezési problémával küzdött, jelentős fejlesztéseket és egyszerűsítéseket hozott a fejlesztők munkájába. A Nette framework legendás részévé vált. Olyan funkcionalitást kínált, amelyet a PHP csak sok évvel később ért el - az objektumtulajdonságok validálásától a kifinomult hibakezelésig. A PHP 8.4 megjelenésével befejezte történelmi küldetését, mivel minden funkciója a nyelv natív részévé vált. Figyelemreméltó módon 17 évvel előzte meg a PHP fejlődését. -Tulajdonságok, Getterek és Setterek .[#toc-properties-getters-and-setters] -========================================================================== +A SmartObject érdekes technikai fejlődésen ment keresztül. Eredetileg `Nette\Object` osztályként implementálták, amelyből más osztályok örökölték a szükséges funkcionalitást. Jelentős változás jött a PHP 5.4-gyel, amely bevezette a trait-ek támogatását. Ez lehetővé tette az átalakulást `Nette\SmartObject` trait-té, ami nagyobb rugalmasságot hozott - a fejlesztők olyan osztályokban is használhatták a funkcionalitást, amelyek már örököltek egy másik osztályból. Míg az eredeti `Nette\Object` osztály megszűnt a PHP 7.2 megjelenésével (amely megtiltotta az osztályok 'Object' szóval való elnevezését), a `Nette\SmartObject` trait tovább él. -A modern objektumorientált nyelvekben (pl. C#, Python, Ruby, JavaScript) a *tulajdonság* kifejezés az [osztályok speciális tagjaira |https://en.wikipedia.org/wiki/Property_(programming)] 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. +Nézzük át azokat a tulajdonságokat, amelyeket egykor a `Nette\Object`, majd később a `Nette\SmartObject` kínált. Mindegyik funkció jelentős előrelépést jelentett a PHP objektumorientált programozásában a maga idejében. -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 $xyz` -- Hozzon létre egy `getXyz()` vagy `isXyz()` nevű gettert, egy settert, amelynek a neve `setXyz()` -- 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. +Konzisztens hibaállapotok +------------------------- +A korai PHP egyik legégetőbb problémája az objektumokkal való munka következetlen viselkedése volt. A `Nette\Object` rendet és kiszámíthatóságot hozott ebbe a káoszba. Nézzük meg, hogyan működött eredetileg a PHP: -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: +```php +echo $obj->undeclared; // E_NOTICE, később E_WARNING +$obj->undeclared = 1; // csendben lefut, figyelmeztetés nélkül +$obj->unknownMethod(); // Fatal error (nem elfogható try/catch-csel) +``` + +A Fatal error leállította az alkalmazást anélkül, hogy bármilyen reakcióra lehetőség lett volna. A nem létező tagok csendes írása figyelmeztetés nélkül súlyos hibákhoz vezethetett, amelyeket nehéz volt felderíteni. A `Nette\Object` minden ilyen esetet elkapott és `MemberAccessException` kivételt dobott, lehetővé téve a programozóknak, hogy reagáljanak és kezeljék ezeket a hibákat: + +```php +echo $obj->undeclared; // Nette\MemberAccessException kivételt dob +$obj->undeclared = 1; // Nette\MemberAccessException kivételt dob +$obj->unknownMethod(); // Nette\MemberAccessException kivételt dob +``` + +A PHP 7.0 óta a nyelv már nem okoz elfoghatatlan fatal error-t, és a PHP 8.2 óta a nem deklarált tagok elérése hibaként kezelendő. + + +"Did you mean?" segítség +------------------------ +A `Nette\Object` egy nagyon hasznos funkcióval érkezett: intelligens javaslatokkal elgépelések esetén. Amikor egy fejlesztő hibát vétett egy metódus vagy változó nevében, nemcsak jelezte a hibát, hanem segítséget is nyújtott a helyes név javaslatával. Ez az ikonikus üzenet, amely "did you mean?" néven vált ismertté, órákat spórolt meg a programozóknak az elgépelések keresésében: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// Nette\MemberAccessException kivételt dob +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Bár a mai PHP-nek nincs beépített "did you mean?" funkciója, ezt a kiegészítést a [Tracy|tracy:] biztosítja. Sőt, az ilyen hibákat [automatikusan javítani|tracy:open-files-in-ide#demos] is tudja. + + +Tulajdonságok ellenőrzött hozzáféréssel +--------------------------------------- +Jelentős újítás, amit a SmartObject hozott a PHP-ba, az ellenőrzött hozzáféréssel rendelkező tulajdonságok voltak. Ez a koncepció, amely olyan nyelvekben általános, mint a C# vagy Python, lehetővé tette a fejlesztőknek, hogy elegánsan szabályozzák az objektumadatok elérését és biztosítsák azok konzisztenciáját. A tulajdonságok az objektumorientált programozás hatékony eszközei. Változókként működnek, de valójában metódusok (getter-ek és setter-ek) reprezentálják őket. Ez lehetővé teszi a bemenetek validálását vagy az értékek generálását olvasás időpontjában. + +A tulajdonságok használatához szükséges: +- A `@property $xyz` annotáció hozzáadása az osztályhoz +- Getter létrehozása `getXyz()` vagy `isXyz()` néven, setter létrehozása `setXyz()` néven +- A getter és setter *public* vagy *protected* láthatóságú legyen. Opcionálisak - így létezhetnek *csak olvasható* vagy *csak írható* tulajdonságokként + +Nézzünk egy gyakorlati példát a Circle osztályon, ahol a tulajdonságokat használjuk annak biztosítására, hogy a sugár mindig nem negatív szám legyen. A `public $radius`-t tulajdonságra cseréljük: ```php /** @@ -34,7 +79,7 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // nem nyilvános + private float $radius = 0.0; // nem public! // getter a $radius tulajdonsághoz protected function getRadius(): float @@ -45,7 +90,7 @@ class Circle // setter a $radius tulajdonsághoz protected function setRadius(float $radius): void { - // az érték mentés előtti szanálása + // érték sanitizálása mentés előtt $this->radius = max(0.0, $radius); } @@ -57,69 +102,30 @@ class Circle } $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"((syntactic sugar)), 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. - - -Pillantás a történelembe .[#toc-a-glimpse-into-history] -======================================================= - -A SmartObject korábban számos módon finomította az objektumok viselkedését, de a mai PHP már natívan tartalmazza ezen fejlesztések nagy részét. A következő szöveg egy nosztalgikus visszatekintés a történelembe, emlékeztetve minket a dolgok fejlődésére. - -A PHP objektummodellje a kezdetektől fogva számtalan komoly hiányosságtól és hiányosságtól szenvedett. Ez vezetett a `Nette\Object` osztály létrehozásához (2007-ben), amelynek célja ezen problémák orvoslása és a PHP használatának kényelmesebbé tétele volt. Mindössze arra volt szükség, hogy más osztályok is örököljenek belőle, és máris élvezhették az általa kínált előnyöket. Amikor a PHP 5.4 bevezette a tulajdonságok támogatását, a `Nette\Object` osztály helyébe a `Nette\SmartObject` tulajdonság lépett. Ezzel megszűnt a közös őstől való öröklés szükségessége. Sőt, a tulajdonságot olyan osztályokban is lehetett használni, amelyek már örököltek egy másik osztálytól. A `Nette\Object` végleges megszűnését a PHP 7.2 kiadása jelentette, amely megtiltotta, hogy az osztályok neve `Object` legyen. - -A PHP fejlesztésének előrehaladtával az objektummodell és a nyelvi képességek tovább fejlődtek. A `SmartObject` osztály különböző funkciói feleslegessé váltak. A PHP 8.2 kiadása óta csak egyetlen olyan funkció maradt, amelyet a PHP közvetlenül nem támogat: az úgynevezett [tulajdonságok |#Properties, getters, and setters] használatának lehetősége. - -Milyen funkciókat kínált a `Nette\Object` és ennek folytán a `Nette\SmartObject`? Íme egy áttekintés. (A példákban a `Nette\Object` osztályt használjuk, de a legtöbb funkció a `Nette\SmartObject` tulajdonságra is vonatkozik). - - -Inkonzisztens hibák .[#toc-inconsistent-errors] ------------------------------------------------ -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: - -```php -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ó) +$circle->radius = 10; // valójában a setRadius(10)-et hívja +echo $circle->radius; // getRadius()-t hív +echo $circle->visible; // isVisible()-t hív ``` -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. - -```php -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? .[#toc-did-you-mean] ----------------------------------- -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. +A PHP 8.4 óta ugyanez a funkcionalitás elérhető property hooks segítségével, amely sokkal elegánsabb és tömörebb szintaxist kínál: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Bár a mai PHP nem rendelkezik "úgy értetted?" funkcióval, ez a mondat hozzáadható a hibákhoz a [Tracy |tracy:] által. Még az [ilyen hibákat |tracy:open-files-in-ide#toc-demos] is képes [automatikusan kijavítani |tracy:open-files-in-ide#toc-demos]. - -Bővítési módszerek .[#toc-extension-methods] --------------------------------------------- -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á. +Extension methods (Kiterjesztő metódusok) +----------------------------------------- +A `Nette\Object` egy másik érdekes koncepciót hozott a PHP-ba a modern programozási nyelvekből - az extension methods-t. Ez a C#-ból kölcsönzött funkció lehetővé tette a fejlesztőknek, hogy elegánsan bővítsék a meglévő osztályokat új metódusokkal anélkül, hogy módosítaniuk kellene azokat vagy örökölniük kellene belőlük. Például hozzáadhattak egy `addDateTime()` metódust egy űrlaphoz, amely egy egyéni DateTimePicker-t ad hozzá: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $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 extension metódusok nem bizonyultak praktikusnak, mert az editorok nem ajánlották fel a nevüket, sőt jelezték, hogy a metódus nem létezik. Ezért a támogatásuk megszűnt. Ma már általánosabb a kompozíció vagy öröklődés használata az osztályfunkcionalitás kiterjesztésére. -Az osztály nevének megadása .[#toc-getting-the-class-name] ----------------------------------------------------------- +Osztálynév lekérdezése +---------------------- +A SmartObject egyszerű metódust kínált az osztálynév lekérdezésére: ```php -$class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // PHP 8.0 óta +$class = $obj->getClass(); // Nette\Object használatával +$class = $obj::class; // PHP 8.0 óta ``` -Hozzáférés a reflexióhoz és a megjegyzésekhez .[#toc-access-to-reflection-and-annotations] ------------------------------------------------------------------------------------------- - -`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: +Reflexió és annotáció hozzáférés +-------------------------------- +A `Nette\Object` hozzáférést biztosított a reflexióhoz és az annotációkhoz a `getReflection()` és `getAnnotation()` metódusok segítségével. Ez a megközelítés jelentősen egyszerűsítette az osztály metainformációkkal való munkát: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // visszaadja 'John Doe' +$reflection->getAnnotation('author'); // 'John Doe'-t ad vissza ``` -A PHP 8.0-tól kezdve a metainformációkat attribútumok formájában is elérhetjük: +A PHP 8.0 óta lehetséges a metainformációk elérése attribútumok formájában, amelyek még több lehetőséget és jobb típusellenőrzést kínálnak: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Metódus Getterek .[#toc-method-getters] ---------------------------------------- - -`Nette\Object` elegáns módot kínált arra, hogy a módszereket úgy kezeljük, mintha változók lennének: +Method getterek +--------------- +A `Nette\Object` elegáns módot kínált metódusok átadására, mintha változók lennének: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $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":https://www.php.net/manual/en/functions.first_class_callable_syntax: +A PHP 8.1 óta használható az úgynevezett "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, amely még tovább viszi ezt a koncepciót: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Események .[#toc-events] ------------------------- - -`Nette\Object` szintaktikai cukrot kínált az [esemény |nette:glossary#events] kiváltásához: +Események +--------- +A SmartObject egyszerűsített szintaxist kínál az [események|nette:glossary#events] kezeléséhez. Az események lehetővé teszik az objektumoknak, hogy értesítsék az alkalmazás többi részét állapotuk változásairól: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -A `$this->onChange($this, $radius)` kód a következővel egyenértékű: +A `$this->onChange($this, $radius)` kód egyenértékű a következő ciklussal: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -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 |arrays#invoke] függvény: +Az átláthatóság érdekében javasoljuk a `$this->onChange()` mágikus metódus kerülését. Praktikus helyettesítője a [Nette\Utils\Arrays::invoke|arrays#invoke] függvény: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/it/smartobject.texy b/utils/it/smartobject.texy index a9247d68bc..5bf08dac64 100644 --- a/utils/it/smartobject.texy +++ b/utils/it/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject correggeva il comportamento degli oggetti in molti modi, ma il PHP di oggi include già la maggior parte di questi miglioramenti in modo nativo. Tuttavia, aggiunge ancora il supporto per le *proprietà*. +SmartObject ha migliorato per anni il comportamento degli oggetti in PHP. Dalla versione PHP 8.4, tutte le sue funzionalità sono diventate parte nativa di PHP stesso, completando così la sua missione storica di essere pioniere dell'approccio orientato agli oggetti moderno in PHP. Installazione: @@ -11,19 +11,64 @@ Installazione: composer require nette/utils ``` +SmartObject è nato nel 2007 come soluzione rivoluzionaria alle carenze del modello a oggetti di PHP dell'epoca. In un periodo in cui PHP soffriva di numerosi problemi di design orientato agli oggetti, ha portato significativi miglioramenti e semplificazioni nel lavoro degli sviluppatori. È diventato una parte leggendaria del framework Nette. Ha offerto funzionalità che PHP avrebbe acquisito solo molti anni dopo - dalla validazione dell'accesso alle proprietà alla gestione sofisticata degli errori. Con l'arrivo di PHP 8.4, ha completato la sua missione storica, poiché tutte le sue funzionalità sono diventate parti native del linguaggio. Ha anticipato lo sviluppo di PHP di ben 17 anni. -Proprietà, Getter e Setter .[#toc-properties-getters-and-setters] -================================================================= +SmartObject ha attraversato un'interessante evoluzione tecnica. Inizialmente, è stato implementato come classe `Nette\Object`, dalla quale altre classi ereditavano la funzionalità necessaria. Un cambiamento significativo è arrivato con PHP 5.4, che ha introdotto il supporto per i trait. Questo ha permesso la trasformazione nel trait `Nette\SmartObject`, portando maggiore flessibilità - gli sviluppatori potevano utilizzare la funzionalità anche nelle classi che già ereditavano da un'altra classe. Mentre la classe originale `Nette\Object` ha cessato di esistere con PHP 7.2 (che ha proibito di nominare le classi con la parola 'Object'), il trait `Nette\SmartObject` continua a esistere. -Nei moderni linguaggi orientati agli oggetti (ad esempio, C#, Python, Ruby, JavaScript), il termine *property* si riferisce a [membri speciali delle classi |https://en.wikipedia.org/wiki/Property_(programming)] che sembrano variabili, ma in realtà sono rappresentati da metodi. Quando il valore di questa "variabile" viene assegnato o letto, viene richiamato il metodo corrispondente (chiamato getter o setter). Si tratta di una cosa molto comoda, che ci dà il pieno controllo sull'accesso alle variabili. Possiamo convalidare l'input o generare risultati solo quando la proprietà viene letta. +Esaminiamo le funzionalità che `Nette\Object` e successivamente `Nette\SmartObject` offrivano. Ognuna di queste funzioni rappresentava un significativo passo avanti nella programmazione orientata agli oggetti in PHP. -Le proprietà PHP non sono supportate, ma il trait `Nette\SmartObject` può imitarle. Come si usa? -- Aggiungere un'annotazione alla classe nella forma `@property $xyz` +Stati di Errore Consistenti +--------------------------- +Uno dei problemi più urgenti del primo PHP era il comportamento inconsistente nel lavoro con gli oggetti. `Nette\Object` ha portato ordine e prevedibilità in questo caos. Vediamo come si comportava PHP originariamente: + +```php +echo $obj->undeclared; // E_NOTICE, successivamente E_WARNING +$obj->undeclared = 1; // passa silenziosamente senza avviso +$obj->unknownMethod(); // Fatal error (non catturabile con try/catch) +``` + +Fatal error terminava l'applicazione senza possibilità di reazione. La scrittura silenziosa su membri non esistenti senza avviso poteva portare a errori gravi difficili da rilevare. `Nette\Object` catturava tutti questi casi e lanciava una `MemberAccessException`, permettendo ai programmatori di reagire e gestire questi errori: + +```php +echo $obj->undeclared; // lancia Nette\MemberAccessException +$obj->undeclared = 1; // lancia Nette\MemberAccessException +$obj->unknownMethod(); // lancia Nette\MemberAccessException +``` + +Da PHP 7.0, il linguaggio non causa più errori fatali non catturabili e da PHP 8.2, l'accesso a membri non dichiarati è considerato un errore. + + +Suggerimento "Did you mean?" +---------------------------- +`Nette\Object` è arrivato con una funzionalità molto conveniente: i suggerimenti intelligenti per gli errori di battitura. Quando uno sviluppatore commetteva un errore nel nome di un metodo o di una variabile, non solo segnalava l'errore ma offriva anche aiuto suggerendo il nome corretto. Questo messaggio iconico, noto come "did you mean?", ha risparmiato agli sviluppatori ore di ricerca degli errori di battitura: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// lancia Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Mentre PHP stesso non ha alcuna forma di "did you mean?", questa funzionalità è ora fornita da [Tracy|tracy:]. Può persino [correggere automaticamente|tracy:open-files-in-ide#demos] questi errori. + + +Proprietà con Accesso Controllato +--------------------------------- +Un'innovazione significativa che SmartObject ha portato in PHP sono state le proprietà con accesso controllato. Questo concetto, comune in linguaggi come C# o Python, ha permesso agli sviluppatori di controllare elegantemente l'accesso ai dati dell'oggetto e garantire la loro consistenza. Le proprietà sono uno strumento potente della programmazione orientata agli oggetti. Funzionano come variabili ma sono in realtà rappresentate da metodi (getter e setter). Questo permette la validazione degli input o la generazione dei valori al momento della lettura. + +Per utilizzare le proprietà, è necessario: +- Aggiungere l'annotazione `@property $xyz` alla classe - Creare un getter chiamato `getXyz()` o `isXyz()`, un setter chiamato `setXyz()` -- Il getter e il setter devono essere *pubblici* o *protetti* e sono opzionali, quindi può esserci una proprietà *di sola lettura* o *di sola scrittura*. +- Assicurarsi che getter e setter siano *public* o *protected*. Sono opzionali - quindi possono esistere come proprietà *read-only* o *write-only* -Utilizzeremo la proprietà per la classe Circle per garantire che nella variabile `$radius` vengano inseriti solo numeri non negativi. Sostituire `public $radius` con la proprietà: +Vediamo un esempio pratico usando la classe Circle, dove useremo le proprietà per garantire che il raggio sia sempre non negativo. Sostituiremo `public $radius` con una proprietà: ```php /** @@ -34,7 +79,7 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // non pubblico + private float $radius = 0.0; // non public! // getter per la proprietà $radius protected function getRadius(): float @@ -45,7 +90,7 @@ class Circle // setter per la proprietà $radius protected function setRadius(float $radius): void { - // sanitizza il valore prima di salvarlo + // sanitizziamo il valore prima del salvataggio $this->radius = max(0.0, $radius); } @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // in realtà chiama setRadius(10) -echo $circle->radius; // chiama getRadius() +$circle->radius = 10; // in realtà chiama setRadius(10) +echo $circle->radius; // chiama getRadius() echo $circle->visible; // chiama isVisible() ``` -Le proprietà sono principalmente "zucchero sintattico" ((syntactic sugar)), che ha lo scopo di rendere più dolce la vita del programmatore semplificando il codice. Se non le si vuole, non è necessario usarle. - - -Uno sguardo alla storia .[#toc-a-glimpse-into-history] -====================================================== - -SmartObject era solito perfezionare il comportamento degli oggetti in molti modi, ma il PHP di oggi incorpora già la maggior parte di questi miglioramenti in modo nativo. Il testo che segue è uno sguardo nostalgico alla storia, che ci ricorda come si sono evolute le cose. - -Fin dall'inizio, il modello a oggetti di PHP soffriva di una miriade di gravi mancanze e carenze. Questo ha portato alla creazione della classe `Nette\Object` (nel 2007), che mirava a correggere questi problemi e a migliorare il comfort dell'utilizzo di PHP. Bastava che altre classi ereditassero da essa per ottenere i vantaggi che offriva. Quando PHP 5.4 ha introdotto il supporto ai tratti, la classe `Nette\Object` è stata sostituita dal tratto `Nette\SmartObject`. Questo ha eliminato la necessità di ereditare da un antenato comune. Inoltre, il tratto poteva essere usato in classi che già ereditavano da un'altra classe. La fine definitiva di `Nette\Object` avvenne con il rilascio di PHP 7.2, che proibiva alle classi di chiamarsi `Object`. - -Con il proseguire dello sviluppo di PHP, il suo modello di oggetti e le capacità del linguaggio sono migliorati. Diverse funzioni della classe `SmartObject` sono diventate superflue. Dal rilascio di PHP 8.2, rimane solo una caratteristica non supportata direttamente da PHP: la possibilità di usare le cosiddette [proprietà |#Properties, getters, and setters]. - -Quali caratteristiche offrivano `Nette\Object` e, per estensione, `Nette\SmartObject`? Ecco una panoramica. (Negli esempi viene utilizzata la classe `Nette\Object`, ma la maggior parte delle caratteristiche si applicano anche al tratto `Nette\SmartObject` ). - - -Errori inconsistenti .[#toc-inconsistent-errors] ------------------------------------------------- -PHP aveva un comportamento incoerente quando si accedeva a membri non dichiarati. Lo stato al momento di `Nette\Object` era il seguente: - -```php -echo $obj->undeclared; // E_NOTICE, successivamente E_WARNING -$obj->undeclared = 1; // passa in modo silenzioso, senza segnalare nulla -$obj->unknownMethod(); // Errore fatale (non catturabile con try/catch) -``` - -L'errore fatale terminava l'applicazione senza alcuna possibilità di reagire. Scrivere silenziosamente su membri inesistenti senza preavviso poteva portare a errori gravi, difficili da individuare. `Nette\Object` Tutti questi casi sono stati catturati ed è stata lanciata l'eccezione `MemberAccessException`. - -```php -echo $obj->undeclared; // lancia l'eccezione Nette\MemberAccessException -$obj->undeclared = 1; // lanciare l'eccezione Nette\MemberAccessException -$obj->unknownMethod(); // lanciare l'eccezione Nette\MemberAccessException -``` -A partire da PHP 7.0, PHP non causa più errori fatali non catturabili e l'accesso a membri non dichiarati è un bug da PHP 8.2. - - -Intendevi dire? .[#toc-did-you-mean] ------------------------------------- -Se veniva lanciato un errore `Nette\MemberAccessException`, magari a causa di un errore di battitura nell'accesso a una variabile oggetto o nella chiamata di un metodo, `Nette\Object` cercava di dare un suggerimento nel messaggio di errore su come correggere l'errore, sotto forma dell'iconico addendum "intendevi? +Da PHP 8.4, la stessa funzionalità può essere ottenuta usando i property hooks, che offrono una sintassi molto più elegante e concisa: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Sebbene il PHP di oggi non disponga di una funzione di "hai inteso?", questa frase può essere aggiunta agli errori da [Tracy |tracy:]. Può anche [correggere automaticamente tali errori |tracy:open-files-in-ide#toc-demos]. - -Metodi di estensione .[#toc-extension-methods] ----------------------------------------------- -Ispirato ai metodi di estensione di C#. Offrono la possibilità di aggiungere nuovi metodi a classi esistenti. Ad esempio, si può aggiungere il metodo `addDateTime()` a un modulo per aggiungere il proprio DateTimePicker. +Extension Methods +----------------- +`Nette\Object` ha portato in PHP un altro concetto interessante ispirato ai linguaggi di programmazione moderni - i metodi di estensione. Questa funzionalità, presa in prestito da C#, permetteva agli sviluppatori di estendere elegantemente le classi esistenti con nuovi metodi senza modificarle o ereditare da esse. Per esempio, si poteva aggiungere un metodo `addDateTime()` a un form che aggiungesse un DateTimePicker personalizzato: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -I metodi di estensione si sono rivelati poco pratici, perché i loro nomi non venivano autocompilati dagli editor, che invece segnalavano che il metodo non esisteva. Pertanto, il loro supporto è stato interrotto. +I metodi di estensione si sono rivelati poco pratici perché gli editor non suggerivano i loro nomi e invece segnalavano che il metodo non esisteva. Pertanto, il loro supporto è stato interrotto. Oggi, è più comune utilizzare la composizione o l'ereditarietà per estendere la funzionalità delle classi. -Ottenere il nome della classe .[#toc-getting-the-class-name] ------------------------------------------------------------- +Ottenere il Nome della Classe +----------------------------- +SmartObject offriva un metodo semplice per ottenere il nome della classe: ```php $class = $obj->getClass(); // usando Nette\Object -$class = $obj::class; // da PHP 8.0 +$class = $obj::class; // da PHP 8.0 ``` -Accesso alla riflessione e alle annotazioni .[#toc-access-to-reflection-and-annotations] ----------------------------------------------------------------------------------------- - -`Nette\Object` ha offerto l'accesso alla riflessione e alle annotazioni utilizzando i metodi `getReflection()` e `getAnnotation()`: +Accesso a Reflection e Annotazioni +---------------------------------- +`Nette\Object` forniva accesso a reflection e annotazioni attraverso i metodi `getReflection()` e `getAnnotation()`. Questo approccio semplificava significativamente il lavoro con le meta-informazioni delle classi: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // restituisce 'John Doe'. +$reflection->getAnnotation('author'); // restituisce 'John Doe' ``` -A partire da PHP 8.0, è possibile accedere alle meta-informazioni sotto forma di attributi: +Da PHP 8.0, è possibile accedere alle meta-informazioni attraverso gli attributi, che offrono ancora più possibilità e un migliore controllo dei tipi: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Metodi Getter .[#toc-method-getters] ------------------------------------- - -`Nette\Object` offre un modo elegante per trattare i metodi come se fossero variabili: +Method Getters +-------------- +`Nette\Object` offriva un modo elegante per passare i metodi come se fossero variabili: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -A partire da PHP 8.1, è possibile utilizzare la cosiddetta "sintassi callable di prima classe":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Da PHP 8.1, è possibile utilizzare la "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, che porta questo concetto ancora più avanti: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Eventi .[#toc-events] ---------------------- - -`Nette\Object` ha offerto uno zucchero sintattico per innescare l'[evento |nette:glossary#events]: +Eventi +------ +SmartObject offre una sintassi semplificata per lavorare con gli [eventi|nette:glossary#events]. Gli eventi permettono agli oggetti di informare altre parti dell'applicazione sui cambiamenti del loro stato: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Il codice `$this->onChange($this, $radius)` è equivalente al seguente: +Il codice `$this->onChange($this, $radius)` è equivalente al seguente ciclo: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Per motivi di chiarezza si consiglia di evitare il metodo magico `$this->onChange()`. Un sostituto pratico è la funzione [Nette\Utils\Arrays::invoke |arrays#invoke]: +Per chiarezza, raccomandiamo di evitare il metodo magico `$this->onChange()`. Un'alternativa pratica è la funzione [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/pl/smartobject.texy b/utils/pl/smartobject.texy index 199dd1fd8c..8a9811d045 100644 --- a/utils/pl/smartobject.texy +++ b/utils/pl/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject poprawiał zachowanie obiektów na wiele sposobów, ale dzisiejszy PHP zawiera już większość tych ulepszeń natywnie. Wciąż jednak dodaje wsparcie dla *property*. +SmartObject przez lata usprawniał zachowanie obiektów w PHP. Od wersji PHP 8.4 wszystkie jego funkcje są już częścią samego PHP, tym samym kończąc swoją historyczną misję jako pionier nowoczesnego podejścia obiektowego w PHP. Instalacja: @@ -11,19 +11,64 @@ Instalacja: composer require nette/utils ``` +SmartObject powstał w 2007 roku jako rewolucyjne rozwiązanie niedoskonałości ówczesnego modelu obiektowego PHP. W czasie, gdy PHP borykało się z wieloma problemami w projektowaniu obiektowym, wprowadził znaczące usprawnienia i uproszczenia w pracy programistów. Stał się legendarną częścią frameworka Nette. Oferował funkcjonalności, które PHP zyskało dopiero wiele lat później - od walidacji dostępu do właściwości obiektów po zaawansowaną obsługę błędów. Wraz z nadejściem PHP 8.4 zakończył swoją historyczną misję, ponieważ wszystkie jego funkcje stały się natywną częścią języka. Wyprzedził rozwój PHP o imponujące 17 lat. -Właściwości, gettery i settery .[#toc-properties-gettery-a-settery] -=================================================================== +SmartObject przeszedł ciekawą ewolucję techniczną. Początkowo był zaimplementowany jako klasa `Nette\Object`, po której inne klasy dziedziczyły potrzebną funkcjonalność. Znacząca zmiana nastąpiła wraz z PHP 5.4, które wprowadziło obsługę trait. Umożliwiło to transformację w trait `Nette\SmartObject`, co przyniosło większą elastyczność - programiści mogli wykorzystywać funkcjonalność nawet w klasach, które już dziedziczyły po innej klasie. Podczas gdy oryginalna klasa `Nette\Object` przestała istnieć wraz z PHP 7.2 (które zabroniło nazywania klas słowem 'Object'), trait `Nette\SmartObject` żyje nadal. -W nowoczesnych językach obiektowych (np. C#, Python, Ruby, JavaScript) termin *property* odnosi się do [specjalnych członków klas |https://en.wikipedia.org/wiki/Property_(programming)], które wyglądają jak zmienne, ale w rzeczywistości są reprezentowane przez metody. Kiedy wartość tej "zmiennej" jest przypisywana lub odczytywana, wywoływana jest odpowiednia metoda (zwana getterem lub setterem). Jest to bardzo przydatna rzecz, daje nam pełną kontrolę nad dostępem do zmiennych. Możemy zatwierdzić dane wejściowe lub wygenerować wyniki tylko wtedy, gdy właściwość zostanie odczytana. +Przyjrzyjmy się funkcjom, które kiedyś oferował `Nette\Object`, a później `Nette\SmartObject`. Każda z tych funkcji stanowiła w swoim czasie znaczący krok naprzód w programowaniu obiektowym w PHP. -Właściwości PHP nie są obsługiwane, ale traita `Nette\SmartObject` może je imitować. Jak to zrobić? -- Dodaj adnotację do klasy w postaci `@property $xyz` -- Utwórz getter o nazwie `getXyz()` lub `isXyz()`, setter o nazwie `setXyz()` -- Getter i setter muszą być *public* lub *protected* i są opcjonalne, więc może być właściwość *read-only* lub *write-only*. +Spójne stany błędów +------------------- +Jednym z najbardziej palących problemów wczesnego PHP było niespójne zachowanie podczas pracy z obiektami. `Nette\Object` wprowadził porządek i przewidywalność do tego chaosu. Zobaczmy, jak wyglądało oryginalne zachowanie PHP: -Wykorzystamy własność dla klasy Circle, aby zapewnić, że do zmiennej `$radius` wstawiane są tylko liczby nieujemne. Zamień `public $radius` na własność: +```php +echo $obj->undeclared; // E_NOTICE, później E_WARNING +$obj->undeclared = 1; // przechodzi bez ostrzeżenia +$obj->unknownMethod(); // Fatal error (nieprzechwytywany przez try/catch) +``` + +Fatal error powodował zakończenie aplikacji bez możliwości reakcji. Ciche zapisywanie do nieistniejących członków bez ostrzeżenia mogło prowadzić do poważnych błędów, które trudno było wykryć. `Nette\Object` przechwytywał wszystkie te przypadki i rzucał wyjątek `MemberAccessException`, co pozwalało programistom reagować na błędy i je obsługiwać: + +```php +echo $obj->undeclared; // rzuca Nette\MemberAccessException +$obj->undeclared = 1; // rzuca Nette\MemberAccessException +$obj->unknownMethod(); // rzuca Nette\MemberAccessException +``` + +Od PHP 7.0 język nie powoduje już nieprzechwytywanych błędów krytycznych, a od PHP 8.2 dostęp do niezadeklarowanych członków jest traktowany jako błąd. + + +Podpowiedź "Did you mean?" +-------------------------- +`Nette\Object` wprowadził bardzo przydatną funkcję: inteligentne podpowiedzi przy literówkach. Gdy programista popełnił błąd w nazwie metody lub zmiennej, nie tylko zgłaszał błąd, ale także oferował pomoc w postaci sugestii prawidłowej nazwy. Ta charakterystyczna wiadomość, znana jako "did you mean?", zaoszczędziła programistom godziny szukania literówek: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// rzuca Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Dzisiejsze PHP nie ma wprawdzie żadnej formy „did you mean?", ale ten dodatek potrafi uzupełniać [Tracy|tracy:]. A nawet [automatycznie poprawiać|tracy:open-files-in-ide#demos] takie błędy. + + +Properties z kontrolowanym dostępem +----------------------------------- +Znaczącą innowacją, którą SmartObject wprowadził do PHP, były properties z kontrolowanym dostępem. Ta koncepcja, powszechna w językach takich jak C# czy Python, pozwoliła programistom elegancko kontrolować dostęp do danych obiektu i zapewnić ich spójność. Properties są potężnym narzędziem programowania obiektowego. Działają jak zmienne, ale w rzeczywistości są reprezentowane przez metody (gettery i settery). Umożliwia to walidację danych wejściowych lub generowanie wartości w momencie odczytu. + +Aby używać properties, musisz: +- Dodać do klasy adnotację w formacie `@property $xyz` +- Utworzyć getter o nazwie `getXyz()` lub `isXyz()`, setter o nazwie `setXyz()` +- Zapewnić, aby getter i setter były *public* lub *protected*. Są opcjonalne - mogą więc istnieć jako *read-only* lub *write-only* property + +Zobaczmy praktyczny przykład na klasie Circle, gdzie użyjemy properties, aby zapewnić, że promień będzie zawsze nieujemny. Zastąpimy oryginalne `public $radius` przez property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // není publiczne! + private float $radius = 0.0; // nie jest public! - // getter pro właściwość $radius + // getter dla property $radius protected function getRadius(): float { return $this->radius; } - // setter pro właściwość $radius + // setter dla property $radius protected function setRadius(float $radius): void { - // hodnotu před uložením sanitizujeme + // sanityzacja wartości przed zapisem $this->radius = max(0.0, $radius); } - // getter pro właściwość $visible + // getter dla property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // ve skutečnosti volá setRadius(10) -echo $circle->radius; // volá getRadius() -echo $circle->visible; // volá isVisible() -``` - -Właściwości to przede wszystkim "cukier syntaktyczny"((syntactic sugar)), który ma uczynić życie programisty słodszym poprzez uproszczenie kodu. Jeśli ich nie chcesz, nie musisz z nich korzystać. - - -Rzut oka na historię .[#toc-a-glimpse-into-history] -=================================================== - -SmartObject udoskonalał zachowanie obiektów na wiele sposobów, ale dzisiejszy PHP zawiera już większość tych ulepszeń natywnie. Poniższy tekst jest nostalgicznym spojrzeniem wstecz na historię, przypominającym nam, jak rzeczy ewoluowały. - -Od samego początku model obiektowy PHP cierpiał na niezliczoną ilość poważnych niedociągnięć i braków. Doprowadziło to do stworzenia klasy `Nette\Object` (w 2007 roku), która miała na celu naprawienie tych problemów i zwiększenie komfortu korzystania z PHP. Wystarczyło tylko, by inne klasy po niej dziedziczyły, a zyskałyby oferowane przez nią korzyści. Gdy w PHP 5.4 wprowadzono obsługę cech, klasa `Nette\Object` została zastąpiona cechą `Nette\SmartObject`. Wyeliminowało to potrzebę dziedziczenia po wspólnym przodku. Co więcej, cecha mogła być używana w klasach, które już dziedziczyły po innej klasie. Ostateczny koniec `Nette\Object` nastąpił wraz z wydaniem PHP 7.2, które zabroniło klasom nazywania się `Object`. - -W miarę rozwoju PHP, jego model obiektowy i możliwości językowe ulegały poprawie. Różne funkcje klasy `SmartObject` stały się zbędne. Od wydania PHP 8.2 pozostała tylko jedna funkcja nieobsługiwana bezpośrednio w PHP: możliwość korzystania z tak zwanych [właściwości |#Properties, getters, and setters]. - -Jakie funkcje oferowały `Nette\Object` i, rozszerzając, `Nette\SmartObject`? Oto przegląd. (W przykładach użyto klasy `Nette\Object`, ale większość funkcji dotyczy również cechy `Nette\SmartObject` ). - - -Niespójne błędy .[#toc-nekonzistentni-chyby] --------------------------------------------- -PHP zachowywał się niespójnie podczas dostępu do niezadeklarowanych członków. Stan w momencie wejścia na stronę `Nette\Object` był następujący: - -```php -echo $obj->undeclared; // E_NOTICE, później E_WARNING -$obj->undeclared = 1; // przechodzi cicho bez zgłaszania -$obj->unknownMethod(); // Błąd fatalny (nie do wychwycenia przez try/catch) -``` - -Fatal error zakończył działanie aplikacji bez możliwości reakcji. Ciche pisanie do nieistniejących członków bez ostrzeżenia mogło prowadzić do poważnych błędów trudnych do wykrycia. `Nette\Object` Wszystkie te przypadki zostały złapane i wyjątek rzucony przez `MemberAccessException`. - -```php -echo $obj->undeclared; // vyhodí Nette\MemberAccessException -$obj->undeclared = 1; // vyhodí Nette\MemberAccessException -$obj->unknownMethod(); // vyhodí Nette\MemberAccessException +$circle->radius = 10; // w rzeczywistości wywołuje setRadius(10) +echo $circle->radius; // wywołuje getRadius() +echo $circle->visible; // wywołuje isVisible() ``` -Od PHP 7.0, PHP nie powoduje już nieśledzonych błędów fatalnych, a dostęp do niezadeklarowanych członków jest błędem od PHP 8.2. - -Miałeś na myśli? .[#toc-did-you-mean] -------------------------------------- -Jeśli został rzucony błąd `Nette\MemberAccessException`, być może z powodu literówki przy dostępie do zmiennej obiektu lub wywołaniu metody, `Nette\Object` próbował w komunikacie o błędzie dać podpowiedź, jak naprawić błąd, w postaci ikonicznego dodatku "czy miałeś na myśli?". +Od PHP 8.4 można osiągnąć tę samą funkcjonalność za pomocą property hooks, które oferują znacznie elegantszą i zwięzłą składnię: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// vyhodí Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Podczas gdy dzisiejszy PHP nie ma funkcji "czy miałeś na myśli?", ta fraza może być dodawana do błędów przez [Tracy |tracy:]. Może nawet [automatycznie korygować takie błędy |tracy:open-files-in-ide#toc-demos]. - -Metody rozszerzania .[#toc-extension-methods] ---------------------------------------------- -Zainspirowany metodami rozszerzającymi z języka C#. Dawały one możliwość dodawania nowych metod do istniejących klas. Na przykład możesz dodać metodę `addDateTime()` do formularza, aby dodać własny DateTimePicker. +Extension methods +----------------- +`Nette\Object` wprowadził do PHP kolejną interesującą koncepcję zainspirowaną nowoczesnymi językami programowania - extension methods. Ta funkcja, zapożyczona z C#, pozwoliła programistom elegancko rozszerzać istniejące klasy o nowe metody bez konieczności ich modyfikacji lub dziedziczenia. Na przykład można było dodać do formularza metodę `addDateTime()`, która dodaje własny DateTimePicker: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Metody rozszerzania okazały się niepraktyczne, ponieważ ich nazwy nie sugerowały redaktorów, zamiast tego informowały, że dana metoda nie istnieje. Dlatego też zaprzestano ich wspierania. +Extension methods okazały się niepraktyczne, ponieważ ich nazwy nie były podpowiadane przez edytory, przeciwnie - zgłaszały, że metoda nie istnieje. Dlatego ich wsparcie zostało zakończone. Obecnie częściej stosuje się kompozycję lub dziedziczenie do rozszerzania funkcjonalności klas. -Uzyskanie nazwy klasy: +Pobieranie nazwy klasy ---------------------- +Do pobrania nazwy klasy SmartObject oferował prostą metodę: ```php -$class = $obj->getClass(); // używając Nette. -$class = $obj::class; // od PHP 8.0 +$class = $obj->getClass(); // za pomocą Nette\Object +$class = $obj::class; // od PHP 8.0 ``` -Dostęp do refleksji i adnotacji -------------------------------- - -`Nette\Object` zaoferował dostęp do refleksji i adnotacji za pomocą metod `getReflection()` i `getAnnotation()`: +Dostęp do reflection i adnotacji +-------------------------------- +`Nette\Object` oferował dostęp do reflection i adnotacji za pomocą metod `getReflection()` i `getAnnotation()`. To podejście znacząco uprościło pracę z metainformacjami klas: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // vrátí 'John Doe' +$reflection->getAnnotation('author'); // zwraca 'John Doe' ``` -Od PHP 8.0 możliwy jest dostęp do metainformacji w postaci atrybutów: +Od PHP 8.0 można uzyskiwać dostęp do metainformacji za pomocą atrybutów, które oferują jeszcze większe możliwości i lepszą kontrolę typów: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Metoda gettery +Method gettery -------------- - -`Nette\Object` oferował elegancki sposób przekazywania metod tak, jakby były one zmiennymi: +`Nette\Object` oferował elegancki sposób przekazywania metod, jakby były zmiennymi: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Od PHP 8.1 można używać tzw. składni wywoływalnej pierwszej klasy:https://www.php.net/manual/en/functions.first_class_callable_syntax: +Od PHP 8.1 można wykorzystać tzw. "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, która rozwija ten koncept jeszcze dalej: ```php $obj = new Foo; @@ -205,8 +210,7 @@ echo $method(2, 3); // 5 Wydarzenia ---------- - -`Nette\Object` zaoferował cukier syntaktyczny do wywołania [zdarzenia |nette:glossary#Events]: +SmartObject oferuje uproszczoną składnię do pracy z [wydarzeniami|nette:glossary#events]. Wydarzenia pozwalają obiektom informować inne części aplikacji o zmianach swojego stanu: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Kod `$this->onChange($this, $radius)` jest równoważny z następującym: +Kod `$this->onChange($this, $radius)` jest równoważny następującej pętli: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Dla jasności, zalecamy unikanie metody magicznej `$this->onChange()`. Praktycznym substytutem jest funkcja [Nette\Utils\Arrays::invoke |arrays#invoke]: +Ze względu na przejrzystość zalecamy unikanie magicznej metody `$this->onChange()`. Praktycznym zamiennikiem jest funkcja [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/pt/smartobject.texy b/utils/pt/smartobject.texy index e3f7e5f255..4363751d91 100644 --- a/utils/pt/smartobject.texy +++ b/utils/pt/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -O SmartObject costumava corrigir o comportamento dos objetos de várias maneiras, mas o PHP atual já inclui a maioria desses aprimoramentos nativamente. No entanto, ele ainda adiciona suporte para *property*. +O SmartObject aprimorou o comportamento dos objetos em PHP durante muitos anos. Desde o PHP 8.4, todas as suas funcionalidades se tornaram parte nativa do próprio PHP, concluindo assim sua missão histórica como pioneiro da abordagem orientada a objetos moderna em PHP. Instalação: @@ -11,19 +11,64 @@ Instalação: composer require nette/utils ``` +O SmartObject surgiu em 2007 como uma solução revolucionária para as limitações do modelo de objetos do PHP da época. Quando o PHP sofria com vários problemas de design orientado a objetos, ele trouxe melhorias significativas e simplificou o trabalho dos desenvolvedores. Tornou-se uma parte lendária do framework Nette. Oferecia funcionalidades que o PHP só obteria muitos anos depois - desde a validação de acesso às propriedades até o tratamento sofisticado de erros. Com a chegada do PHP 8.4, completou sua missão histórica, pois todas as suas funcionalidades se tornaram partes nativas da linguagem. Esteve à frente do desenvolvimento do PHP por notáveis 17 anos. -Propriedades, Getters e Setters .[#toc-properties-getters-and-setters] -====================================================================== +O SmartObject passou por uma evolução técnica interessante. Inicialmente, foi implementado como a classe `Nette\Object`, da qual outras classes herdavam a funcionalidade necessária. Uma mudança significativa veio com o PHP 5.4, que introduziu o suporte a traits. Isso permitiu a transformação em um trait `Nette\SmartObject`, trazendo maior flexibilidade - os desenvolvedores podiam usar a funcionalidade mesmo em classes que já herdavam de outra classe. Enquanto a classe original `Nette\Object` deixou de existir com o PHP 7.2 (que proibiu nomear classes com a palavra 'Object'), o trait `Nette\SmartObject` continua vivo. -Em linguagens modernas orientadas a objetos (por exemplo, C#, Python, Ruby, JavaScript), o termo *propriedade* se refere a [membros especiais de classes |https://en.wikipedia.org/wiki/Property_(programming)] que se parecem com variáveis, mas que na verdade são representadas por métodos. Quando o valor desta "variável" é atribuído ou lido, o método correspondente (chamado getter ou setter) é chamado. Isto é uma coisa muito útil de se fazer, nos dá total controle sobre o acesso às variáveis. Podemos validar a entrada ou gerar resultados somente quando a propriedade é lida. +Vamos explorar as funcionalidades que o `Nette\Object` e depois o `Nette\SmartObject` ofereciam. Cada uma dessas funções representava um passo significativo na programação orientada a objetos em PHP na época. -As propriedades do PHP não são suportadas, mas o traço `Nette\SmartObject` pode imitá-las. Como utilizá-lo? -- Acrescentar uma anotação à classe no formulário `@property $xyz` -- Crie um getter chamado `getXyz()` ou `isXyz()`, um setter chamado `setXyz()` -- O getter e o setter devem ser *públicos* ou *protegidos* e são opcionais, portanto pode haver uma propriedade *somente de leitura* ou *somente de escrita*. +Estados de Erro Consistentes +---------------------------- +Um dos problemas mais críticos do PHP inicial era o comportamento inconsistente ao trabalhar com objetos. O `Nette\Object` trouxe ordem e previsibilidade a esse caos. Vejamos como o PHP se comportava originalmente: -Utilizaremos a propriedade da classe Circle para garantir que somente números não-negativos sejam colocados na variável `$radius`. Substituir `public $radius` por propriedade: +```php +echo $obj->undeclared; // E_NOTICE, depois E_WARNING +$obj->undeclared = 1; // passa silenciosamente sem aviso +$obj->unknownMethod(); // Fatal error (não capturável por try/catch) +``` + +O fatal error terminava a aplicação sem possibilidade de reação. A escrita silenciosa em membros inexistentes sem aviso poderia levar a erros graves difíceis de detectar. O `Nette\Object` capturava todos esses casos e lançava uma `MemberAccessException`, permitindo que os programadores reagissem e tratassem esses erros: + +```php +echo $obj->undeclared; // lança Nette\MemberAccessException +$obj->undeclared = 1; // lança Nette\MemberAccessException +$obj->unknownMethod(); // lança Nette\MemberAccessException +``` + +Desde o PHP 7.0, a linguagem não causa mais fatal errors não capturáveis, e desde o PHP 8.2, o acesso a membros não declarados é considerado um erro. + + +Sugestão "Did you mean?" +------------------------ +O `Nette\Object` veio com uma funcionalidade muito conveniente: sugestões inteligentes para erros de digitação. Quando um desenvolvedor cometia um erro no nome de um método ou variável, ele não apenas relatava o erro, mas também oferecia ajuda sugerindo o nome correto. Esta mensagem icônica, conhecida como "did you mean?", poupou horas de busca por erros de digitação: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// lança Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Embora o PHP em si não tenha nenhuma forma de "did you mean?", essa funcionalidade agora é fornecida pelo [Tracy|tracy:]. E ele pode até mesmo [corrigir automaticamente|tracy:open-files-in-ide#demos] esses erros. + + +Properties com Acesso Controlado +-------------------------------- +Uma inovação significativa que o SmartObject trouxe ao PHP foram as properties com acesso controlado. Esse conceito, comum em linguagens como C# ou Python, permitiu que os desenvolvedores controlassem elegantemente o acesso aos dados do objeto e garantissem sua consistência. Properties são uma ferramenta poderosa da programação orientada a objetos. Funcionam como variáveis, mas na verdade são representadas por métodos (getters e setters). Isso permite validar entradas ou gerar valores no momento da leitura. + +Para usar properties, você deve: +- Adicionar a anotação `@property $xyz` à classe +- Criar um getter chamado `getXyz()` ou `isXyz()`, um setter chamado `setXyz()` +- Garantir que o getter e setter sejam *public* ou *protected*. Eles são opcionais - assim podem existir como properties *read-only* ou *write-only* + +Vejamos um exemplo prático usando a classe Circle, onde usaremos properties para garantir que o raio seja sempre não negativo. Substituiremos `public $radius` por uma property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // não público + private float $radius = 0.0; // não é public! - // getter para propriedade $radius + // getter para a property $radius protected function getRadius(): float { return $this->radius; } - // setter para propriedade $radius + // setter para a property $radius protected function setRadius(float $radius): void { - // valor higienizante antes de salvá-lo + // sanitiza o valor antes de salvar $this->radius = max(0.0, $radius); } - // adquirente por propriedade $visível + // getter para a property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // na verdade chama setRadius(10) -echo $circle->radius; // chama getRadius() -echo $circle->visible; // chamadas isVisible() -``` - -As propriedades são principalmente o "açúcar sintático"((açúcar sintático)), que se destina a tornar a vida do programador mais doce, simplificando o código. Se você não os quer, não precisa usá-los. - - -Um vislumbre da história .[#toc-a-glimpse-into-history] -======================================================= - -O SmartObject costumava refinar o comportamento dos objetos de várias maneiras, mas o PHP atual já incorpora a maioria desses aprimoramentos de forma nativa. O texto a seguir é um olhar nostálgico para a história, lembrando-nos de como as coisas evoluíram. - -Desde seu início, o modelo de objeto do PHP sofria de uma infinidade de falhas e deficiências graves. Isso levou à criação da classe `Nette\Object` (em 2007), cujo objetivo era corrigir esses problemas e aumentar o conforto do uso do PHP. Tudo o que era necessário era que outras classes herdassem essa classe e obtivessem os benefícios que ela oferecia. Quando o PHP 5.4 introduziu o suporte a características, a classe `Nette\Object` foi substituída pela característica `Nette\SmartObject`. Isso eliminou a necessidade de herdar de um ancestral comum. Além disso, a característica poderia ser usada em classes que já herdavam de outra classe. O fim definitivo de `Nette\Object` veio com o lançamento do PHP 7.2, que proibiu que as classes fossem nomeadas `Object`. - -À medida que o desenvolvimento do PHP continuava, seu modelo de objeto e os recursos da linguagem foram aprimorados. Várias funções da classe `SmartObject` tornaram-se redundantes. Desde o lançamento do PHP 8.2, resta apenas um recurso não suportado diretamente no PHP: a capacidade de usar as chamadas [propriedades |#Properties, getters, and setters]. - -Quais recursos o `Nette\Object` e, por extensão, o `Nette\SmartObject` ofereceram? Aqui está uma visão geral. (Nos exemplos, a classe `Nette\Object` é usada, mas a maioria dos recursos também se aplica à característica `Nette\SmartObject` ). - - -Erros Inconsistentes .[#toc-inconsistent-errors] ------------------------------------------------- -O PHP tinha um comportamento inconsistente ao acessar membros não declarados. O estado, na época de `Nette\Object`, era o seguinte: - -```php -echo $obj->undeclared; // E_NOTICE, mais tarde E_WARNING -$obj->undeclared = 1; // passa silenciosamente sem informar -$obj->unknownMethod(); // Erro fatal (não detectável por tentativa/captura) -``` - -O erro fatal terminou a aplicação sem qualquer possibilidade de reação. Escrever silenciosamente a membros inexistentes sem aviso prévio poderia levar a erros graves que eram difíceis de detectar. `Nette\Object` Todos estes casos foram pegos e uma exceção `MemberAccessException` foi lançada. - -```php -echo $obj->undeclared; // jogue Nette\MemberAccessException -$obj->undeclared = 1; // jogar Nette\MemberAccessException -$obj->unknownMethod(); // jogar Nette\MemberAccessException +$circle->radius = 10; // na verdade chama setRadius(10) +echo $circle->radius; // chama getRadius() +echo $circle->visible; // chama isVisible() ``` -Desde o PHP 7.0, o PHP não causa mais erros fatais não detectáveis, e acessar membros não declarados tem sido um erro desde o PHP 8.2. - -Você quis dizer? .[#toc-did-you-mean] -------------------------------------- -Se um erro `Nette\MemberAccessException` foi lançado, talvez devido a um erro de digitação ao acessar uma variável de objeto ou ao chamar um método, `Nette\Object` tentou dar uma dica na mensagem de erro sobre como corrigir o erro, na forma do icônico adendo "você quis dizer...". +Desde o PHP 8.4, a mesma funcionalidade pode ser alcançada usando property hooks, que oferecem uma sintaxe muito mais elegante e concisa: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Embora o PHP atual não tenha um recurso "você quis dizer?", essa frase pode ser adicionada aos erros pela [Tracy |tracy:]. Ele pode até mesmo [corrigir automaticamente esses erros |tracy:open-files-in-ide#toc-demos]. - -Métodos de extensão .[#toc-extension-methods] ---------------------------------------------- -Inspirado pelos métodos de extensão da C#. Eles deram a possibilidade de adicionar novos métodos às classes existentes. Por exemplo, você poderia adicionar o método `addDateTime()` a um formulário para adicionar seu próprio DateTimePicker. +Extension Methods +----------------- +O `Nette\Object` trouxe outro conceito interessante ao PHP inspirado em linguagens de programação modernas - extension methods. Essa funcionalidade, emprestada do C#, permitia que os desenvolvedores estendessem elegantemente classes existentes com novos métodos sem modificá-las ou herdar delas. Por exemplo, você poderia adicionar um método `addDateTime()` a um formulário que adiciona um DateTimePicker personalizado: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Os métodos de extensão se mostraram impraticáveis porque seus nomes não foram autocompletados pelos editores, em vez disso, eles relataram que o método não existia. Portanto, seu apoio foi descontinuado. +As extension methods se mostraram impráticas porque os editores não sugeriam seus nomes e, em vez disso, relatavam que o método não existia. Portanto, seu suporte foi descontinuado. Hoje, é mais comum usar composição ou herança para estender a funcionalidade da classe. -Como obter o nome da classe .[#toc-getting-the-class-name] ----------------------------------------------------------- +Obtendo o Nome da Classe +------------------------ +O SmartObject oferecia um método simples para obter o nome da classe: ```php $class = $obj->getClass(); // usando Nette\Object -$class = $obj::class; // desde PHP 8.0 +$class = $obj::class; // desde PHP 8.0 ``` -Acesso à Reflexão e Anotações .[#toc-access-to-reflection-and-annotations] --------------------------------------------------------------------------- - -`Nette\Object` ofereceu acesso à reflexão e à anotação utilizando os métodos `getReflection()` e `getAnnotation()`: +Acesso à Reflexão e Anotações +----------------------------- +O `Nette\Object` fornecia acesso à reflexão e anotações através dos métodos `getReflection()` e `getAnnotation()`. Essa abordagem simplificou significativamente o trabalho com metainformações de classe: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // devolve 'John Doe' +$reflection->getAnnotation('author'); // retorna 'John Doe' ``` -A partir do PHP 8.0, é possível acessar meta-informação sob a forma de atributos: +Desde o PHP 8.0, é possível acessar metainformações através de atributos, que oferecem ainda mais possibilidades e melhor verificação de tipo: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Método Getters .[#toc-method-getters] -------------------------------------- - -`Nette\Object` oferecia uma maneira elegante de lidar com métodos como se fossem variáveis: +Method Getters +-------------- +O `Nette\Object` oferecia uma maneira elegante de passar métodos como se fossem variáveis: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -A partir do PHP 8.1, você pode usar a chamada "sintaxe de primeira classe que pode ser chamada":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Desde o PHP 8.1, você pode usar a "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, que leva esse conceito ainda mais longe: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Eventos .[#toc-events] ----------------------- - -`Nette\Object` ofereceu açúcar sintáctico para acionar o [evento |nette:glossary#events]: +Eventos +------- +O SmartObject oferece uma sintaxe simplificada para trabalhar com [eventos|nette:glossary#events]. Eventos permitem que objetos informem outras partes da aplicação sobre mudanças em seu estado: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -O código `$this->onChange($this, $radius)` é equivalente ao seguinte: +O código `$this->onChange($this, $radius)` é equivalente ao seguinte loop: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Por uma questão de clareza, recomendamos evitar o método mágico `$this->onChange()`. Um substituto prático é a [Arrays Nette\Utils::invoke |arrays#invoke] function: +Para maior clareza, recomendamos evitar o método mágico `$this->onChange()`. Uma substituição prática é a função [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/ro/smartobject.texy b/utils/ro/smartobject.texy index 3d1ed9e89f..3f9d6840b4 100644 --- a/utils/ro/smartobject.texy +++ b/utils/ro/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject obișnuia să corecteze comportamentul obiectelor în mai multe moduri, dar PHP-ul de astăzi include deja majoritatea acestor îmbunătățiri în mod nativ. Cu toate acestea, se adaugă încă suport pentru *proprietate*. +SmartObject a îmbunătățit comportamentul obiectelor în PHP timp de mulți ani. Începând cu PHP 8.4, toate funcționalitățile sale au devenit parte nativă a PHP-ului, completându-și astfel misiunea istorică de a fi pionierul abordării moderne orientate pe obiecte în PHP. Instalare: @@ -11,19 +11,64 @@ Instalare: composer require nette/utils ``` +SmartObject a apărut în 2007 ca o soluție revoluționară pentru deficiențele modelului de obiecte din PHP la acea vreme. Într-o perioadă în care PHP suferea de numeroase probleme legate de design-ul orientat pe obiecte, acesta a adus îmbunătățiri semnificative și a simplificat munca dezvoltatorilor. A devenit o parte legendară a framework-ului Nette. A oferit funcționalități pe care PHP le-a obținut abia mulți ani mai târziu - de la validarea accesului la proprietăți până la gestionarea sofisticată a erorilor. Odată cu apariția PHP 8.4, și-a încheiat misiunea istorică, deoarece toate funcționalitățile sale au devenit părți native ale limbajului. A fost cu 17 ani înaintea dezvoltării PHP-ului. -Proprietăți, Getters și Setters .[#toc-properties-getters-and-setters] -====================================================================== +SmartObject a trecut printr-o evoluție tehnică interesantă. Inițial, a fost implementat ca și clasă `Nette\Object`, de la care alte clase moșteneau funcționalitatea necesară. O schimbare semnificativă a venit cu PHP 5.4, care a introdus suportul pentru trait-uri. Acest lucru a permis transformarea în trait-ul `Nette\SmartObject`, oferind mai multă flexibilitate - dezvoltatorii puteau folosi funcționalitatea chiar și în clasele care moșteneau deja dintr-o altă clasă. În timp ce clasa originală `Nette\Object` a încetat să existe odată cu PHP 7.2 (care a interzis denumirea claselor cu cuvântul 'Object'), trait-ul `Nette\SmartObject` continuă să existe. -În limbajele moderne orientate pe obiecte (de exemplu, C#, Python, Ruby, JavaScript), termenul *proprietate* se referă la [membrii speciali ai claselor |https://en.wikipedia.org/wiki/Property_(programming)] care seamănă cu variabilele, dar care sunt de fapt reprezentate de metode. Atunci când valoarea acestei "variabile" este atribuită sau citită, se apelează metoda corespunzătoare (numită getter sau setter). Acesta este un lucru foarte util, deoarece ne oferă un control total asupra accesului la variabile. Putem valida intrarea sau genera rezultate numai atunci când proprietatea este citită. +Să explorăm funcționalitățile pe care `Nette\Object` și mai târziu `Nette\SmartObject` le-au oferit. Fiecare dintre aceste funcții a reprezentat un pas important înainte în programarea orientată pe obiecte în PHP. -Proprietățile PHP nu sunt acceptate, dar trait `Nette\SmartObject` le poate imita. Cum se utilizează? -- Adăugați o adnotare la clasă sub forma `@property $xyz` +Stări de eroare consistente +--------------------------- +Una dintre cele mai presante probleme ale PHP-ului timpuriu a fost comportamentul inconsistent în lucrul cu obiectele. `Nette\Object` a adus ordine și predictibilitate în acest haos. Să vedem cum se comporta PHP-ul inițial: + +```php +echo $obj->undeclared; // E_NOTICE, mai târziu E_WARNING +$obj->undeclared = 1; // trece silențios fără avertisment +$obj->unknownMethod(); // Fatal error (imposibil de prins cu try/catch) +``` + +Fatal error termina aplicația fără posibilitatea de a reacționa. Scrierea silențioasă în membri inexistenți fără avertisment putea duce la erori grave, greu de detectat. `Nette\Object` prindea toate aceste cazuri și arunca o excepție `MemberAccessException`, permițând programatorilor să reacționeze și să gestioneze aceste erori: + +```php +echo $obj->undeclared; // aruncă Nette\MemberAccessException +$obj->undeclared = 1; // aruncă Nette\MemberAccessException +$obj->unknownMethod(); // aruncă Nette\MemberAccessException +``` + +Din PHP 7.0, limbajul nu mai cauzează fatal error-uri imposibil de prins, iar din PHP 8.2, accesul la membri nedeclarați este considerat o eroare. + + +Ajutorul "Did you mean?" +------------------------ +`Nette\Object` a venit cu o funcționalitate foarte convenabilă: sugestii inteligente pentru greșeli de scriere. Când un dezvoltator făcea o greșeală în numele unei metode sau variabile, nu doar raporta eroarea, ci oferea și ajutor sugerând numele corect. Acest mesaj iconic, cunoscut ca "did you mean?", a economisit programatorilor ore întregi de căutare a greșelilor de scriere: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// aruncă Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Deși PHP-ul în sine nu are nicio formă de "did you mean?", această funcționalitate este acum oferită de [Tracy|tracy:]. Poate chiar [auto-corecta|tracy:open-files-in-ide#demos] astfel de erori. + + +Properties cu acces controlat +----------------------------- +O inovație semnificativă pe care SmartObject a adus-o în PHP au fost properties cu acces controlat. Acest concept, comun în limbaje precum C# sau Python, a permis dezvoltatorilor să controleze elegant accesul la datele obiectelor și să asigure consistența acestora. Properties sunt un instrument puternic al programării orientate pe obiecte. Funcționează ca variabile, dar sunt de fapt reprezentate prin metode (getteri și setteri). Acest lucru permite validarea intrărilor sau generarea valorilor în momentul citirii. + +Pentru a utiliza properties, trebuie să: +- Adăugați adnotarea `@property $xyz` la clasă - Creați un getter numit `getXyz()` sau `isXyz()`, un setter numit `setXyz()` -- Getterul și setterul trebuie să fie *public* sau *protected* și sunt opționale, astfel încât poate exista o proprietate *read-only* sau *write-only*. +- Să vă asigurați că getter-ul și setter-ul sunt *public* sau *protected*. Sunt opționali - astfel pot exista ca properties *read-only* sau *write-only* -Vom utiliza proprietatea pentru clasa Circle pentru a ne asigura că în variabila `$radius` sunt introduse numai numere non-negative. Înlocuiți `public $radius` cu proprietatea: +Să vedem un exemplu practic folosind clasa Circle, unde vom folosi properties pentru a ne asigura că raza este întotdeauna non-negativă. Vom înlocui `public $radius` cu o property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // nu sunt publice + private float $radius = 0.0; // nu este public! - // getter pentru proprietatea $radius + // getter pentru property-ul $radius protected function getRadius(): float { return $this->radius; } - // setter pentru proprietatea $radius + // setter pentru property-ul $radius protected function setRadius(float $radius): void { - // curățarea valorii înainte de a o salva + // sanitizăm valoarea înainte de salvare $this->radius = max(0.0, $radius); } - // getter pentru proprietatea $visible + // getter pentru property-ul $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // apelează de fapt setRadius(10) +$circle->radius = 10; // de fapt apelează setRadius(10) echo $circle->radius; // apelează getRadius() -echo $circle->visible; // solicită isVisible() -``` - -Proprietățile sunt în primul rând "zahăr sintactic"((zahăr sintactic)), care are rolul de a face viața programatorului mai ușoară prin simplificarea codului. Dacă nu le doriți, nu trebuie să le folosiți. - - -O privire în istorie .[#toc-a-glimpse-into-history] -=================================================== - -SmartObject obișnuia să rafineze comportamentul obiectelor în numeroase moduri, dar PHP-ul de astăzi încorporează deja majoritatea acestor îmbunătățiri în mod nativ. Următorul text este o privire nostalgică asupra istoriei, amintindu-ne cum au evoluat lucrurile. - -Încă de la începuturile sale, modelul de obiecte din PHP a suferit de o multitudine de deficiențe și neajunsuri grave. Acest lucru a dus la crearea clasei `Nette\Object` (în 2007), care a avut ca scop rectificarea acestor probleme și îmbunătățirea confortului de utilizare a PHP. Tot ceea ce era necesar era ca alte clase să moștenească din ea și să obțină beneficiile oferite de aceasta. Când PHP 5.4 a introdus suportul pentru trăsături, clasa `Nette\Object` a fost înlocuită cu trăsătura `Nette\SmartObject`. Acest lucru a eliminat necesitatea de a moșteni de la un strămoș comun. În plus, trăsătura putea fi utilizată în clase care moșteneau deja de la o altă clasă. Sfârșitul definitiv al `Nette\Object` a venit odată cu lansarea PHP 7.2, care a interzis ca clasele să fie denumite `Object`. - -Pe măsură ce dezvoltarea PHP a continuat, modelul său de obiecte și capacitățile limbajului s-au îmbunătățit. Diverse funcții ale clasei `SmartObject` au devenit redundante. De la lansarea PHP 8.2, a rămas o singură funcție care nu este direct suportată în PHP: capacitatea de a utiliza așa-numitele [proprietăți |#Properties, getters, and setters]. - -Ce caracteristici au oferit `Nette\Object` și, prin extensie, `Nette\SmartObject`? Iată o prezentare generală. (În exemple, este utilizată clasa `Nette\Object`, dar majoritatea caracteristicilor se aplică și trăsăturii `Nette\SmartObject` ). - - -Erori inconsistente .[#toc-inconsistent-errors] ------------------------------------------------ -PHP avea un comportament inconsecvent la accesarea membrilor nedeclarați. Starea la momentul `Nette\Object` era următoarea: - -```php -echo $obj->undeclared; // E_NOTICE, ulterior E_WARNING -$obj->undeclared = 1; // trece în tăcere fără a raporta -$obj->unknownMethod(); // Eroare fatală (care nu poate fi prinsă prin try/catch) -``` - -O eroare fatală a încheiat aplicația fără nicio posibilitate de reacție. Scrierea silențioasă în membri inexistenți fără avertisment putea duce la erori grave, greu de detectat. `Nette\Object` Toate aceste cazuri au fost detectate și a fost lansată o excepție `MemberAccessException`. - -```php -echo $obj->undeclared; // aruncați Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException +echo $circle->visible; // apelează isVisible() ``` -Începând cu PHP 7.0, PHP nu mai provoacă erori fatale care nu pot fi prinse, iar accesarea membrilor nedeclarați este un bug începând cu PHP 8.2. - -Ați vrut să spuneți? .[#toc-did-you-mean] ------------------------------------------ -În cazul în care se producea o eroare `Nette\MemberAccessException`, poate din cauza unei greșeli de scriere la accesarea unei variabile de obiect sau la apelarea unei metode, `Nette\Object` încerca să ofere un indiciu în mesajul de eroare cu privire la modul de corectare a erorii, sub forma unui addendum iconic "did you mean?". +Din PHP 8.4, aceeași funcționalitate poate fi obținută folosind property hooks, care oferă o sintaxă mult mai elegantă și concisă: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -În timp ce PHP-ul actual nu are o funcție "ai vrut să spui?", această frază poate fi adăugată la erori de către [Tracy |tracy:]. Acesta poate chiar să [corecteze automat astfel de erori |tracy:open-files-in-ide#toc-demos]. - -Metode de extensie .[#toc-extension-methods] --------------------------------------------- -Inspirat de metodele de extensie din C#. Acestea au oferit posibilitatea de a adăuga noi metode la clasele existente. De exemplu, ați putea adăuga metoda `addDateTime()` la un formular pentru a adăuga propriul DateTimePicker. +Extension methods +----------------- +`Nette\Object` a adus în PHP un alt concept interesant inspirat din limbajele de programare moderne - extension methods. Această funcționalitate, împrumutată din C#, a permis dezvoltatorilor să extindă elegant clasele existente cu metode noi fără a le modifica sau moșteni. De exemplu, puteați adăuga o metodă `addDateTime()` unui formular care adaugă un DateTimePicker personalizat: ```php Form::extensionMethod( @@ -131,11 +137,12 @@ $form = new Form; $form->addDateTime('date'); ``` -Metodele de extensie s-au dovedit a fi nepractice deoarece numele lor nu era completat automat de către editori, în schimb aceștia raportau că metoda nu există. Prin urmare, suportul lor a fost întrerupt. +Extension methods s-au dovedit nepractice deoarece editoarele nu sugerau numele lor și în schimb raportau că metoda nu există. Prin urmare, suportul lor a fost întrerupt. Astăzi, este mai comun să se folosească compoziția sau moștenirea pentru a extinde funcționalitatea clasei. -Obținerea numelui clasei .[#toc-getting-the-class-name] -------------------------------------------------------- +Obținerea numelui clasei +------------------------ +SmartObject oferea o metodă simplă pentru obținerea numelui clasei: ```php $class = $obj->getClass(); // folosind Nette\Object @@ -143,10 +150,9 @@ $class = $obj::class; // din PHP 8.0 ``` -Acces la reflecție și adnotări .[#toc-access-to-reflection-and-annotations] ---------------------------------------------------------------------------- - -`Nette\Object` a oferit acces la reflectare și adnotare prin intermediul metodelor `getReflection()` și `getAnnotation()`: +Acces la Reflexie și Adnotări +----------------------------- +`Nette\Object` oferea acces la reflexie și adnotări prin metodele `getReflection()` și `getAnnotation()`. Această abordare a simplificat semnificativ lucrul cu meta-informațiile claselor: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // returnează "John Doe +$reflection->getAnnotation('author'); // returnează 'John Doe' ``` -Începând cu PHP 8.0, este posibilă accesarea meta-informațiilor sub formă de atribute: +Din PHP 8.0, este posibil să accesați meta-informațiile prin atribute, care oferă chiar mai multe posibilități și o verificare mai bună a tipurilor: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Metode Getters .[#toc-method-getters] -------------------------------------- - -`Nette\Object` a oferit o modalitate elegantă de a trata metodele ca și cum ar fi fost variabile: +Method getteri +-------------- +`Nette\Object` oferea o modalitate elegantă de a transmite metode ca și cum ar fi fost variabile: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Începând cu PHP 8.1, puteți utiliza așa-numita "sintaxă de primă clasă pentru metode apelabile":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Din PHP 8.1, puteți utiliza "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, care duce acest concept și mai departe: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Evenimente .[#toc-events] -------------------------- - -`Nette\Object` a oferit zahăr sintactic pentru a declanșa [evenimentul |nette:glossary#events]: +Evenimente +---------- +SmartObject oferă o sintaxă simplificată pentru lucrul cu [evenimente|nette:glossary#events]. Evenimentele permit obiectelor să informeze alte părți ale aplicației despre schimbările din starea lor: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Codul `$this->onChange($this, $radius)` este echivalent cu următoarele: +Codul `$this->onChange($this, $radius)` este echivalent cu următoarea buclă: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Din motive de claritate, vă recomandăm să evitați metoda magică `$this->onChange()`. Un substitut practic este funcția [Nette\Utils\Arrays::invoke |arrays#invoke]: +Pentru claritate, recomandăm evitarea metodei magice `$this->onChange()`. O înlocuire practică este funcția [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/ru/smartobject.texy b/utils/ru/smartobject.texy index e0301323fd..cf1c5f6540 100644 --- a/utils/ru/smartobject.texy +++ b/utils/ru/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject во многом исправлял поведение объектов, но современный PHP уже включает большинство этих улучшений нативно. Тем не менее, в нем все еще добавлена поддержка *property*. +SmartObject на протяжении многих лет улучшал поведение объектов в PHP. Начиная с версии PHP 8.4, все его функции стали встроенной частью самого PHP, тем самым завершив свою историческую миссию первопроходца современного объектно-ориентированного подхода в PHP. Установка: @@ -11,19 +11,64 @@ SmartObject во многом исправлял поведение объект composer require nette/utils ``` +SmartObject появился в 2007 году как революционное решение недостатков объектной модели PHP того времени. В период, когда PHP страдал от множества проблем с объектно-ориентированным дизайном, он принес значительные улучшения и упростил работу разработчиков. Он стал легендарной частью фреймворка Nette. SmartObject предлагал функциональность, которую PHP получил лишь много лет спустя - от валидации доступа к свойствам объектов до сложной обработки ошибок. С выходом PHP 8.4 он завершил свою историческую миссию, так как все его функции стали встроенной частью языка. SmartObject опередил развитие PHP на впечатляющие 17 лет. -Свойства, геттери и сеттери .[#toc-properties-getters-and-setters] -================================================================== +Технически SmartObject прошел интересный путь развития. Изначально он был реализован как класс `Nette\Object`, от которого другие классы наследовали необходимую функциональность. Значительные изменения пришли с PHP 5.4, который ввел поддержку trait-ов. Это позволило трансформировать его в trait `Nette\SmartObject`, что принесло большую гибкость - разработчики могли использовать функциональность даже в классах, которые уже наследовались от другого класса. В то время как оригинальный класс `Nette\Object` прекратил существование с выходом PHP 7.2 (который запретил именование классов словом 'Object'), trait `Nette\SmartObject` продолжает жить. -В современных объектно-ориентированных языках (например, C#, Python, Ruby, JavaScript) термин *свойство* относится к [специальным членам классов |https://en.wikipedia.org/wiki/Property_(programming)], которые выглядят как переменные, но на самом деле представлены методами. Когда значение такой "переменной" присваивается или считывается, вызывается соответствующий метод (называемый getter или setter). Это очень удобная вещь, она дает нам полный контроль над доступом к переменным. Мы можем проверять вводимые данные или генерировать результаты только тогда, когда свойство прочитано. +Давайте рассмотрим возможности, которые предлагали `Nette\Object` и позже `Nette\SmartObject`. Каждая из этих функций в свое время представляла значительный шаг вперед в области объектно-ориентированного программирования в PHP. -Свойства PHP не поддерживаются, но trait `Nette\SmartObject` может их имитировать. Как это использовать? -- Добавьте аннотацию к классу в виде `@property $xyz` -- Создайте геттер с именем `getXyz()` или `isXyz()`, сеттер с именем `setXyz()` -- Геттер и сеттер должны быть *публичными* или *защищенными* и необязательными, поэтому может быть свойство *только для чтения* или *только для записи*. +Последовательная обработка ошибок +--------------------------------- +Одной из самых острых проблем раннего PHP было непоследовательное поведение при работе с объектами. `Nette\Object` внес порядок и предсказуемость в этот хаос. Посмотрим, как вело себя PHP изначально: + +```php +echo $obj->undeclared; // E_NOTICE, позже E_WARNING +$obj->undeclared = 1; // проходит молча без предупреждения +$obj->unknownMethod(); // Fatal error (необрабатываемый try/catch) +``` + +Fatal error завершал приложение без возможности какой-либо реакции. Тихая запись в несуществующие члены без предупреждения могла привести к серьезным ошибкам, которые было трудно обнаружить. `Nette\Object` отлавливал все эти случаи и выбрасывал исключение `MemberAccessException`, что позволяло программистам реагировать на ошибки и обрабатывать их: + +```php +echo $obj->undeclared; // выбрасывает Nette\MemberAccessException +$obj->undeclared = 1; // выбрасывает Nette\MemberAccessException +$obj->unknownMethod(); // выбрасывает Nette\MemberAccessException +``` + +Начиная с PHP 7.0, язык больше не вызывает необрабатываемые fatal error, а с PHP 8.2 доступ к необъявленным членам считается ошибкой. + + +Подсказка "Did you mean?" +------------------------- +`Nette\Object` пришел с очень удобной функцией: интеллектуальными подсказками при опечатках. Когда разработчик делал ошибку в названии метода или переменной, он не только сообщал об ошибке, но и предлагал помощь в виде предложения правильного названия. Это знаковое сообщение, известное как "did you mean?", сэкономило программистам часы поиска опечаток: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// выбрасывает Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Хотя современный PHP не имеет встроенных подсказок "did you mean?", эту функциональность теперь предоставляет [Tracy|tracy:]. Он даже может [автоматически исправлять|tracy:open-files-in-ide#demos] такие ошибки. -Мы будем использовать свойство для класса Circle, чтобы гарантировать, что в переменную `$radius` будут помещаться только неотрицательные числа. Замените `public $radius` на property: + +Properties с контролируемым доступом +------------------------------------ +Значительным нововведением, которое SmartObject принес в PHP, были properties с контролируемым доступом. Эта концепция, распространенная в таких языках как C# или Python, позволила разработчикам элегантно контролировать доступ к данным объекта и обеспечивать их целостность. Properties являются мощным инструментом объектно-ориентированного программирования. Они работают как переменные, но фактически представлены методами (getters и setters). Это позволяет валидировать входные данные или генерировать значения в момент чтения. + +Для использования properties необходимо: +- Добавить классу аннотацию вида `@property $xyz` +- Создать getter с именем `getXyz()` или `isXyz()`, setter с именем `setXyz()` +- Обеспечить, чтобы getter и setter были *public* или *protected*. Они опциональны - могут существовать как *read-only* или *write-only* properties + +Рассмотрим практический пример на классе Circle, где мы используем properties для обеспечения того, чтобы радиус всегда был неотрицательным числом. Заменим `public $radius` на property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // not public + private float $radius = 0.0; // не public! - // getter for property $radius + // getter для property $radius protected function getRadius(): float { return $this->radius; } - // setter for property $radius + // setter для property $radius protected function setRadius(float $radius): void { - // sanitizing value before saving it + // санитизируем значение перед сохранением $this->radius = max(0.0, $radius); } - // getter for property $visible + // getter для property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // actually calls setRadius(10) -echo $circle->radius; // calls getRadius() -echo $circle->visible; // calls isVisible() +$circle->radius = 10; // фактически вызывает setRadius(10) +echo $circle->radius; // вызывает getRadius() +echo $circle->visible; // вызывает isVisible() ``` -Свойства - это прежде всего "синтаксический сахар"((syntactic sugar)), который призван сделать жизнь программиста слаще за счет упрощения кода. Если они вам не нужны, вы не обязаны их использовать. - - -Взгляд в историю .[#toc-a-glimpse-into-history] -=============================================== - -SmartObject использовался для улучшения поведения объектов различными способами, но современный PHP уже включает в себя большинство из этих улучшений. Следующий текст - это ностальгический взгляд в историю, напоминающий нам о том, как все развивалось. - -С самого начала своего существования объектная модель PHP страдала от множества серьезных недостатков и недоработок. Это привело к созданию класса `Nette\Object` (в 2007 году), который был призван исправить эти недостатки и повысить удобство работы с PHP. Достаточно было наследовать от него другие классы, чтобы они получили те преимущества, которые он дает. Когда в PHP 5.4 появилась поддержка трейтов, класс `Nette\Object` был заменен трейтом `Nette\SmartObject`. Это избавило от необходимости наследоваться от общего предка. Более того, трейты можно было использовать в классах, которые уже наследовались от другого класса. Окончательный конец `Nette\Object` наступил с выходом PHP 7.2, который запретил называть классы `Object`. - -По мере развития PHP его объектная модель и возможности языка совершенствовались. Различные функции класса `SmartObject` стали излишними. После выхода PHP 8.2 в PHP осталась только одна функция, не поддерживаемая напрямую: возможность использования так называемых [свойств |#Properties, getters, and setters]. - -Какие же возможности предоставляли `Nette\Object` и, соответственно, `Nette\SmartObject`? Вот обзор. (В примерах используется класс `Nette\Object`, но большинство возможностей применимо и к свойству `Nette\SmartObject` ). - - -Непоследовательные ошибки .[#toc-inconsistent-errors] ------------------------------------------------------ -PHP имел непоследовательное поведение при обращении к необъявленным членам. Состояние на момент публикации `Nette\Object` было следующим: - -```php -echo $obj->undeclared; // E_NOTICE, later E_WARNING -$obj->undeclared = 1; // passes silently without reporting -$obj->unknownMethod(); // Fatal error (not catchable by try/catch) -``` - -Фатальная ошибка завершала приложение без какой-либо возможности отреагировать. Тихая запись в несуществующие члены без предупреждения могла привести к серьезным ошибкам, которые было трудно обнаружить. `Nette\Object` Все эти случаи были пойманы, и было выброшено исключение `MemberAccessException`. +Начиная с PHP 8.4, той же функциональности можно достичь с помощью property hooks, которые предлагают более элегантный и краткий синтаксис: ```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException -``` -Начиная с PHP 7.0, PHP больше не вызывает неперехватываемых фатальных ошибок, а доступ к необъявленным членам стал ошибкой начиная с PHP 8.2. - - -Вы имели в виду? .[#toc-did-you-mean] -------------------------------------- -Если возникала ошибка `Nette\MemberAccessException`, возможно, из-за опечатки при обращении к объектной переменной или вызове метода, `Nette\Object` пытался дать подсказку в сообщении об ошибке, как исправить ошибку, в виде знакового дополнения "Вы имели в виду?". - -```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Хотя современный PHP не имеет функции "Вы имели в виду?", эта фраза может быть добавлена к ошибкам [Tracy |tracy:]. Она даже может [автоматически исправлять такие ошибки |tracy:open-files-in-ide#toc-demos]. - -Методы расширения .[#toc-extension-methods] -------------------------------------------- -Вдохновлен методами расширения из C#. Они дают возможность добавлять новые методы к существующим классам. Например, вы можете добавить метод `addDateTime()` к форме, чтобы добавить свой собственный DateTimePicker. +Extension methods +----------------- +`Nette\Object` принес в PHP еще одну интересную концепцию, вдохновленную современными языками программирования - extension methods. Эта функция, заимствованная из C#, позволяла разработчикам элегантно расширять существующие классы новыми методами без необходимости их модификации или наследования. Например, вы могли добавить в форму метод `addDateTime()`, который добавляет пользовательский DateTimePicker: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Методы расширения оказались непрактичными, поскольку их имена не автозаполнялись редакторами, вместо этого они сообщали, что метод не существует. Поэтому их поддержка была прекращена. +Extension methods оказались непрактичными, потому что редакторы не подсказывали их имена, а наоборот, сообщали, что метод не существует. Поэтому их поддержка была прекращена. Сегодня для расширения функциональности классов чаще используется композиция или наследование. -Получение имени класса .[#toc-getting-the-class-name] ------------------------------------------------------ +Получение имени класса +---------------------- +Для получения имени класса SmartObject предлагал простой метод: ```php -$class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // since PHP 8.0 +$class = $obj->getClass(); // используя Nette\Object +$class = $obj::class; // начиная с PHP 8.0 ``` -Доступ к размышлениям и аннотациям .[#toc-access-to-reflection-and-annotations] -------------------------------------------------------------------------------- - -`Nette\Object` предложен доступ к размышлениям и аннотациям с помощью методов `getReflection()` и `getAnnotation()`: +Доступ к рефлексии и аннотациям +------------------------------- +`Nette\Object` предоставлял доступ к рефлексии и аннотациям через методы `getReflection()` и `getAnnotation()`. Этот подход значительно упростил работу с метаинформацией классов: ```php /** @@ -158,10 +164,10 @@ class Foo extends Nette\Object $obj = new Foo; $reflection = $obj->getReflection(); -$reflection->getAnnotation('author'); // returns 'John Doe' +$reflection->getAnnotation('author'); // возвращает 'John Doe' ``` -Начиная с PHP 8.0, появилась возможность доступа к мета-информации в виде атрибутов: +Начиная с PHP 8.0, можно получать доступ к метаинформации через атрибуты, которые предлагают еще больше возможностей и лучшую проверку типов: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Геттеры методов .[#toc-method-getters] --------------------------------------- - -`Nette\Object` предлагают элегантный способ работы с методами, как если бы они были переменными: +Method getters +-------------- +`Nette\Object` предлагал элегантный способ передачи методов, как если бы они были переменными: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Начиная с PHP 8.1, вы можете использовать так называемый "первоклассный синтаксис вызываемых методов":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Начиная с PHP 8.1, можно использовать так называемый "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, который развивает эту концепцию еще дальше: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -События .[#toc-events] ----------------------- - -`Nette\Object` предлагает синтаксический сахар для запуска [события |nette:glossary#events]: +События +------- +SmartObject предлагает упрощенный синтаксис для работы с [событиями|nette:glossary#events]. События позволяют объектам информировать другие части приложения об изменениях своего состояния: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Код `$this->onChange($this, $radius)` эквивалентен следующему: +Код `$this->onChange($this, $radius)` эквивалентен следующему циклу: ```php foreach ($this->onChange as $callback) { @@ -229,8 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Для ясности мы рекомендуем избегать магического метода `$this->onChange()`. Хорошей заменой будет [Nette\Utils\Arrays::invoke |arrays#invoke]: +Для ясности мы рекомендуем избегать магического метода `$this->onChange()`. Практичной заменой является функция [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); -``` diff --git a/utils/sl/smartobject.texy b/utils/sl/smartobject.texy index 1045e03c54..2aabb5175b 100644 --- a/utils/sl/smartobject.texy +++ b/utils/sl/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -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*. +SmartObject je leta izboljševal obnašanje objektov v PHP-ju. Od različice PHP 8.4 so vse njegove funkcije del samega PHP-ja, s čimer je zaključil svoje zgodovinsko poslanstvo biti pionir modernega objektno usmerjenega pristopa v PHP-ju. Namestitev: @@ -11,19 +11,64 @@ Namestitev: composer require nette/utils ``` +SmartObject je nastal leta 2007 kot revolucionarna rešitev pomanjkljivosti tedanjega objektnega modela PHP. V času, ko je PHP trpel zaradi številnih težav z objektno usmerjenim načrtovanjem, je prinesel pomembne izboljšave in poenostavil delo razvijalcem. Postal je legendarna komponenta ogrodja Nette. Ponujal je funkcionalnost, ki jo je PHP dobil šele mnogo let pozneje - od validacije dostopa do lastnosti objektov do naprednega ravnanja z napakami. S prihodom PHP 8.4 je zaključil svoje zgodovinsko poslanstvo, saj so vse njegove funkcije postale sestavni del jezika. Prehitel je razvoj PHP za izjemnih 17 let. -Lastnosti, nastavljalci in pridobitelji .[#toc-properties-getters-and-setters] -============================================================================== +SmartObject je doživel zanimiv tehnični razvoj. Prvotno je bil implementiran kot razred `Nette\Object`, od katerega so drugi razredi podedovali potrebno funkcionalnost. Pomembna sprememba je prišla s PHP 5.4, ki je uvedel podporo za trait-e. To je omogočilo preoblikovanje v trait `Nette\SmartObject`, kar je prineslo večjo prilagodljivost - razvijalci so lahko uporabljali funkcionalnost tudi v razredih, ki so že podedovali od drugega razreda. Medtem ko je prvotni razred `Nette\Object` prenehal obstajati s PHP 7.2 (ki je prepovedal poimenovanje razredov z besedo 'Object'), trait `Nette\SmartObject` živi naprej. -V sodobnih objektno usmerjenih jezikih (npr. C#, Python, Ruby, JavaScript) se izraz *lastnost* nanaša na [posebne člane razredov |https://en.wikipedia.org/wiki/Property_(programming)], 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. +Poglejmo si funkcije, ki sta jih nekoč ponujala `Nette\Object` in kasneje `Nette\SmartObject`. Vsaka od teh funkcij je v svojem času predstavljala pomemben korak naprej na področju objektno usmerjenega programiranja v PHP-ju. -Lastnosti PHP niso podprte, vendar jih lahko posnemamo z lastnostmi `Nette\SmartObject`. Kako jo uporabiti? -- V razred dodajte opombo v obliki `@property $xyz` -- Ustvarite getter z imenom `getXyz()` ali `isXyz()`, setter z imenom `setXyz()` -- Getter in setter morata biti *javna* ali *zaščitena* in sta neobvezna, zato je lahko lastnost *samo za branje* ali *samo za pisanje* +Konsistentna obravnava napak +---------------------------- +Ena najbolj perečih težav zgodnjega PHP-ja je bilo nedosledno obnašanje pri delu z objekti. `Nette\Object` je v ta kaos prinesel red in predvidljivost. Poglejmo si, kako se je PHP prvotno obnašal: -Lastnost za razred Circle bomo uporabili za zagotovitev, da se v spremenljivko `$radius` vnesejo samo nenegativna števila. Zamenjajte `public $radius` z lastnostjo: +```php +echo $obj->undeclared; // E_NOTICE, kasneje E_WARNING +$obj->undeclared = 1; // se izvede tiho brez opozorila +$obj->unknownMethod(); // Fatal error (ni mogoče ujeti s try/catch) +``` + +Fatal error je prekinil aplikacijo brez možnosti odziva. Tiho pisanje v neobstoječe člane brez opozorila je lahko vodilo do resnih napak, ki jih je bilo težko odkriti. `Nette\Object` je vse te primere prestregel in vrgel izjemo `MemberAccessException`, kar je programerjem omogočilo odzivanje na napake in njihovo reševanje: + +```php +echo $obj->undeclared; // vrže Nette\MemberAccessException +$obj->undeclared = 1; // vrže Nette\MemberAccessException +$obj->unknownMethod(); // vrže Nette\MemberAccessException +``` + +Od PHP 7.0 jezik ne povzroča več neulovljivih fatal error-jev, od PHP 8.2 pa se dostop do nedeklariranih članov šteje za napako. + + +Pomoč "Did you mean?" +--------------------- +`Nette\Object` je prišel z zelo priročno funkcijo: inteligentnimi predlogi pri napakah v črkovanju. Ko je razvijalec naredil napako v imenu metode ali spremenljivke, ni le sporočil napake, ampak je tudi ponudil pomoč v obliki predloga pravilnega imena. To ikonično sporočilo, znano kot "did you mean?", je programerjem prihranilo ure iskanja napak v črkovanju: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// vrže Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Današnji PHP sicer nima nobene oblike "did you mean?", vendar to funkcionalnost v sporočila o napakah dodaja [Tracy|tracy:]. In celo [samodejno popravlja|tracy:open-files-in-ide#demos] take napake. + + +Properties s kontroliranim dostopom +----------------------------------- +Pomembna inovacija, ki jo je SmartObject prinesel v PHP, so bile properties s kontroliranim dostopom. Ta koncept, običajen v jezikih kot sta C# in Python, je razvijalcem omogočil eleganten nadzor nad dostopom do podatkov objekta in zagotavljanje njihove konsistentnosti. Properties so močno orodje objektno usmerjenega programiranja. Delujejo kot spremenljivke, vendar so v resnici predstavljene z metodami (getterji in setterji). To omogoča validacijo vhodnih podatkov ali generiranje vrednosti šele ob branju. + +Za uporabo properties morate: +- Dodati razredu anotacijo v obliki `@property $xyz` +- Ustvariti getter z imenom `getXyz()` ali `isXyz()`, setter z imenom `setXyz()` +- Zagotoviti, da sta getter in setter *public* ali *protected*. Sta opcijska - lahko torej obstajata kot *read-only* ali *write-only* property + +Poglejmo si praktičen primer na razredu Circle, kjer bomo properties uporabili za zagotavljanje, da je polmer vedno nenegativno število. Nadomestili bomo prvotni `public $radius` s property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // ni javno + private float $radius = 0.0; // ni public! - // getter za lastnost $radius + // getter za property $radius protected function getRadius(): float { return $this->radius; } - // setter za lastnost $radius + // setter za property $radius protected function setRadius(float $radius): void { - // sanitizacija vrednosti pred shranjevanjem + // vrednost pred shranjevanjem preverimo $this->radius = max(0.0, $radius); } - // getter za lastnost $visible + // getter za property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $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"((sintactic sugar)), ki je namenjen temu, da programerju s poenostavitvijo kode osladi življenje. Če jih ne želite, vam jih ni treba uporabljati. - - -Pogled v zgodovino .[#toc-a-glimpse-into-history] -================================================= - -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 |#Properties, getters, and setters]. - -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 .[#toc-inconsistent-errors] ---------------------------------------------- -PHP se je nedosledno obnašal pri dostopu do nereklariranih članov. Stanje v času `Nette\Object` je bilo naslednje: - -```php -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`. - -```php -echo $obj->undeclared; // vrgel Izjemo Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException +$circle->radius = 10; // v resnici kliče setRadius(10) +echo $circle->radius; // kliče getRadius() +echo $circle->visible; // kliče isVisible() ``` -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? .[#toc-did-you-mean] -------------------------------------- -Č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?". +Od PHP 8.4 lahko isto funkcionalnost dosežemo s property hooks, ki ponujajo veliko bolj elegantno in jedrnato sintakso: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Čeprav današnji PHP nima funkcije "Ali ste mislili?", lahko [Tracy |tracy:] napakam doda ta stavek. [Takšne napake |tracy:open-files-in-ide#toc-demos] lahko celo [samodejno popravi |tracy:open-files-in-ide#toc-demos]. - -Metode razširitve .[#toc-extension-methods] -------------------------------------------- -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. +Extension methods +----------------- +`Nette\Object` je v PHP prinesel še en zanimiv koncept, navdihnjen s sodobnimi programskimi jeziki - extension methods. Ta funkcionalnost, prevzeta iz C#, je razvijalcem omogočila elegantno razširjanje obstoječih razredov z novimi metodami brez potrebe po spreminjanju ali dedovanju. Na primer, obrazcu ste lahko dodali metodo `addDateTime()`, ki doda lasten DateTimePicker: ```php Form::extensionMethod( @@ -131,11 +137,12 @@ $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. +Extension metode so se izkazale za nepraktične, ker njihovih imen urejevalniki niso predlagali, nasprotno, javljali so, da metoda ne obstaja. Zato je bila njihova podpora ukinjena. Danes je običajneje uporabljati kompozicijo ali dedovanje za razširitev funkcionalnosti razredov. -Pridobivanje imena razreda .[#toc-getting-the-class-name] ---------------------------------------------------------- +Pridobivanje imena razreda +-------------------------- +Za pridobivanje imena razreda je SmartObject ponujal preprosto metodo: ```php $class = $obj->getClass(); // z uporabo Nette\Object @@ -143,10 +150,9 @@ $class = $obj::class; // od PHP 8.0 ``` -Dostop do razmisleka in opomb .[#toc-access-to-reflection-and-annotations] --------------------------------------------------------------------------- - -`Nette\Object` ponujen dostop do refleksije in anotacij z metodama `getReflection()` in `getAnnotation()`: +Dostop do refleksije in anotacij +-------------------------------- +`Nette\Object` je ponujal dostop do refleksije in anotacij preko metod `getReflection()` in `getAnnotation()`. Ta pristop je pomembno poenostavil delo z metainformacijami razredov: ```php /** @@ -161,7 +167,7 @@ $reflection = $obj->getReflection(); $reflection->getAnnotation('author'); // vrne 'John Doe' ``` -Od različice PHP 8.0 je mogoče dostopati do metainformacij v obliki atributov: +Od PHP 8.0 je mogoče dostopati do metainformacij v obliki atributov, ki ponujajo še več možnosti in boljšo tipsko preverjanje: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Metode, ki pridobivajo .[#toc-method-getters] ---------------------------------------------- - -`Nette\Object` je ponujal eleganten način za ravnanje z metodami, kot da bi bile spremenljivke: +Method getterji +--------------- +`Nette\Object` je ponujal eleganten način za predajanje metod, kot da bi šlo za spremenljivke: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $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/manual/en/functions.first_class_callable_syntax +Od PHP 8.1 je mogoče uporabiti t.i. "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, ki ta koncept pelje še dlje: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Dogodki .[#toc-events] ----------------------- - -`Nette\Object` ponujen sintaktični sladkor za sprožitev [dogodka |nette:glossary#events]: +Dogodki +------- +SmartObject ponuja poenostavljeno sintakso za delo z [dogodki|nette:glossary#events]. Dogodki omogočajo objektom, da obvestijo druge dele aplikacije o spremembah svojega stanja: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Koda `$this->onChange($this, $radius)` je enakovredna naslednjemu: +Koda `$this->onChange($this, $radius)` je enakovredna naslednji zanki: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Zaradi jasnosti priporočamo, da se izognete čarobni metodi `$this->onChange()`. Praktično nadomestilo je funkcija [Nette\Utils\Arrays::invoke |arrays#invoke]: +Zaradi jasnosti priporočamo, da se izognete magični metodi `$this->onChange()`. Praktična zamenjava je funkcija [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/tr/smartobject.texy b/utils/tr/smartobject.texy index b08dcbca57..fc84c3b9b9 100644 --- a/utils/tr/smartobject.texy +++ b/utils/tr/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -SmartObject nesnelerin davranışını birçok yönden düzeltirdi, ancak günümüz PHP'si bu iyileştirmelerin çoğunu zaten yerel olarak içermektedir. Bununla birlikte, hala *property* için destek ekler. +SmartObject, yıllarca PHP'deki nesne davranışını geliştirdi. PHP 8.4 sürümünden itibaren tüm özellikleri PHP'nin doğal bir parçası haline geldi ve böylece modern nesne yönelimli yaklaşımın öncüsü olma tarihi misyonunu tamamladı. Kurulum: @@ -11,19 +11,64 @@ Kurulum: composer require nette/utils ``` +SmartObject, 2007 yılında PHP'nin o zamanki nesne modelinin eksikliklerine devrimci bir çözüm olarak ortaya çıktı. PHP'nin nesne yönelimli tasarımda birçok sorunla karşı karşıya olduğu bir dönemde, geliştiriciler için önemli iyileştirmeler ve basitleştirmeler getirdi. Nette framework'ünün efsanevi bir parçası haline geldi. PHP'nin yıllar sonra kazanacağı işlevselliği sundu - nesne özelliklerine erişim doğrulamasından sofistike hata yönetimine kadar. PHP 8.4'ün gelişiyle, tüm özellikleri dilin doğal bir parçası haline geldiğinde tarihi misyonunu tamamladı. PHP'nin gelişimini dikkat çekici bir şekilde 17 yıl önceden gösterdi. -Özellikler, Getiriciler ve Ayarlayıcılar .[#toc-properties-getters-and-setters] -=============================================================================== +SmartObject ilginç bir teknik evrim geçirdi. Başlangıçta, diğer sınıfların gerekli işlevselliği miras aldığı `Nette\Object` sınıfı olarak uygulandı. PHP 5.4 ile trait desteğinin gelmesiyle önemli bir değişiklik yaşandı. Bu, `Nette\SmartObject` trait'ine dönüşümü sağladı ve daha fazla esneklik getirdi - geliştiriciler işlevselliği başka bir sınıftan miras alan sınıflarda bile kullanabilir hale geldi. Orijinal `Nette\Object` sınıfı PHP 7.2 ile (sınıfların 'Object' kelimesiyle adlandırılmasını yasaklayan) sona ererken, `Nette\SmartObject` trait'i yaşamaya devam ediyor. -Modern nesne yönelimli dillerde (örneğin C#, Python, Ruby, JavaScript) *property* terimi, [sınıfların |https://en.wikipedia.org/wiki/Property_(programming)] değişken gibi görünen ancak aslında yöntemlerle temsil edilen [özel üyelerini |https://en.wikipedia.org/wiki/Property_(programming)] ifade eder. Bu "değişkenin" değeri atandığında veya okunduğunda, ilgili yöntem (getter veya setter olarak adlandırılır) çağrılır. Bu çok kullanışlı bir şeydir, bize değişkenlere erişim üzerinde tam kontrol sağlar. Girdiyi doğrulayabilir veya yalnızca özellik okunduğunda sonuç üretebiliriz. +`Nette\Object` ve daha sonra `Nette\SmartObject`'in sunduğu özellikleri inceleyelim. Bu işlevlerin her biri, zamanında PHP nesne yönelimli programlamada önemli bir adımı temsil ediyordu. -PHP özellikleri desteklenmez, ancak trait `Nette\SmartObject` bunları taklit edebilir. Nasıl kullanılır? -- Sınıfa şu biçimde bir ek açıklama ekleyin `@property $xyz` -- `getXyz()` veya `isXyz()` adında bir getter, adında bir setter oluşturun. `setXyz()` -- Getter ve setter *public* veya *protected* olmalıdır ve isteğe bağlıdır, bu nedenle *read-only* veya *write-only* özelliği olabilir +Tutarlı Hata Durumları +---------------------- +Erken dönem PHP'nin en önemli sorunlarından biri, nesnelerle çalışırken tutarsız davranışlardı. `Nette\Object` bu kargaşaya düzen ve öngörülebilirlik getirdi. PHP'nin orijinal davranışına bakalım: -`$radius` değişkenine yalnızca negatif olmayan sayıların girilmesini sağlamak için Circle sınıfının özelliğini kullanacağız. `public $radius` öğesini özellik ile değiştirin: +```php +echo $obj->undeclared; // E_NOTICE, daha sonra E_WARNING +$obj->undeclared = 1; // sessizce uyarısız geçer +$obj->unknownMethod(); // Fatal error (try/catch ile yakalanamaz) +``` + +Fatal error uygulamayı herhangi bir tepki verme olasılığı olmadan sonlandırırdı. Var olmayan üyelere sessizce yazma, tespit edilmesi zor ciddi hatalara yol açabilirdi. `Nette\Object` tüm bu durumları yakalayıp bir `MemberAccessException` fırlatırdı, bu da programcıların hatalara tepki vermesini ve onları ele almasını sağlardı: + +```php +echo $obj->undeclared; // Nette\MemberAccessException fırlatır +$obj->undeclared = 1; // Nette\MemberAccessException fırlatır +$obj->unknownMethod(); // Nette\MemberAccessException fırlatır +``` + +PHP 7.0'dan beri dil artık yakalanamayan fatal error'lar oluşturmuyor ve PHP 8.2'den beri tanımlanmamış üyelere erişim hata olarak kabul ediliyor. + + +"Did you mean?" Yardımcısı +-------------------------- +`Nette\Object` çok kullanışlı bir özellikle geldi: yazım hatalarında akıllı öneriler. Bir geliştirici bir metot veya değişken adında hata yaptığında, sadece hatayı bildirmekle kalmaz, aynı zamanda doğru adı önererek yardım ederdi. "Did you mean?" olarak bilinen bu ikonik mesaj, programcıların yazım hatalarını aramak için harcadıkları saatleri kurtardı: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// Nette\MemberAccessException fırlatır +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Günümüzde PHP'nin kendisinde "did you mean?" özelliği yok, ancak bu özellik [Tracy|tracy:] tarafından sağlanıyor. Hatta bu tür hataları [otomatik düzeltebiliyor|tracy:open-files-in-ide#demos]. + + +Kontrollü Erişimli Properties +----------------------------- +SmartObject'in PHP'ye getirdiği önemli yeniliklerden biri kontrollü erişimli property'lerdi. C# veya Python gibi dillerde yaygın olan bu konsept, geliştiricilerin nesne verilerine erişimi zarif bir şekilde kontrol etmelerini ve tutarlılıklarını sağlamalarını sağladı. Property'ler nesne yönelimli programlamanın güçlü bir aracıdır. Değişkenler gibi çalışırlar ancak aslında metodlarla (getter ve setter) temsil edilirler. Bu, girişlerin doğrulanmasını veya okuma anında değerlerin oluşturulmasını sağlar. + +Property'leri kullanmak için şunları yapmalısınız: +- Sınıfa `@property $xyz` şeklinde bir annotation ekleyin +- `getXyz()` veya `isXyz()` adında bir getter, `setXyz()` adında bir setter oluşturun +- Getter ve setter'ın *public* veya *protected* olmasını sağlayın. Bunlar isteğe bağlıdır - yani *read-only* veya *write-only* property'ler olabilir + +Circle sınıfında yarıçapın her zaman negatif olmayan bir sayı olmasını sağlamak için property'leri kullanan pratik bir örneğe bakalım. `public $radius` yerine bir property kullanalım: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // herkese açık değil + private float $radius = 0.0; // public değil! - //radius özelliği için getter + // $radius property'si için getter protected function getRadius(): float { return $this->radius; } - //radius özelliği için setter + // $radius property'si için setter protected function setRadius(float $radius): void { - // kaydetmeden önce değeri sanitize etme + // kaydetmeden önce değeri temizle $this->radius = max(0.0, $radius); } - // özellik için getter $visible + // $visible property'si için getter protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // aslında setRadius(10) çağırır -echo $circle->radius; // getRadius() işlevini çağırır -echo $circle->visible; // isVisible() işlevini çağırır -``` - -Özellikler öncelikle "sözdizimsel şeker"((syntactic sugar)) olup, kodu basitleştirerek programcının hayatını daha tatlı hale getirmeyi amaçlar. Eğer onları istemiyorsanız, kullanmak zorunda değilsiniz. - - -Tarihe Bir Bakış .[#toc-a-glimpse-into-history] -=============================================== - -SmartObject, nesnelerin davranışlarını çeşitli şekillerde iyileştirmek için kullanılırdı, ancak bugünün PHP'si bu geliştirmelerin çoğunu zaten yerel olarak içeriyor. Aşağıdaki metin geçmişe nostaljik bir bakıştır ve bize işlerin nasıl geliştiğini hatırlatır. - -PHP'nin nesne modeli, başlangıcından itibaren sayısız ciddi eksiklik ve yetersizlikten muzdaripti. Bu durum, bu sorunları gidermeyi ve PHP'nin kullanım rahatlığını artırmayı amaçlayan `Nette\Object` sınıfının (2007'de) oluşturulmasına yol açtı. İhtiyaç duyulan tek şey diğer sınıfların bu sınıftan miras almasıydı ve onlar da bu sınıfın sunduğu avantajlardan yararlanabileceklerdi. PHP 5.4 özellikler için destek sunduğunda, `Nette\Object` sınıfının yerini `Nette\SmartObject` özelliği aldı. Bu, ortak bir atadan miras alma ihtiyacını ortadan kaldırdı. Dahası, özellik zaten başka bir sınıftan miras alan sınıflarda kullanılabiliyordu. `Nette\Object` 'un kesin sonu, sınıfların `Object` olarak adlandırılmasını yasaklayan PHP 7.2'nin yayınlanmasıyla geldi. - -PHP'nin gelişimi devam ettikçe, nesne modeli ve dil yetenekleri de gelişti. `SmartObject` sınıfının çeşitli işlevleri gereksiz hale geldi. PHP 8.2'nin yayınlanmasından bu yana, PHP'de doğrudan desteklenmeyen tek bir özellik kaldı: sözde [özellikleri |#Properties, getters, and setters] kullanma yeteneği. - - `Nette\Object` ve buna bağlı olarak `Nette\SmartObject` hangi özellikleri sunuyordu? İşte genel bir bakış. (Örneklerde `Nette\Object` sınıfı kullanılmıştır, ancak çoğu özellik `Nette\SmartObject` özelliği için de geçerlidir). - - -Tutarsız Hatalar .[#toc-inconsistent-errors] --------------------------------------------- -PHP, bildirilmemiş üyelere erişirken tutarsız davranışlar sergiliyordu. `Nette\Object` adresinin durumu aşağıdaki gibiydi: - -```php -echo $obj->undeclared; // E_NOTICE, daha sonra E_WARNING -$obj->undeclared = 1; // raporlama yapmadan sessizce geçer -$obj->unknownMethod(); // Ölümcül hata (try/catch ile yakalanamaz) +$circle->radius = 10; // aslında setRadius(10)'u çağırır +echo $circle->radius; // getRadius()'u çağırır +echo $circle->visible; // isVisible()'ı çağırır ``` -Ölümcül hata, herhangi bir tepki verme imkanı olmadan uygulamayı sonlandırdı. Var olmayan üyelere uyarı vermeden sessizce yazmak, tespit edilmesi zor ciddi hatalara yol açabilirdi. `Nette\Object` Tüm bu durumlar yakalandı ve bir istisna `MemberAccessException` atıldı. - -```php -echo $obj->undeclared; // throw Nette\MemberAccessException -$obj->undeclared = 1; // throw Nette\MemberAccessException -$obj->unknownMethod(); // throw Nette\MemberAccessException -``` -PHP 7.0'dan beri PHP artık yakalanamayan ölümcül hatalara neden olmamaktadır ve bildirilmemiş üyelere erişim PHP 8.2'den beri bir hatadır. - - -Ne demek istiyorsun? .[#toc-did-you-mean] ------------------------------------------ -Bir nesne değişkenine erişirken veya bir yöntemi çağırırken yapılan yazım hatası nedeniyle `Nette\MemberAccessException` hatası atıldığında, `Nette\Object` hata mesajında hatanın nasıl düzeltileceğine dair ikonik "demek istediniz mi?" eki şeklinde bir ipucu vermeye çalışmıştır. +PHP 8.4'ten beri, aynı işlevselliğe property hooks kullanılarak ulaşılabilir, bu da çok daha zarif ve özlü bir sözdizimi sunar: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Günümüz PHP'sinde "demek istediniz mi?" özelliği olmasa da, bu ifade [Tracy |tracy:] tarafından hatalara eklenebilir. Hatta bu [tür hataları otomatik olarak düzeltebilir |tracy:open-files-in-ide#toc-demos]. - -Uzatma yöntemleri .[#toc-extension-methods] -------------------------------------------- -C#'ın uzantı yöntemlerinden esinlenilmiştir. Mevcut sınıflara yeni yöntemler ekleme imkanı verdiler. Örneğin, kendi DateTimePicker'ınızı eklemek için bir forma `addDateTime()` yöntemini ekleyebilirsiniz. +Extension Methods +----------------- +`Nette\Object`, modern programlama dillerinden esinlenen başka bir ilginç konsepti PHP'ye getirdi - extension methods. C#'tan alınan bu özellik, geliştiricilerin var olan sınıfları değiştirmeden veya onlardan miras almadan yeni metodlarla genişletmelerini sağladı. Örneğin, bir forma özel bir DateTimePicker ekleyen bir `addDateTime()` metodu ekleyebilirdiniz: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Uzatma yöntemlerinin pratik olmadığı kanıtlandı çünkü isimleri editörler tarafından otomatik olarak tamamlanmadı, bunun yerine yöntemin mevcut olmadığını bildirdiler. Bu nedenle destekleri kesilmiştir. +Extension metodları, editörlerin isimlerini önermemesi ve bunun yerine metodun var olmadığını bildirmesi nedeniyle pratik olmadığını kanıtladı. Bu nedenle destekleri sonlandırıldı. Günümüzde sınıf işlevselliğini genişletmek için composition veya inheritance kullanmak daha yaygın. -Sınıf Adını Alma .[#toc-getting-the-class-name] ------------------------------------------------ +Sınıf Adını Alma +---------------- +SmartObject sınıf adını almak için basit bir metod sunuyordu: ```php -$class = $obj->getClass(); // using Nette\Object -$class = $obj::class; // PHP 8.0'dan beri +$class = $obj->getClass(); // Nette\Object kullanarak +$class = $obj::class; // PHP 8.0'dan beri ``` -Yansıma ve Ek Açıklamalara Erişim .[#toc-access-to-reflection-and-annotations] ------------------------------------------------------------------------------- - -`Nette\Object` `getReflection()` ve `getAnnotation()` yöntemlerini kullanarak yansıtma ve ek açıklamaya erişim sundu: +Reflection ve Annotation Erişimi +-------------------------------- +`Nette\Object`, `getReflection()` ve `getAnnotation()` metodları aracılığıyla reflection ve annotation'lara erişim sağlıyordu. Bu yaklaşım sınıf meta-bilgileriyle çalışmayı önemli ölçüde basitleştirdi: ```php /** @@ -161,7 +167,7 @@ $reflection = $obj->getReflection(); $reflection->getAnnotation('author'); // 'John Doe' döndürür ``` -PHP 8.0'dan itibaren meta-bilgilere öznitelikler şeklinde erişmek mümkündür: +PHP 8.0'dan beri, meta-bilgilere özellikler (attributes) aracılığıyla erişmek mümkün, bu da daha fazla olanak ve daha iyi tip kontrolü sunuyor: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Yöntem Getiriciler .[#toc-method-getters] ------------------------------------------ - -`Nette\Object` metotları değişkenlermiş gibi ele almak için zarif bir yol sundu: +Method Getters +-------------- +`Nette\Object` metodları değişkenlermiş gibi aktarmanın zarif bir yolunu sunuyordu: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -PHP 8.1'den itibaren, "birinci sınıf çağrılabilir sözdizimi":https://www.php.net/manual/en/functions.first_class_callable_syntax olarak adlandırılan sözdizimini:https://www.php.net/manual/en/functions.first_class_callable_syntax kullanabilirsiniz: +PHP 8.1'den beri "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax kullanabilirsiniz, bu da konsepti daha da ileri taşıyor: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Etkinlikler .[#toc-events] --------------------------- - -`Nette\Object` [olayı |nette:glossary#events] tetiklemek için sözdizimsel şeker sundu: +Olaylar (Events) +---------------- +SmartObject [olaylarla|nette:glossary#events] çalışmak için basitleştirilmiş bir sözdizimi sunar. Olaylar, nesnelerin durumlarındaki değişiklikler hakkında uygulamanın diğer kısımlarını bilgilendirmesini sağlar: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -`$this->onChange($this, $radius)` kodu aşağıdakine eşdeğerdir: +`$this->onChange($this, $radius)` kodu aşağıdaki döngüye eşdeğerdir: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Netlik açısından sihirli yöntemden kaçınmanızı öneririz `$this->onChange()`. Pratik bir ikame [Nette\Utils\Arrays::invoke |arrays#invoke] fonksiyonudur: +Netlik için `$this->onChange()` sihirli metodundan kaçınmanızı öneririz. Pratik bir alternatif [Nette\Utils\Arrays::invoke|arrays#invoke] fonksiyonudur: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius); diff --git a/utils/uk/smartobject.texy b/utils/uk/smartobject.texy index 446738b83e..8257edc244 100644 --- a/utils/uk/smartobject.texy +++ b/utils/uk/smartobject.texy @@ -2,7 +2,7 @@ SmartObject *********** .[perex] -Раніше SmartObject виправляв поведінку об'єктів багатьма способами, але сьогоднішній PHP вже містить більшість з цих покращень вбудовано. Проте, він все ще додає підтримку *property*. +SmartObject протягом багатьох років покращував поведінку об'єктів у PHP. Починаючи з версії PHP 8.4, усі його функції стали вбудованими частинами самого PHP, чим завершив свою історичну місію бути піонером сучасного об'єктно-орієнтованого підходу в PHP. Встановлення: @@ -11,19 +11,64 @@ SmartObject composer require nette/utils ``` +SmartObject з'явився у 2007 році як революційне рішення недоліків тодішньої об'єктної моделі PHP. У час, коли PHP страждав від численних проблем з об'єктно-орієнтованим дизайном, він приніс значні покращення та спростив роботу розробників. Він став легендарною частиною фреймворку Nette. Він пропонував функціональність, яку PHP отримав лише через багато років - від валідації доступу до властивостей об'єктів до складної обробки помилок. З появою PHP 8.4 він завершив свою історичну місію, оскільки всі його функції стали вбудованими частинами мови. Він випередив розвиток PHP на вражаючих 17 років. -Властивості, геттери та сеттери .[#toc-properties-getters-and-setters] -====================================================================== +SmartObject пройшов цікавий технічний розвиток. Спочатку він був реалізований як клас `Nette\Object`, від якого інші класи успадковували потрібну функціональність. Значна зміна прийшла з PHP 5.4, який представив підтримку trait. Це дозволило трансформацію у trait `Nette\SmartObject`, що принесло більшу гнучкість - розробники могли використовувати функціональність навіть у класах, які вже успадковувалися від іншого класу. У той час як оригінальний клас `Nette\Object` припинив існування з появою PHP 7.2 (який заборонив називати класи словом 'Object'), trait `Nette\SmartObject` продовжує жити. -У сучасних об'єктно-орієнтованих мовах (наприклад, C#, Python, Ruby, JavaScript) термін *властивість* відноситься до [спеціальних членів класів |https://en.wikipedia.org/wiki/Property_(programming)], що виглядають як змінні, але насправді представлені методами. Коли значення такої "змінної" присвоюється або зчитується, викликається відповідний метод (званий getter або setter). Це дуже зручна річ, вона дає нам повний контроль над доступом до змінних. Ми можемо перевіряти дані, що вводяться, або генерувати результати тільки тоді, коли властивість прочитано. +Розглянемо функції, які колись пропонували `Nette\Object` і пізніше `Nette\SmartObject`. Кожна з цих функцій у свій час представляла значний крок вперед у області об'єктно-орієнтованого програмування в PHP. -Властивості PHP не підтримуються, але trait `Nette\SmartObject` може їх імітувати. Як це використовувати? -- Додайте анотацію до класу у вигляді `@property $xyz` -- Створіть геттер з іменем `getXyz()` або `isXyz()`, сеттер з іменем `setXyz()` -- Геттер і сеттер мають бути *публічними* або *захищеними* і необов'язковими, тому може бути властивість *тільки для читання* або *тільки для запису*. +Послідовна обробка помилок +-------------------------- +Однією з найгостріших проблем раннього PHP була непослідовна поведінка при роботі з об'єктами. `Nette\Object` вніс порядок і передбачуваність у цей хаос. Подивімося, як поводилось PHP спочатку: -Ми будемо використовувати властивість для класу Circle, щоб гарантувати, що в змінну `$radius` будуть поміщатися тільки невід'ємні числа. Замініть `public $radius` на property: +```php +echo $obj->undeclared; // E_NOTICE, пізніше E_WARNING +$obj->undeclared = 1; // проходить тихо без попередження +$obj->unknownMethod(); // Fatal error (неможливо перехопити за допомогою try/catch) +``` + +Fatal error завершував роботу програми без можливості якось відреагувати. Тихий запис у неіснуючі члени без попередження міг призвести до серйозних помилок, які було важко виявити. `Nette\Object` перехоплював усі ці випадки і викидав виняток `MemberAccessException`, що дозволяло програмістам реагувати на помилки та обробляти їх: + +```php +echo $obj->undeclared; // викидає Nette\MemberAccessException +$obj->undeclared = 1; // викидає Nette\MemberAccessException +$obj->unknownMethod(); // викидає Nette\MemberAccessException +``` + +Починаючи з PHP 7.0, мова більше не викликає неперехоплювані fatal error, а з PHP 8.2 доступ до неоголошених членів вважається помилкою. + + +Підказка "Did you mean?" +------------------------ +`Nette\Object` з'явився з дуже зручною функцією: інтелектуальними підказками при опечатках. Коли розробник робив помилку в назві методу чи змінної, він не лише повідомляв про помилку, але й пропонував допомогу у вигляді підказки правильної назви. Це іконічне повідомлення, відоме як "did you mean?", зекономило програмістам години пошуку опечаток: + +```php +class Foo extends Nette\Object +{ + public static function from($var) + { + } +} + +$foo = Foo::form($var); +// викидає Nette\MemberAccessException +// "Call to undefined static method Foo::form(), did you mean from()?" +``` + +Сучасний PHP хоч і не має жодної форми "did you mean?", але цю функцію тепер забезпечує [Tracy|tracy:]. Він навіть може [автоматично виправляти|tracy:open-files-in-ide#demos] такі помилки. + + +Properties з контрольованим доступом +------------------------------------ +Значною інновацією, яку SmartObject приніс у PHP, були properties з контрольованим доступом. Ця концепція, поширена в таких мовах як C# чи Python, дозволила розробникам елегантно контролювати доступ до даних об'єкта та забезпечувати їх консистентність. Properties є потужним інструментом об'єктно-орієнтованого програмування. Вони функціонують як змінні, але насправді представлені методами (getters і setters). Це дозволяє валідувати вхідні дані або генерувати значення в момент читання. + +Для використання properties потрібно: +- Додати анотацію класу у форматі `@property $xyz` +- Створити getter з назвою `getXyz()` або `isXyz()`, setter з назвою `setXyz()` +- Забезпечити, щоб getter і setter були *public* або *protected*. Вони опціональні - тобто можуть існувати як *read-only* або *write-only* properties + +Розглянемо практичний приклад на класі Circle, де properties використовуються для забезпечення того, що радіус завжди буде невід'ємним числом. Замінимо `public $radius` на property: ```php /** @@ -34,22 +79,22 @@ class Circle { use Nette\SmartObject; - private float $radius = 0.0; // not public + private float $radius = 0.0; // не public! - // геттер для властивості $radius + // getter для property $radius protected function getRadius(): float { return $this->radius; } - // задатчик для властивості $radius + // setter для property $radius protected function setRadius(float $radius): void { - // очищення значення перед збереженням + // санітизуємо значення перед збереженням $this->radius = max(0.0, $radius); } - // геттер для властивості $visible + // getter для property $visible protected function isVisible(): bool { return $this->radius > 0; @@ -57,69 +102,30 @@ class Circle } $circle = new Circle; -$circle->radius = 10; // власне викликає setRadius(10) -echo $circle->radius; // викликаємо getRadius() -echo $circle->visible; // викликаємо isVisible() -``` - -Властивості - це насамперед "синтаксичний цукор"((syntactic sugar)), який покликаний зробити життя програміста солодшим за рахунок спрощення коду. Якщо вони вам не потрібні, ви не зобов'язані їх використовувати. - - -Погляд в історію .[#toc-a-glimpse-into-history] -=============================================== - -SmartObject використовувався для покращення поведінки об'єктів багатьма способами, але сьогоднішній PHP вже включає в себе більшість з цих покращень нативно. Наступний текст - це ностальгічний погляд в історію, що нагадує нам про те, як все розвивалося. - -Від самого початку об'єктна модель PHP страждала від безлічі серйозних недоліків і недоробок. Це призвело до створення класу `Nette\Object` (у 2007 році), який мав на меті виправити ці проблеми та підвищити комфорт використання PHP. Все, що було потрібно - це щоб інші класи успадковували його, і вони отримували переваги, які він пропонував. Коли в PHP 5.4 було введено підтримку трейтів, клас `Nette\Object` було замінено на трейт `Nette\SmartObject`. Це усунуло необхідність успадковувати від спільного предка. Крім того, ознаку можна було використовувати в класах, які вже були успадковані від іншого класу. Остаточний кінець `Nette\Object` настав з виходом PHP 7.2, який заборонив класам називатися `Object`. - -З розвитком PHP його об'єктна модель і можливості мови покращувалися. Різні функції класу `SmartObject` стали надлишковими. З виходом PHP 8.2 залишилася лише одна функція, яка не підтримується безпосередньо в PHP: можливість використання так званих [властивостей |#Properties, getters, and setters]. - -Які ж можливості пропонували `Nette\Object` і, відповідно, `Nette\SmartObject`? Ось короткий огляд. (У прикладах використовується клас `Nette\Object`, але більшість можливостей також застосовні до властивості `Nette\SmartObject` ). - - -Непослідовні помилки .[#toc-inconsistent-errors] ------------------------------------------------- -PHP мав непослідовну поведінку при зверненні до неоголошених членів. Стан на момент публікації `Nette\Object` був таким: - -```php -echo $obj->undeclared; // E_NOTICE, пізніше E_WARNING -$obj->undeclared = 1; // проходить мовчки без повідомлення -$obj->unknownMethod(); // Фатальна помилка (не перехоплюється try/catch) -``` - -Фатальна помилка завершувала додаток без будь-якої можливості відреагувати. Тихий запис у неіснуючі члени без попередження міг призвести до серйозних помилок, які було важко виявити. `Nette\Object` Всі ці випадки були спіймані, і було викинуто виняток `MemberAccessException`. - -```php -echo $obj->undeclared; // згенерувати виключення Nette\MemberAccessException -$obj->undeclared = 1; // згенерувати виключення Nette\MemberAccessException -$obj->unknownMethod(); // згенерувати виключення Nette\MemberAccessException +$circle->radius = 10; // насправді викликає setRadius(10) +echo $circle->radius; // викликає getRadius() +echo $circle->visible; // викликає isVisible() ``` -Починаючи з PHP 7.0, PHP більше не спричиняє неперехоплюваних фатальних помилок, а доступ до неоголошених членів став помилкою, починаючи з PHP 8.2. - -Ви мали на увазі? .[#toc-did-you-mean] --------------------------------------- -Якщо виникала помилка `Nette\MemberAccessException`, можливо, через друкарську помилку під час звернення до об'єктної змінної або виклику методу, `Nette\Object` намагався дати підказку в повідомленні про помилку, як виправити помилку, у вигляді знакового доповнення "Ви мали на увазі?". +Починаючи з PHP 8.4, тієї ж функціональності можна досягти за допомогою property hooks, які пропонують набагато елегантніший і коротший синтаксис: ```php -class Foo extends Nette\Object +class Circle { - public static function from($var) - { + public float $radius = 0.0 { + set => max(0.0, $value); } -} -$foo = Foo::form($var); -// throw Nette\MemberAccessException -// "Call to undefined static method Foo::form(), did you mean from()?" + public bool $visible { + get => $this->radius > 0; + } +} ``` -Хоча сучасний PHP не має функції "ви мали на увазі?", цю фразу можна додати до помилок за допомогою [Трейсі |tracy:]. Він навіть може [автоматично виправляти такі помилки |tracy:open-files-in-ide#toc-demos]. - -Методи розширення .[#toc-extension-methods] -------------------------------------------- -Натхненний методами розширення з C#. Вони дають можливість додавати нові методи до наявних класів. Наприклад, ви можете додати метод `addDateTime()` до форми, щоб додати свій власний DateTimePicker. +Extension methods +----------------- +`Nette\Object` приніс у PHP ще одну цікаву концепцію, натхненну сучасними мовами програмування - extension methods. Ця функція, запозичена з C#, дозволила розробникам елегантно розширювати існуючі класи новими методами без необхідності їх модифікації чи успадкування. Наприклад, ви могли додати до форми метод `addDateTime()`, який додає власний DateTimePicker: ```php Form::extensionMethod( @@ -131,22 +137,22 @@ $form = new Form; $form->addDateTime('date'); ``` -Методи розширення виявилися непрактичними, оскільки їхні імена не автозаповнювалися редакторами, натомість вони повідомляли, що метод не існує. Тому їхню підтримку було припинено. +Extension methods виявилися непрактичними, оскільки редактори не підказували їх назви, навпаки, повідомляли, що метод не існує. Тому їх підтримку було припинено. Сьогодні більш поширеним є використання композиції або успадкування для розширення функціональності класів. -Отримання імені класу .[#toc-getting-the-class-name] ----------------------------------------------------- +Отримання назви класу +--------------------- +Для отримання назви класу SmartObject пропонував простий метод: ```php -$class = $obj->getClass(); // використання Nette\Object -$class = $obj::class; // починаючи з PHP 8.0 +$class = $obj->getClass(); // використовуючи Nette\Object +$class = $obj::class; // починаючи з PHP 8.0 ``` -Доступ до роздумів та анотацій .[#toc-access-to-reflection-and-annotations] ---------------------------------------------------------------------------- - -`Nette\Object` запропоновано доступ до роздумів і анотацій за допомогою методів `getReflection()` і `getAnnotation()`: +Доступ до рефлексії та анотацій +------------------------------- +`Nette\Object` надавав доступ до рефлексії та анотацій через методи `getReflection()` та `getAnnotation()`. Цей підхід значно спростив роботу з метаінформацією класів: ```php /** @@ -161,7 +167,7 @@ $reflection = $obj->getReflection(); $reflection->getAnnotation('author'); // повертає 'John Doe' ``` -Починаючи з PHP 8.0, з'явилася можливість доступу до мета-інформації у вигляді атрибутів: +Починаючи з PHP 8.0, можна отримувати доступ до метаінформації через атрибути, які пропонують ще більше можливостей та кращу перевірку типів: ```php #[Author('John Doe')] @@ -175,10 +181,9 @@ $reflection->getAttributes(Author::class)[0]; ``` -Геттери методів .[#toc-method-getters] --------------------------------------- - -`Nette\Object` пропонують елегантний спосіб роботи з методами, наче вони є змінними: +Method getters +-------------- +`Nette\Object` пропонував елегантний спосіб передачі методів, ніби вони були змінними: ```php class Foo extends Nette\Object @@ -194,7 +199,7 @@ $method = $obj->adder; echo $method(2, 3); // 5 ``` -Починаючи з PHP 8.1, ви можете використовувати так званий "першокласний синтаксис методів, що викликаються":https://www.php.net/manual/en/functions.first_class_callable_syntax: +Починаючи з PHP 8.1, можна використовувати так званий "first-class callable syntax":https://www.php.net/manual/en/functions.first_class_callable_syntax, який розвиває цю концепцію ще далі: ```php $obj = new Foo; @@ -203,10 +208,9 @@ echo $method(2, 3); // 5 ``` -Події .[#toc-events] --------------------- - -`Nette\Object` пропонує синтаксичний цукор для запуску [події |nette:glossary#events]: +Події +----- +SmartObject пропонує спрощений синтаксис для роботи з [подіями|nette:glossary#events]. Події дозволяють об'єктам інформувати інші частини програми про зміни свого стану: ```php class Circle extends Nette\Object @@ -221,7 +225,7 @@ class Circle extends Nette\Object } ``` -Код `$this->onChange($this, $radius)` еквівалентний наступному: +Код `$this->onChange($this, $radius)` еквівалентний наступному циклу: ```php foreach ($this->onChange as $callback) { @@ -229,7 +233,7 @@ foreach ($this->onChange as $callback) { } ``` -Для ясності ми рекомендуємо уникати магічного методу `$this->onChange()`. Хорошою заміною буде [Nette\Utils\Arrays::invoke |arrays#invoke]: +Для кращої зрозумілості рекомендуємо уникати магічного методу `$this->onChange()`. Практичною заміною є функція [Nette\Utils\Arrays::invoke|arrays#invoke]: ```php Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);