Dla kompletności, oto przykład działania zarówno przy użyciu adnotacji, jak i jak rozszerzyć język PHP w celu ich obsługi, wszystko w jednym pliku.
Są to "prawdziwe" adnotacje, co oznacza, zadeklarowane na poziomie językowym i niewidoczne w komentarzach. Zaletą korzystania z adnotacji w stylu Java, takich jak te, jest to, że nie mogą być przeoczone przez parsery ignorujące komentarze.
Górna część, przed __halt_compiler();
jest procesorem, rozszerzającym język PHP o prostą adnotację metody, która buforuje wywołania metod.
Klasa na dole jest przykładem użycia adnotacji @cache
dla metody.
(ten kod najlepiej przeczytać z dołu do góry).
<?php
// parser states
const S_MODIFIER = 0; // public, protected, private, static, abstract, final
const S_FUNCTION = 1; // function name
const S_SIGSTART = 2; // (
const S_SIGEND = 3; //)
const S_BODYSTART = 4; // {
const S_BODY = 5; // ...}
function scan_method($tokens, $i)
{
$state = S_MODIFIER;
$depth = 0; # {}
$funcstart = $i;
$fnameidx;
$funcbodystart;
$funcbodyend;
$sig_start;
$sig_end;
$argnames=array();
$i--;
while (++$i < count($tokens))
{
$tok = $tokens[$i];
if ($tok[0] == T_WHITESPACE)
continue;
switch ($state)
{
case S_MODIFIER:
switch ($tok[0])
{
case T_PUBLIC:
case T_PRIVATE:
case T_PROTECTED:
case T_STATIC:
case T_FINAL:
case T_ABSTRACT: # todo: handle body-less functions below
break;
case T_FUNCTION:
$state=S_FUNCTION;
break;
default:
return false;
}
break;
case S_FUNCTION:
$fname = $tok[1];
$fnameidx = $i;
$state = S_SIGSTART;
break;
case S_SIGSTART:
if ($tok[1]=='(')
{
$sig_start = $i;
$state = S_SIGEND;
}
else return false;
case S_SIGEND:
if ($tok[1]==')')
{
$sig_end = $i;
$state = S_BODYSTART;
}
else if ($tok[0] == T_VARIABLE)
$argnames[]=$tok[1];
break;
case S_BODYSTART:
if ($tok[1] == '{')
{
$funcbodystart = $i;
$state = S_BODY;
}
else return false;
#break; # fallthrough: inc depth
case S_BODY:
if ($tok[1] == '{') $depth++;
else if ($tok[1] == '}')
if (--$depth == 0)
return (object) array(
'body_start' => $funcbodystart,
'body_end' => $i,
'func_start' => $funcstart,
'fnameidx' => $fnameidx,
'fname' => $fname,
'argnames' => $argnames,
'sig_start' => $sig_start,
'sig_end' => $sig_end,
);
break;
default: die("error - unknown state $state");
}
}
return false;
}
function fmt($tokens) {
return implode('', array_map(function($v){return $v[1];}, $tokens));
}
function process_annotation_cache($tokens, $i, $skip, $mi, &$instructions)
{
// prepare some strings
$args = join(', ', $mi->argnames);
$sig = fmt(array_slice($tokens, $mi->sig_start, $mi->sig_end - $mi->sig_start ));
$origf = fmt(array_slice($tokens, $mi->func_start, $mi->body_start - $mi->func_start));
// inject an instruction to rename the cached function
$instructions[] = array(
'action' => 'replace',
'trigger' => $i,
'arg' => $mi->sig_end -$i -1,
'tokens' => array(array("STR", "private function __cached_fn_$mi->fname$sig"))
);
// inject an instruction to insert the caching replacement function
$instructions[] = array(
'action' => 'inject',
'trigger' => $mi->body_end + 1,
'tokens' => array(array("STR", "
$origf
{
static \$cache = array();
\$key = join('#', func_get_args());
return isset(\$cache[\$key]) ? \$cache[\$key]: \$cache[\$key] = \$this->__cached_fn_$mi->fname($args);
}
")));
}
function process_tokens($tokens)
{
$newtokens=array();
$skip=0;
$instructions=array();
foreach ($tokens as $i=>$t)
{
// check for annotation
if ($t[1] == '@'
&& $tokens[$i+1][0]==T_STRING // annotation name
&& $tokens[$i+2][0]==T_WHITESPACE
&& false !== ($methodinfo = scan_method($tokens, $i+3))
)
{
$skip=3; // skip '@', name, whitespace
$ann_method = 'process_annotation_'.$tokens[$i+1][1];
if (function_exists($ann_method))
$ann_method($tokens, $i, $skip, $methodinfo, $instructions);
# else warn about unknown annotation
}
// process instructions to modify the code
if (!empty($instructions))
if ($instructions[0]['trigger'] == $i) // the token index to trigger at
{
$instr = array_shift($instructions);
switch ($instr['action'])
{
case 'replace': $skip = $instr['arg']; # fallthrough
case 'inject': $newtokens=array_merge($newtokens, $instr['tokens']);
break;
default:
echo "<code style='color:red'>unknown instruction '{$instr[1]}'</code>";
}
}
if ($skip) $skip--;
else $newtokens[]=$t;
}
return $newtokens;
}
// main functionality
$data = file_get_contents(__FILE__, null, null, __COMPILER_HALT_OFFSET__);
$tokens = array_slice(token_get_all("<"."?php ". $data), 1);
// make all tokens arrays for easier processing
$tokens = array_map(function($v) { return is_string($v) ? array("STR",$v) : $v;}, $tokens);
echo "<pre style='background-color:black;color:#ddd'>" . htmlentities(fmt($tokens)) . "</pre>";
// modify the tokens, processing annotations
$newtokens = process_tokens($tokens);
// format the new source code
$newcode = fmt($newtokens);
echo "<pre style='background-color:black;color:#ddd'>" . htmlentities($newcode) . "</pre>";
// execute modified code
eval($newcode);
// stop processing this php file so we can have data at the end
__halt_compiler();
class AnnotationExample {
@cache
private function foo($arg = 'default') {
echo "<b>(timeconsuming code)</b>";
return $arg . ": 1";
}
public function __construct() {
echo "<h1 style='color:red'>".get_class()."</h1>";
echo $this->foo("A")."<br/>";
echo $this->foo("A")."<br/>";
echo $this->foo()."<br/>";
echo $this->foo()."<br/>";
}
}
new AnnotationExample();
Pozostając przy przykładzie DI Container (który ma w zasadzie nic wspólnego z adnotacjami), powyższe podejście może być również stosowany do modyfikacji konstruktorów klasy dbać o wstrzykiwanie wszelkie zależności, co sprawia, że korzystanie części całkowicie przezroczyste. Podejście polegające na modyfikacji kodu źródłowego przed jego oceną jest w przybliżeniu równoznaczne z "instrumentacją kodu bajtowego" w niestandardowych modułach ładujących klasy Java. (Wspominam o Javie od czasu AFAIK, to tam po raz pierwszy wprowadzono adnotacje).
Przydatność tego szczególnego przykładu polega na tym, że zamiast ręcznie zapisywać kod pamięci podręcznej dla każdej metody, można po prostu oznaczyć metodę jako wymagającą buforowania, zmniejszając liczbę powtarzających się czynności i czyniąc kod jaśniejszym. Ponadto efekty dowolnej adnotacji w dowolnym miejscu można włączać i wyłączać w czasie wykonywania.
prawdopodobnie powinieneś być bardziej konkretny. –
... lub przynajmniej link do konkretnego przykładu. Zaczerpnięte z twojej odpowiedzi [tutaj] (http://stackoverflow.com/questions/3623355/php-annotation-library/3623493#3623493): http://code.google.com/p/addendum/wiki/ShortTutorialByEprzykład – deceze
Jeśli Zapytałem, do czego ORM był przydatny, dostałbym milion odpowiedzi. Patrzę na przykłady adnotacji, ale nie w to wpadło. Dokładnie to, co jest dobre? Trudno debugować leniwy ładowanie dynamicznego kodowania? –