Deklarace a kontrola striktních anotací
- paranoiq
- Člen | 392
antimotto:
Marcus Boerger: „Basically there is no need for annotations in PHP“
toto je pokračování debaty o anotacích: https://forum.nette.org/…yt-prekazkou
okolo anotací je třeba vyřešit několik věcí:
- kontrola jména a parametrů anotace
- kontrola působnosti anotace
- dědičné anotace
- API
koukal jsem jak se to řeší jinde:
Java 5: anotace je definována podobně jako interface.
neobsahuje vlastní validátory, ale u prvků jsou uvedeny očekávané typy
dat (a tudíž třeba i výčet) a volitelně i výchozí hodnoty prvků
anotací. z dalších vlastností je zajímavé omezení působnosti anotace
(@Target) a dědičnost anotací (@Inherited). dědičnost je
jednoduchá. při nenalezení anotace dané třídy se prohledávají
předci – vždy jde tedy o překrytí hodnoty předka
(krásně je vše popsáno třeba tady: http://knihy.pecinovsky.cz/java5novinky/)
Addendum: (http://code.google.com/p/addendum/)
od prvotní verze prošlo velkou změnou. krom jmen řeší i kontrolu
působnosti anotací. parser umí vnořené anotace (anotace je hodnotou jiné
anotace), třídní konstanty v anotacích, vnořená pole pomocí syntaxe
{}
. stále neumí typ NULL a dokumentační anotace (a zřejmě se
jim vyhýbá záměrně:
testAnnotationMatcherShouldNotMatchAnnotationWithSmallStartingLetter()
).
podivnou vlastností je vyhledávání anotací k třídám
s podtržítkovými namespaces (např. k anotaci Abc dohledá třídu
Něco_Abc; zřejmě nějaká magie)
anotace jsou rozšířením třídy Annotation. prvky anotací jsou reprezentovány veřejnými proměnnými s defaultní hodnotou. validace hodnot není možná. parser umí téměř vše co je třeba a dal by se s úpravami převzít, kdyby to nebylo pod LGPL. rozumíte těm licencím někdo? dá se LGPL začlenit do Nette?
Doctrine 2: nová (zatím nestabilní) verze obsahuje celkem jednoduchý parser, který umí i inline anotace, PHP konstanty v anotacích a samozřejmě kontrolu jmen. anotace jsou definovány jako final třídy odvozené od předka Doctrine\Common\Annotations\Annotation. jejich prvky jsou veřejné proměnné a mohou mít defaultní hodnotu. není zde možnost validace hodnot anotací. knihovna anotací stejně jako celá Doctrine 2 pracuje se jmennými prostory PHP 5.3
Stubbles framework: jedinný framework, který jsem našel
(http://stubbles.net/…/Annotations).
podporuje anotace k parametrům metod, stejně jako Java. v PHP docbloky
k parametrům psát nejdou, proto využívá vlastní syntaxe:
@ParamAnnotation{param}(...)
. podporuje typové anotace –
hodnoty anotace se vrátí třídou jiného jména. to je celkem zbytečná
vlastnost, pokud je třída jen nosičem dat a nelze ji rozšiřovat. podporuje
omezení působnosti anotace metodou getAnnotationTarget()
. a
podporuje také uživatelskou kontrolu hodnot – po naplnění anotace se na
ní volá metoda finish(). k anotacím se přistupuje přes rozšíření
reflection API
na blogu autora je rok starý článek o různých implementacích anotací v PHP: http://www.stubbles.org/…P-world.html
phpAnnotations: zmatená implementace se zbytečně
nafouklým nelogickým API. vlastně toho moc neumí. validaci parametrů
ponechává na třídě anotace
(http://sourceforge.net/…annotations/)
o dalších buď nemá smysl psát, nebo jsem je nenašel
1) kontrola jména a parametrů anotace
na fóru i na poslední poslední sobotě byla debata o tom, že
implementace anotací v Nette by měla obsahovat seznam podporovaných, nebo
spíše ignorovaných (nekontrolovaných) anotací. hodily by se tedy statické
metody
addNěco() pro přidání jména anotace do seznamu a
removeNěco() pro odebrání jména ze seznamu (pokud je pro anotace, která je
normálně ignorována využívána nějakým modulem. např. anotace
var
ke kontrole typu)
ostatní (uživatelské) anotace by měly být deklarovány třídou s názvem NěcoAnnotation, kde ‚Něco‘ značí jméno anotace
řešení č.1:
definovat interface IAnnotation s povinným konstruktorem přijímajícím data
a metodou getValue() s parametrem pro jméno prvku (defaultně ‚value‘ pro
anotace s jednou hodnotou) a getValues() pro všechny prvky:
interface IAnnotation {
public function __construct($data);
public function getValue($name = 'value');
public function getValues();
}
class MojeAnotaceAnnotation implements IAnnotation {
//...
}
/** @MojeAnotace(...) */
řešení č.2:
pokud bychom vynechali kontrolu hodnot a kontrolovlali jen jména, mohlo by to
vypadat nějak takto – anotace jsou rozšířením nějaké základní
třídy (dejme tomu třeba Annotation, i když ta má nyní jiný účel). pak
by prvky mohly být přímo veřejné vlastnosti anotace
class MojeAnotaceAnnotation extends Annotation {
public nepovinna = 'default';
public povinna;
public dalsi;
}
/** @MojeAnotace(povinna = 123, dalsi = 4.56) */
// vrátí array('nepovinna' => 'default', 'povinna' => 123, 'dalsi' => 4.56)
první možnost je jednodušší na implementaci a dává uživatelům více volnosti, za to ale uživatel musí implementovat samotnou kontrolu jmen prvků a jejich hodnot. druhou používá většina konkurenčních řešení a je na uživatele nenáročná, i když nekontroluje hodnoty samotné. i tak by se ale dala doplnit nějaká základní kontrola typů hodnot anotací (třeba pomocí anotací :). např.:
class MojeAnotaceAnnotation extends Annotation {
/** @var string */
public nepovinna = 'default';
/** @var integer */
public povinna;
/** @var float */
public povinna;
}
pozn.:
do ignorovaných anotací bych zařadil všechny, které podporuje phpDoc:
abstract, access, author, category, copyright, deprecated, example, filesource,
final, global, ignore, internal, license, link, method, name, package, param,
property, return, see, since, static, staticvar, subpackage, throws, todo,
tutorial, uses, var, version
otázka je, co s prvním písmenem. u dokumentačních anotací je tak nějak obvyklé, že začínají malým písmenem. u tříd je zas obvyklé, že začínají velkým. jména tříd ale nejsou case sensitive. tím by to mohlo být vyřešeno. nebo ne?
druhá horší otázka k názvům tříd anotací – co se jmennými prostory?
2) kontrola působnosti anotace
neboli kontrola zda je anotace použita na prvku pro který může byla
definována. držel bych se značení zažitého z Javy – anotace
@Target. možné hodnoty: class
, method
,
property
, function
, nebo jejich kombinace, nebo
all
např.:
/** @Target(method) */
class MojeAnotaceAnnotation extends Annotation {}
/** @MojeAnotace */
class Něco {}
// v tomto případě by mělo selhat. anotace je povolená jen pro metody
3) dědičnost anotací
vedle: https://forum.nette.org/…nost-anotaci
příklad značkování á la Java:
/** @Inheritable */
class MojeAnotaceAnnotation extends Annotation {}
4) API
současné API v třídě Annotation je jednoduché a přímočaré, ale nepoužívá se zrovna nejjednodušeji. třída pracující s anotací musí znát dvě různé věci – Annotation a Reflection… a jejich vztah. proto bych raději API anotací přesunul do rozšíření reflection tříd. např.:
class ClassReflection extends ReflectionClass {
hasAnnotation($name) {...}
getAnnotation($name) {...}
getAnnotations() {...}
}
// a podobně u ostatních reflexí
viz třeba tady: https://github.com/…/Reflection/
funkčnost třídy Anotations bych přesunul do nějaké vhodně pojmenované (AnnotationParser?) a přidal obsluhu seznamu ignorovaných anotací addIgnored(), removeIgnored(), getIgnored(). a možná by byla lepší instance parseru, než statická třída
takže, šup sem s nápady! :]
Editoval paranoiq (17. 12. 2009 2:32)
- Petr Motejlek
- Člen | 293
Já bych se chtěl jen zeptat, @Target by bylo obsluhováno vlastním TargetAnnotation? ;)
- paranoiq
- Člen | 392
to by bylo trošku zbytečné. není to uživatelská anotace
tady problém není. @Target a další metaanotace, pokud se na nějakých shodneme by se daly jednoduše jako uživatelské zakázat
spíš jsem nedomyslel tu @var ve třetím příkladu (i když to je jen jedna z možností). tam by mohl být problém. je to normální dokumentační anotace a někdo (třeba já) ji bude určitě chtít používat jako uživatelskou. hmm… nějaké návrhy?
- Petr Motejlek
- Člen | 293
Stejně mi přijde zbytečný některý anotace ignorovat. Bude něco zpomalovat, když bude AuthorAnnotation, CategoryAnnotation? Aby nebylo tolik tříd, možná by bylo lepší, kdyby se použitá obslužná třída nepoznávala jen podle názvu, ale třeba by se u Annotations musely anotační třídy nějak hlásit, třeba něco jako
<?php
Annotations::addAnnotationHandler('author', 'DocumentaryAnnotations', array(Annotations::INHERITABLE => true, Annotatons::TARGET_METHOD => true, Annotations::TARGET_CLASS => true))
?>
DocumentaryAnnotations by potom musela být třída, která by měla statické metody handleAuthorOnMethod(‚třida‘, ‚metoda‘), handleAuthorOnClass(‚třída‘), resp. i ostatní metody podle toho, u kolika anotací je zaregistrovaná jako handler (a jaký má cíl). Samotná třída Annotations by pouze skenovala anotace a spouštěla handlery podle toho, na které by narazila. Obsluha jednotlivých anotací by pak byla přesunutá směrem k anotačním třídám, pryč z Annotations.
Editoval Petr Motejlek (17. 12. 2009 16:40)
- johno
- Člen | 10
Ahojte, kedze som autorom Addendum, tak sa vyjadrim.
Addendum: (http://code.google.com/p/addendum/) od prvotní verze prošlo velkou změnou. krom jmen řeší i kontrolu působnosti anotací. parser umí vnořené anotace (anotace je hodnotou jiné anotace), třídní konstanty v anotacích, vnořená pole pomocí syntaxe
{}
. stále neumí typ NULL a dokumentační anotace (a zřejmě se jim vyhýbá záměrně:testAnnotationMatcherShouldNotMatchAnnotationWithSmallStartingLetter()
).
Typ NULL je do parsera lahke pridat, v prvom momente som ho ale nepotreboval, lebo atributy maju standardne hodnotu null. Ale ked by to uz niekto chcel v inych typoch (poliach a tak) tak to ma zmysel. Mozem dorobit.
Dokumentacne anotacie su zamerne vynechavane, spracovavaju sa len anotacie, ktore su potomkom Annotation resp AddendumAnnotation (tusim), ten test je tam ostal zo starej verzie, kde som to rozlisoval na zaklade prveho pismena.
podivnou vlastností je vyhledávání anotací k třídám s podtržítkovými namespaces (např. k anotaci Abc dohledá třídu Něco_Abc; zřejmě nějaká magie)
Toto je kvoli tomu, ze Neco_ sa bezne pouziva ako namespace a bolo by strasne umorne vsade pisat nazvy anotaci cez plne mena. To som si nevymyslel ja to bol feature request ;-)
anotace jsou rozšířením třídy Annotation. prvky anotací jsou reprezentovány veřejnými proměnnými s defaultní hodnotou. validace hodnot není možná.
Validacia mozna je, dokonca je to „dokumentacii“ http://code.google.com/…ncedFeatures (cast Implementing custom constraints) – teda ak ta chapem spravne.
parser umí téměř vše co je třeba a dal by se s úpravami převzít, kdyby to nebylo pod LGPL. rozumíte těm licencím někdo? dá se LGPL začlenit do Nette?
Nemam problem licenciu specialne pre Nette zmenit, mozem si s tym robit co chcem ;)
Doctrine 2: nová (zatím nestabilní) verze obsahuje celkem jednoduchý parser, který umí i inline anotace, PHP konstanty v anotacích a samozřejmě kontrolu jmen. anotace jsou definovány jako final třídy odvozené od předka Doctrine\Common\Annotations\Annotation. jejich prvky jsou veřejné proměnné a mohou mít defaultní hodnotu. není zde možnost validace hodnot anotací. knihovna anotací stejně jako celá Doctrine 2 pracuje se jmennými prostory PHP 5.3
Doctrine 2 chcelo pouzit Addendum (dokonca aj pouzivalo), netusim preco odrazu pouzivaju svoje vlastne riesenie, ktore minimalne nebude fungovat s vypnutym –preserve-docs co je na hostingoch uplne bezne, ale bude to asi syndrom „not-developed-here“, pretoze autor Doctrine ma nekontaktoval aj ked som s nim mal dlsie diskusie ohladom integracie.
Jedine s cim zatial nie som na 100% spokojny v Addendum je parser, ktory by chcelo prehodit do LL(1) parsera, teraz je to v podstate parser na CF gramatiky.
Tak ci onak, ten projekt je velmi dobre otestovany a v klude sa v nom zacnite rypat, rad poradim.
- paranoiq
- Člen | 392
Petr Motejlek napsal(a):
Stejně mi přijde zbytečný některý anotace ignorovat. Bude něco zpomalovat, když bude AuthorAnnotation, CategoryAnnotation?
pokud by se tam nacpaly nějaké defaultní třídy, nešly by pak nahradit vlastní implementací (pokud by se nezrobila nějaká registrace jak navrhuješ níže)
Aby nebylo tolik tříd, možná by bylo lepší, kdyby se použitá obslužná třída nepoznávala jen podle názvu, ale třeba by se u Annotations musely anotační třídy nějak hlásit, třeba něco jako
<?php Annotations::addAnnotationHandler('author', 'DocumentaryAnnotations', array(Annotations::INHERITABLE => true, Annotatons::TARGET_METHOD => true, Annotations::TARGET_CLASS => true)) ?>
to by možná bylo nejlepší řešení, ale není to zbytečně složité? musela by se registrovat každá třída anotace. pokud by byl seznam ignorovaných, musely by se registrovat (vyřazovat ze seznamu) jen výjimky
DocumentaryAnnotations by potom musela být třída, která by měla statické metody handleAuthorOnMethod(‚třida‘, ‚metoda‘), handleAuthorOnClass(‚třída‘), resp. i ostatní metody podle toho, u kolika anotací je zaregistrovaná jako handler (a jaký má cíl). Samotná třída Annotations by pouze skenovala anotace a spouštěla handlery podle toho, na které by narazila. Obsluha jednotlivých anotací by pak byla přesunutá směrem k anotačním třídám, pryč z Annotations.
určitě bych nedával více anotací do jedné třídy. jedna anotace ⇒ jedna třída
- paranoiq
- Člen | 392
johno napsal(a):
Ahojte, kedze som autorom Addendum, tak sa vyjadrim.
ahoj johno :]
Typ NULL je do parsera lahke pridat, v prvom momente som ho ale nepotreboval, lebo atributy maju standardne hodnotu null. Ale ked by to uz niekto chcel v inych typoch (poliach a tak) tak to ma zmysel. Mozem dorobit.
dík
Dokumentacne anotacie su zamerne vynechavane, spracovavaju sa len anotacie, ktore su potomkom Annotation resp AddendumAnnotation (tusim), ten test je tam ostal zo starej verzie, kde som to rozlisoval na zaklade prveho pismena.
jaký to má vlastně důvod? některé obsahují užitečné informace a bylo by zbytečné je duplikovat nějakou vlastní anotací (DRY)
podivnou vlastností je vyhledávání anotací k třídám s podtržítkovými namespaces (např. k anotaci Abc dohledá třídu Něco_Abc; zřejmě nějaká magie)
Toto je kvoli tomu, ze Neco_ sa bezne pouziva ako namespace a bolo by strasne umorne vsade pisat nazvy anotaci cez plne mena. To som si nevymyslel ja to bol feature request ;-)
jsou s tím dva problémky:
- Nette používá loader, který načítá třídy až za běhu, takže hledaná třída ještě nemusí existovat. jak načteš třídu s namespacem, když nebudeš znát celé její jméno?
- co když budou definovány dvě třídy s jiným prefixem? (např: Něco_Anotace a NěcoJiného_Anotace)
Validacia mozna je, dokonca je to „dokumentacii“ http://code.google.com/…ncedFeatures (cast Implementing custom constraints) – teda ak ta chapem spravne.
přehlédl jsem. super
parser umí téměř vše co je třeba a dal by se s úpravami převzít, kdyby to nebylo pod LGPL. rozumíte těm licencím někdo? dá se LGPL začlenit do Nette?
Nemam problem licenciu specialne pre Nette zmenit, mozem si s tym robit co chcem ;)
Tak ci onak, ten projekt je velmi dobre otestovany a v klude sa v nom zacnite rypat, rad poradim.
tohle je v podstatě na Davídkovi. já bych byl pro začelnění. ať zas nevynalézáme mnohoúhelníky znovu
- Petr Motejlek
- Člen | 293
Já jsem počítal s tím, že když nebude něco zaregistrováno, že by se vyzkoušelo, jestli náhodou neexistuje anotační třída se stejným názvem. Když teda nezaregistruju obsluhu @MyAnnotation, tak se alespoň těsně před umřením zkusí, jestli náhodou neeixstuje MyAnnotationAnnotation.
paranoiq napsal(a):
Petr Motejlek napsal(a):
Stejně mi přijde zbytečný některý anotace ignorovat. Bude něco zpomalovat, když bude AuthorAnnotation, CategoryAnnotation?
pokud by se tam nacpaly nějaké defaultní třídy, nešly by pak nahradit vlastní implementací (pokud by se nezrobila nějaká registrace jak navrhuješ níže)
Aby nebylo tolik tříd, možná by bylo lepší, kdyby se použitá obslužná třída nepoznávala jen podle názvu, ale třeba by se u Annotations musely anotační třídy nějak hlásit, třeba něco jako
<?php Annotations::addAnnotationHandler('author', 'DocumentaryAnnotations', array(Annotations::INHERITABLE => true, Annotatons::TARGET_METHOD => true, Annotations::TARGET_CLASS => true)) ?>
to by možná bylo nejlepší řešení, ale není to zbytečně složité? musela by se registrovat každá třída anotace. pokud by byl seznam ignorovaných, musely by se registrovat (vyřazovat ze seznamu) jen výjimky
DocumentaryAnnotations by potom musela být třída, která by měla statické metody handleAuthorOnMethod(‚třida‘, ‚metoda‘), handleAuthorOnClass(‚třída‘), resp. i ostatní metody podle toho, u kolika anotací je zaregistrovaná jako handler (a jaký má cíl). Samotná třída Annotations by pouze skenovala anotace a spouštěla handlery podle toho, na které by narazila. Obsluha jednotlivých anotací by pak byla přesunutá směrem k anotačním třídám, pryč z Annotations.
určitě bych nedával více anotací do jedné třídy. jedna anotace ⇒ jedna třída
- johno
- Člen | 10
paranoiq napsal(a):
Dokumentacne anotacie su zamerne vynechavane, spracovavaju sa len anotacie, ktore su potomkom Annotation resp AddendumAnnotation (tusim), ten test je tam ostal zo starej verzie, kde som to rozlisoval na zaklade prveho pismena.
jaký to má vlastně důvod? některé obsahují užitečné informace a bylo by zbytečné je duplikovat nějakou vlastní anotací (DRY)
Ten povodny dovod bol ten, ze anotacie phpdoc maju inu trochu syntax a povodne ani nebol umysel ich nijako parsovat. Ako pozeram tak aj Nette anotacie pouzivaju inu syntax. Addendum vyzaduje parametre v zatvorkach.
Nemal by byt vsak problem rozsirit parser o nette syntax. (Ako na to pozeram https://github.com/…tations.phpt tak by stacilo pridat stringy bez uvodzoviek a volitelne zatvorky)
1. Nette používá loader, který načítá třídy až za běhu, takže hledaná třída ještě nemusí existovat. jak načteš třídu s namespacem, když nebudeš znát celé její jméno?
Toto je problem, vid nizsie.
2. co když budou definovány dvě třídy s jiným prefixem? (např: Něco_Anotace a NěcoJiného_Anotace)
Vyleti error – https://code.google.com/…dum_test.php#24
Co sa tyka chovania, tak vidim nasledovne scenare:
Mam /** @MojaAnotacia */ na nejakej triede.
- Ak je MojaAnotacia v ignore liste, tak nie je co riesit.
- Ak existuje trieda MojaAnotacia a je potomkom Annotation, tak tiez nie je co riesit.
- Ak existuje anotacia Prefix_MojaAnotacia (a je to jedinecne), tak nie je co riesit (autoload tu ma problem).
- Ak existuje trieda MojaAnotacia, ale nie je potomkom Annotation, tak error?
- Ak neexistuje trieda MojaAnotacia, tak skusim autoloader (a skusim 2,3 ak sa nepodari tak hodit error)
Je tam zopar otaznikov.
Scenar cislo 2 nemoze fungovat s autoload, jedine ak by autoloader vedel
nejakym sposobom natiahnut len tu spravnu anotaciu. Riesenim by mohlo byt
spravit nieco ako aliasing anotacii
Addendum::aliasAnnotation(‚MegaProject_MyAnnotation‘, ‚MyAnnotation‘),
ktory by musel byt jedinecny a potom by autoloader vedel natiahnut tu spravnu
triedu. Tu vsak pridem o momentalnu magicku funkcionalitu, ktora to vie robit
bez autoloadera.
Problematicky je hlavne scenar cislo 5, pokial chcem hadzat chybu ked anotacna trieda neexistuje, tak mam problem s phpdoc anotaciami, lebo k nim neexistuje trieda.
Nejake navrhy?
Editoval johno (29. 12. 2009 22:03)
- paranoiq
- Člen | 392
johno napsal(a):
Ten povodny dovod bol ten, ze anotacie phpdoc maju inu trochu syntax a povodne ani nebol umysel ich nijako parsovat. Ako pozeram tak aj Nette anotacie pouzivaju inu syntax. Addendum vyzaduje parametre v zatvorkach.
Nemal by byt vsak problem rozsirit parser o nette syntax. (Ako na to pozeram https://github.com/…tations.phpt tak by stacilo pridat stringy bez uvodzoviek a volitelne zatvorky)
Nette původně bralo také jen závorkovou variantu. bezzávorkovou verzi David přidal nedávno. je to celkem malá změna – v Nette tuším pár znaků v jednom regexpu. v Addendum asi větší
2. co když budou definovány dvě třídy s jiným prefixem? (např: Něco_Anotace a NěcoJiného_Anotace)
Vyleti error – https://code.google.com/…dum_test.php#24
a co když v danou chvíli existuje jen jedna a to právě ta nesprávná? (autoloading)
tahle magie se mi od začátku nelíbí právě proto, že si nikdy nemůžu být jist, co se vlastně stane
Co sa tyka chovania, tak vidim nasledovne scenare:
Mam /** @MojaAnotacia */ na nejakej triede.
- Ak je MojaAnotacia v ignore liste, tak nie je co riesit.
- Ak existuje trieda MojaAnotacia a je potomkom Annotation, tak tiez nie je co riesit.
- Ak existuje anotacia Prefix_MojaAnotacia (a je to jedinecne), tak nie je co riesit (autoload tu ma problem).
- Ak existuje trieda MojaAnotacia, ale nie je potomkom Annotation, tak error?
- Ak neexistuje trieda MojaAnotacia, tak skusim autoloader (a skusim 2,3 ak sa nepodari tak hodit error)
4. určitě error. jméno je málo
pokud mají fungovat podtržítkové namespacy i autoloading, nějaký ten seznam aliasů asi bude nutnost. ostatně to by pak umožnilo i rozšiřování anotací, které někdo navrhoval a PHP5.3 namespacy
celý postup bych viděl takhle:
- zkontrolovat ignore-list a případně skončit
- zkontrolovat alias-list a případně z něj zjistit skutečné jméno třídy
- vytvořit objekt (není-li třída, autoloading si ji najde. pokud nenajde, výjimka je vítána)
- zkontrolovat, zda je objekt potomek Annotation
nějakým kontrolováním, zda třída existuje či ne bych se vůbec nezabýval. to je práce autoloadingu
- David Grudl
- Nette Core | 8227
paranoiq napsal(a):
4) API
současné API v třídě Annotation je jednoduché a přímočaré, ale nepoužívá se zrovna nejjednodušeji.
Začnu tímto bodem, protože je klíčový. Současné API je totiž
akademické, vytvořené „od stolu“ nikoliv „z praxe“ a je to hned
vidět. Navrhnout lepší API se dá jen používáním. Tohle ale platí pro
celý návrh funkčnosti anotací. Takže vhodnější, než implementovat
podporu pro @Target
nebo @Inherited
, je zvážit,
jestli to vůbec je v praxi potřeba.
Zpět k API. Jít cestou začlenění do Reflection by bylo elegantní, ale znamená to vytvořit potomky tříd ReflectionClass, ReflectionObject, ReflectionProperty, ReflectionMethod a v nich přepsat řadu metod vracejících tyto objekty (např. getDeclaringClass(), ReflectionClass::getMethods(), …) tak, aby vracely poděděné objekty.
Napsat celou vrstvu kvůli hezčímu začlenění anotací je dle mého plýtvání kilobajty kódu ;)
Ale něco docela jiného by bylo vytvořit vrstvu nad reflection třídami
s doplněním Nette\Object funkcionality. Samotný přístup k anotacím by
byl poté teoreticky realizovatelný skrze extension method (současné API
třídy Annotations je k tomu dokonce navržené, lze použít
Object::extensionMethod('NetteReflectionClass::getAnnotation', 'Annotations::get');
).
Píšu teoreticky, protože podpora anotací by se jistě neřešila
přes extension method, ale důležitý je ten pohled na věc: vrstva nad
reflection classes neexistuje kvůli anotacím, ale umožňuje k nim hezčí
přístup, mimojiné.
Otázkou je, jak potomky tříd ReflectionClass, ReflectionObject,
ReflectionProperty, ReflectionMethod, ReflectionParameter pojmenovat. Všimněte
si totiž toho, že názvy neodpovídají Nette naming convention (a
samozřejmě ani PHP naming bordels), jsou to totiž úplně debilně
pojmenované třídy. Vhodnější než ReflectionClass by bylo ClassReflection
nebo ClassReflector. Také ReflectionProperty zavádí termín
property
pro něco, čemu se ve zbytku PHP říká variable, Nette
pod pojmem property také chápe něco jiného. Jenže tady nejde jen
o pojmenování třídy, termín property je použit v řadě metod.
ReflectionParameter je opět blbý název, není jasné, že jde
o parametry metod.
1) kontrola jména a parametrů anotace
na fóru i na poslední poslední sobotě byla debata o tom, že implementace anotací v Nette by měla obsahovat seznam podporovaných, nebo spíše ignorovaných (nekontrolovaných) anotací.
Ještě přihodím jednu variantu: co kdyby uživatelské anotace začínaly
velkým písmenem? Např. @Roles
by pak vyžadovalo existenci
třídy RolesAnnotation
. Otázka ignorovaných a kontrolovaných
anotací by byla vyřešena. Ale vzniká jiný problém a tím je podpora pro
@persistent
s malým p
.
řešení č.1:
definovat interface IAnnotation s povinným konstruktorem přijímajícím data a metodou getValue() s parametrem pro jméno prvku (defaultně ‚value‘ pro anotace s jednou hodnotou) a getValues() pro všechny prvky:
Interfaces jsou Nette-way, šel bych touto cestou.
>
řešení č.2:
pokud bychom vynechali kontrolu hodnot a kontrolovlali jen jména, mohlo by to vypadat nějak takto – anotace jsou rozšířením nějaké základní třídy (dejme tomu třeba Annotation, i když ta má nyní jiný účel).
Jsem také pro. Vůbec to není v rozporu s bodem 1, prostě třída
Annotation (nebo SimpleAnnotation) bude implementovat IAnnotation a
v konstruktoru nastaví data do veřejných proměnných. Ideální.
>
druhá horší otázka k názvům tříd anotací – co se jmennými prostory?
Protože jde z principu o funkčnost jdoucí napříč jmennými prostory a protože třídy mají připonu Annotation, klidně bych jmenné prostory nepoužíval.
2) kontrola působnosti anotace
Přiznám se, že vůbec netuším, k čemu by to bylo dobré. Prostě anotaci uvedu přímo u prvku, ke kterému se vztahuje, ne?
3) dědičnost anotací
Neměla by být dědičnost implicitní a „nevypnutelná“?
- David Grudl
- Nette Core | 8227
paranoiq napsal(a):
parser umí téměř vše co je třeba a dal by se s úpravami převzít, kdyby to nebylo pod LGPL. rozumíte těm licencím někdo? dá se LGPL začlenit do Nette?
Věřím tomu, že se s johno domluvit by nebyl žádný problém. Ale šel bych spíš cestou vymyšlení, co skutečně chceme, jak by to mělo fungovat a teprve poté řešil implementaci. Jsem vděčný za všechny připomínky johna k funkčnosti, má s tímto určitě největší zkušenosti.
- johno
- Člen | 10
Zpět k API. Jít cestou začlenění do Reflection by bylo elegantní, ale znamená to vytvořit potomky tříd ReflectionClass, ReflectionObject, ReflectionProperty, ReflectionMethod a v nich přepsat řadu metod vracejících tyto objekty (např. getDeclaringClass(), ReflectionClass::getMethods(), …) tak, aby vracely poděděné objekty.
Napsat celou vrstvu kvůli hezčímu začlenění anotací je dle mého plýtvání kilobajty kódu ;)
Tak vela toho nie je – http://code.google.com/…otations.php#175
Ještě přihodím jednu variantu: co kdyby uživatelské anotace začínaly velkým písmenem? Např. @Roles by pak vyžadovalo existenci třídy RolesAnnotation. Otázka ignorovaných a kontrolovaných anotací by byla vyřešena. Ale vzniká jiný problém a tím je podpora pro @persistent s malým p.
Presne takto som to riesil na zaciatku, neosvedcilo sa to. Podla mna tu by
bolo miesto na pouzitie aliasu
Annotations::alias(‚Persistent‘, ‚persistent‘). zvysne spravanie by
mohlo ostat. Problem vsak vznikne ak niekto bude chciet cez Annotations (alebo
ReflectionAPI) spracovavat phpdoc tagy/anotacie, ja tiez nevidim dovod preco by
to niekto chcel…
Kto chce moze si pozriet s cim prisli realni pouzivatelia pocas zivota Addendum (http://code.google.com/…/issues/list?…) a ako som ich vzdy presvedcoval, ze to nepotrebuju ;)
Cize uplne suhlasim s Davidom, ze treba spisat realne pripady pouzitia a potom riesit vsetko ostatne. Ci treba dedenie, kontrolu parametrov, vnorene anotacie. Rad by som videl realne priklady vyuzitia napriklad dedenia anotacii (ktore podla mna funguje v addendum out-of-box aj ked to tak este nikto nechcel).
Co sa tyka @Target, tak to je vyslovene dobre len pre dodatocnu kontrolu. Aby clovek neskusal davat anotaciu niekam kde nema co hladat, v spojeni s kontrolou parametrov by to mohlo robit problemy, ak by napriklad anotacia predpokladala, ze je na triede a bola by nahodou na metode.
Realny priklad
/** @ActsAsTree @ActAsSortable(scope => parent_id) */
class Document extends SuperNetteDibiORMMapperObject {}
pricom @ActsAsTree by pridavala nejake metody cez NObject extension method (addChild, removeChild, children, parent…) a potom @ActsAsSortable opat (moveUp, moveDown, swap…)
Tu je jasne, ze pri instanciacii sa musi kontrolovat ci target anotacie je trieda (a pokial to clovek nechce robit rucne) tak @Target je elegantne riesenie.
- jakubkulhan
- Člen | 55
Přesuňme se do budoucnosti a řekněme, že všechno je implementované. V kterém okamžiku se bude kontrolovat, že anotace tak, jak je zapsaná v doc-komentáři, odpovídá její definici, vytvořené třebas nějakou tou třídou? Chápu správně, že chyba (výjimka?) by byla vyvolána až v momentě, kdy se snažíme data anotace načíst? V Javě, pokud se nepletu, se to děje v compile-time, takže kód, kde je v anotaci syntaktická chyba, chybí povinný parametr apod., se nezkompiluje a hotovo. Bez této vlastnosti (pokud je chyba v anotaci, selže kompilace /v případě PHP vložení souboru?/) se podle mě nemá smysl o nějaké striktnosti anotací bavit; pokud něco má selhat, mělo by to selhat co nejdříve a ne až v případě, kdy to potřebuji.
- v6ak
- Člen | 206
K namespaces z praxe: kolik tu máme @NotNull a @NonNull anotací a to třeba i s trošku jiným významem? (Java)
BTW: Citace z http://projectlombok.org/…es/Data.html : „Any annotations named @NonNull (case insensitive) on a field are interpreted as: This field must not ever hold null.“ (Nezáleží na použitém namespace – asi důsledek množství takovýchto anotací. Neříkám, že postup autorů Lomboku schvaluji, ale chápu to rozhodnutí.)
Přitom com.sun.istack.internal.NotNull má třeba trošku jiný význam.
johno napsal(a):
Riesenim by mohlo byt spravit nieco ako aliasing anotacii Addendum::aliasAnnotation(‚MegaProject_MyAnnotation‘, ‚MyAnnotation‘)
Byl bych spíš pro použití use. Aby bylo aliasAnnotation čisté, muselo by to držet tabulky ke každému souboru zvlášť, což by se asi hackovalo přes stacktrace.
- Jerry123456789
- Člen | 37
Jak to tedy dopadlo s podobou anotací? Poví mi někdo jak se k nim
dostává, nebo si mám přečíst kód? :D
Mimochodem jak je to s rychlostí, zkoušel to někdo? Osobně se anotací
trošku bojím, hlavně kvůli reflekci.