註釋如何在PHP中有用?我一般不是指PHPDoc。註釋在PHP中如何有用?
我只是想要一個真實世界的例子或者什麼的,我想。
因此,根據@最多的回答是:註解完成同樣的事情抽象工廠,只能通過專門的PHPDoc的一條線。 - hopeseekr 0秒前編輯
註釋如何在PHP中有用?我一般不是指PHPDoc。註釋在PHP中如何有用?
我只是想要一個真實世界的例子或者什麼的,我想。
因此,根據@最多的回答是:註解完成同樣的事情抽象工廠,只能通過專門的PHPDoc的一條線。 - hopeseekr 0秒前編輯
Rob Olmos解釋是正確的:
註解基本上讓你注入行爲,並能促進脫鉤。
我的話我會說,這些註釋是有價值尤其是在reflection,你收集類/方法/屬性,你正在檢查(附加)的元數據環境。
另一個例子代替ORM:Dependency Injection框架。例如,即將到來的FLOW3 framework使用docComments/annotations來標識哪些對象注入從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();
}
}
如果你想使用這個類,並用手注入所有的依賴關係,you'd像這樣做:
$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使用,而不是XML註釋是直接在PHP代碼,以確定這些依賴。在FLOW3,你Soldier
類看起來像這樣(語法只作爲一個例子):
class Soldier {
...
// ---> this
/**
* @inject $weapon Weapon
*/
public function setWeapon($weapon) {
$this->weapon = $weapon;
}
...
因此,無需XML標記的Soldier
到Weapon
爲DI容器的依賴。
FLOW 3在AOP的背景下也使用這些註釋來標記應該「編織」的方法(意思是在方法之前或之後注入行爲)。
就我而言,我不太確定這些註釋的用處。我不知道它是否會讓事情變得更容易或者更糟糕,從而「隱藏」這種依賴和設置,而不是使用單獨的文件。
我工作e。 G。在Spring.NET中,NHibernate和PHP中的DI框架(而不是FLOW3)都基於XML配置文件,並且不能說這太難了。維護這些設置文件也是可以的。
但是也許未來的項目與FLOW3證明相反,註釋是真正的路要走。
所以,註釋只能通過一個專門的PHPDoc完成與Abstract Factories相同的功能。 – 2010-09-02 10:55:07
我選擇了這個答案,因爲它給出了註解潛在用途之一的具體測試用例。 – 2010-09-02 11:33:37
通過'行爲'我們是在談論傳統的'四人幫'行爲'設計模式還是比較鬆散的東西? – Snowcrash 2016-09-30 15:26:49
究竟有什麼用途?
註解基本上讓你注入行爲,並可以促進解耦。 Doctrine ORM就是一個例子。由於使用了註釋,因此您不必像Propel ORM那樣從Doctrine特定的類繼承。
難以調試懶加載動態編碼?
不幸的是像解耦如設計模式,數據轉換的大多數/所有動作的副作用等
嗯。我的大腦仍然沒有跟它交流。 - hopeseekr
如果您沒有從學說類繼承,你最有可能使用一些其他的元數據規範,像一個配置文件,指定一個特定的屬性是記錄的ID。在這種情況下,它將遠離註釋(元數據)描述的語法。
嗯。我的大腦仍然沒有跟它交流。 – 2010-09-02 04:01:09
@hopeseekr - 請參閱我的最新編輯 – 2010-09-02 04:56:47
爲了完整起見,以下是使用註釋以及如何擴展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 Classloaders中的'字節碼檢測'。 (我提到Java,因爲AFAIK是第一次引入註釋的地方)。
這個特殊例子的用處在於,不必手動爲每個方法編寫緩存代碼,只需將方法標記爲必須緩存,減少重複工作量並使代碼更清晰即可。此外,任何地方的註釋效果都可以在運行時打開和關閉。
這很有趣 – 2014-06-26 05:50:58
你如何讓IDE支持這個? – tonix 2015-02-03 14:52:28
phpDocumentor和現代IDE使用註釋來確定方法參數類型(@參數),返回值(@返回)等。
PhpUnit測試使用註釋來分組測試,定義依賴關係。
我想你應該更具體些。 – 2010-09-02 02:50:58
...或至少鏈接到一個具體的例子。從你的答案[這裏]採取(http://stackoverflow.com/questions/3623355/php-annotation-library/3623493#3623493):http://code.google.com/p/addendum/wiki/ShortTutorialByExample – deceze 2010-09-02 02:54:18
如果我問了ORM的用處是什麼,我會收到100萬份回覆。我看看註釋的例子,但它沒有陷入。究竟有什麼好處?難以調試懶加載動態編碼? – 2010-09-02 03:09:06