Deklarace a kontrola striktních anotací

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
paranoiq
Člen | 392
+
0
-

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í:

  1. kontrola jména a parametrů anotace
  2. kontrola působnosti anotace
  3. dědičné anotace
  4. 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
+
0
-

Já bych se chtěl jen zeptat, @Target by bylo obsluhováno vlastním TargetAnnotation? ;)

paranoiq
Člen | 392
+
0
-

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
+
0
-

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
+
0
-

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.

johno
Člen | 10
+
0
-

Podpora pre NULL/null je tam: http://code.google.com/…/source/list commit 69 + 70.

paranoiq
Člen | 392
+
0
-

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
+
0
-

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:

  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?
  2. 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

v6ak
Člen | 206
+
0
-

paranoiq napsal(a):

ať zas nevynalézáme mnohoúhelníky znovu

Nekonečnoúhelníky ;-)

Ale určitě bych byl pro určité úpravy.

Petr Motejlek
Člen | 293
+
0
-

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
+
0
-

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.

  1. Ak je MojaAnotacia v ignore liste, tak nie je co riesit.
  2. Ak existuje trieda MojaAnotacia a je potomkom Annotation, tak tiez nie je co riesit.
  3. Ak existuje anotacia Prefix_MojaAnotacia (a je to jedinecne), tak nie je co riesit (autoload tu ma problem).
  4. Ak existuje trieda MojaAnotacia, ale nie je potomkom Annotation, tak error?
  5. 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
+
0
-

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.

  1. Ak je MojaAnotacia v ignore liste, tak nie je co riesit.
  2. Ak existuje trieda MojaAnotacia a je potomkom Annotation, tak tiez nie je co riesit.
  3. Ak existuje anotacia Prefix_MojaAnotacia (a je to jedinecne), tak nie je co riesit (autoload tu ma problem).
  4. Ak existuje trieda MojaAnotacia, ale nie je potomkom Annotation, tak error?
  5. 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:

  1. zkontrolovat ignore-list a případně skončit
  2. zkontrolovat alias-list a případně z něj zjistit skutečné jméno třídy
  3. vytvořit objekt (není-li třída, autoloading si ji najde. pokud nenajde, výjimka je vítána)
  4. 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 | 8110
+
0
-

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 | 8110
+
0
-

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
+
0
-

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.

David Grudl
Nette Core | 8110
+
0
-

Dá se někde stáhnout ten SuperNetteDibiORMMapper? :-)

jakubkulhan
Člen | 55
+
0
-

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.

David Grudl
Nette Core | 8110
+
0
-

V okamziku, kdy je potrebuji, selhani staci.

David Grudl
Nette Core | 8110
+
0
-

Tak vrstva nad třídami Reflection je ve frameworku.

v6ak
Člen | 206
+
0
-

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
+
0
-

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.

romansklenar
Člen | 655
+
0
-

Neboj se dívat do kódu.

johno
Člen | 10
+
0
-

Apropo rychlost, nevidim problem spravit cachovanie parsovanych anotacii niekde do tmp suboru. Potom by stacilo parsovat anotacie v produkcnom rezime len raz.

v6ak
Člen | 206
+
0
-

To se AFAIK už děje.

johno
Člen | 10
+
0
-

v6ak napsal(a):

To se AFAIK už děje.

Aha, ano vidim. Sorry. V kazdom pripade potom rychlosti sa vobec netreba bat.