注释在PHP中如何有用?我不是泛指PHPDoc。
我只是想要一个现实世界的例子,我想。
因此,根据@Max的回答:注释只通过一行专门的PHPDoc完成与抽象工厂相同的任务。- hopeseekr 0秒前编辑
发布于 2010-09-02 08:47:32
罗伯·奥莫斯正确地解释了这一点:
注解基本上允许注入行为,并且可以促进解耦。
用我的话说,这些注释是很有价值的,特别是在反射的上下文中,您可以在这里收集(额外的)元数据,说明您正在检查的类/方法/属性。
另一个示例,而不是ORM:依赖注入框架。例如,即将到来的FLOW3框架使用docComments/注释来识别从DI容器创建的实例中注入了哪些对象,而不是在XML文件中指定对象。
以下是过于简化的示例:
您有两个类,一个Soldier类和一个Weapon类。Weapon实例被注入到Soldier实例中。看看这两个类的定义:
class Weapon {
public function shoot() {
print "... shooting ...";
}
}
class Soldier {
private $weapon;
public function setWeapon($weapon) {
$this->weapon = $weapon;
}
public function fight() {
$this->weapon->shoot();
}
}如果要使用这个类并手动注入所有依赖项,就可以这样做:
$weapon = new Weapon();
$soldier = new Soldier();
$soldier->setWeapon($weapon);
$soldier->fight();好的,那是大量的样板代码(请原谅我,我来解释什么注释是有用的,很快)。依赖注入框架可以为您做的是抽象创建这样的组合对象并自动注入所有依赖项,您只需:
$soldier = Container::getInstance('Soldier');
$soldier->fight(); // ! weapon is already injected是的,但是Container必须知道Soldier类有哪些依赖项。因此,大多数通用框架使用XML作为配置格式。示例配置:
<class name="Soldier">
<!-- call setWeapon, inject new Weapon instance -->
<call method="setWeapon">
<argument name="Weapon" />
</call>
</class>但是,为了定义这些依赖关系,FLOW3使用的是直接在PHP代码中的注释而不是XML。在FLOW3中,您的Soldier类将如下所示(语法仅作为示例):
class Soldier {
...
// ---> this
/**
* @inject $weapon Weapon
*/
public function setWeapon($weapon) {
$this->weapon = $weapon;
}
...因此,不需要XML来标记DI容器的Soldier到Weapon的依赖关系。
FLOW 3也在AOP上下文中使用这些注释来标记应该是"weaved“的方法(意为在方法之前或之后注入行为)。
就我而言,我不太确定这些注释是否有用。我不知道它是否让事情变得更简单或者更糟,用PHP代码来“隐藏”这种依赖关系和设置,而不是使用单独的文件。
例如,我在Spring.NET、NHibernate和PHP中使用了DI框架(而不是FLOW3),它们都是基于文件的,不能说它太难了。维护这些安装文件也可以。
但也许未来的FLOW3项目会证明情况正好相反,注解才是真正的出路。
发布于 2014-05-01 19:44:21
为了完整起见,这里有一个使用注释的工作示例,以及如何扩展PHP语言以支持它们,所有这些都在一个文件中。
这些是“真实的”注释,意思是在语言级别声明的,而不是隐藏在注释中。像这样使用'Java‘风格注释的好处在于,解析器忽略注释不能忽略它们。
在__halt_compiler();之前,最上面的部分是处理器,它用一个简单的方法注释来扩展PHP语言,该注释缓存方法调用。
底部的类是在方法上使用@cache注释的一个示例。
(此代码最好自下而上阅读)。
<?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();使用DI容器的示例(基本上与注释无关),还可以使用上述方法修改类构造函数,以处理注入任何依赖项的问题,这使得组件的使用完全透明。在对源代码进行评估之前修改源代码的方法大致相当于自定义Java中的“字节码检测”。(我提到Java,因为AFAIK是第一次引入注释的地方)。
这个特定示例的有用之处在于,不必手动为每个方法编写缓存代码,您只需将方法标记为必须缓存,减少重复工作量,并使代码更加清晰。此外,任何地方的注释的效果都可以在运行时打开或关闭。
发布于 2015-09-09 06:05:36
phpDocumentor和现代IDE使用注释来确定方法参数类型(@param)、返回值(@ return )等等。
PhpUnit测试使用注释来分组测试,定义依赖项。
https://stackoverflow.com/questions/3623499
复制相似问题