2010-09-02 3 views
32

PHP में एनोटेशन उपयोगी कैसे है? और मेरा मतलब PHPDoc सामान्य रूप से नहीं है।PHP में एनोटेशन उपयोगी कैसे है?

मुझे बस एक असली दुनिया का उदाहरण या कुछ चाहिए, मुझे लगता है।


तो, @ मैक्स के जवाब के अनुसार: एनोटेशन एक ही बात सार कारखानों के रूप में, को पूरा केवल विशेष phpdoc में से एक लाइन के माध्यम से। - hopeseekr 0 सेकंड पहले संपादित करें

+7

मुझे लगता है कि आपको शायद अधिक विशिष्ट होना चाहिए, मुझे लगता है। –

+2

... या कम से कम एक ठोस उदाहरण से लिंक करें। आपके उत्तर से लिया गया [यहां] (http://stackoverflow.com/questions/3623355/php-annotation-library/3623493#3623493): http://code.google.com/p/addendum/wiki/ShortTutorialByExample – deceze

+0

यदि मैंने पूछा कि एक ओआरएम क्या उपयोगी था, मुझे दस लाख प्रतिक्रियाएं मिलेंगी। मैं एनोटेशन के उदाहरण देखता हूं लेकिन इसमें डूब गया नहीं है। वास्तव में यह क्या अच्छा है? आलसी लोडिंग गतिशील कोडिंग डीबग करना मुश्किल है? –

उत्तर

49

Rob Olmos यह सही समझाया:

एनोटेशन मूल रूप से आप व्यवहार इंजेक्षन और decoupling को बढ़ावा देने के कर सकते हैं।

मेरे शब्दों में मुझे लगता है कि इन टिप्पणियों को विशेष रूप से reflection जहां इकट्ठा (अतिरिक्त) वर्ग/विधि/संपत्ति आप का निरीक्षण कर रहे हैं के बारे में मेटाडेटा के संदर्भ में महत्वपूर्ण हैं कहेंगे।

ओआरएम के बजाय एक और उदाहरण: Dependency Injection ढांचे। उदाहरण के लिए आने वाले FLOW3 framework उदाहरण के लिए docComments/annotations का उपयोग यह पहचानने के लिए करता है कि किसी XML कॉन्फ़िगरेशन फ़ाइल में निर्दिष्ट करने के बजाय डी कंटेनर से बनाए गए किसी उदाहरण में कौन सी ऑब्जेक्ट इंजेक्शन दी जाती है।

oversimplified उदाहरण का पालन करना:

आप दो वर्गों, एक Soldier वर्ग और एक Weapon वर्ग की है। एक Weapon उदाहरण Soldier उदाहरण में इंजेक्शन दिया जाता है। एक

$weapon = new Weapon(); 

$soldier = new Soldier(); 
$soldier->setWeapon($weapon); 
$soldier->fight(); 

ठीक है, कि था: दो वर्गों की परिभाषा को देखो:

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 इसे इस तरह करते हैं बॉयलरप्लेट कोड के बहुत सारे (मेरे साथ भालू, मैं यह बताने के लिए आ रहा हूं कि बहुत जल्द के लिए एनोटेशन उपयोगी क्यों हैं)।क्या निर्भरता इंजेक्शन चौखटे आप के लिए क्या कर सकते हैं सार निर्माण इस तरह की रचना की वस्तुओं के लिए है और स्वचालित रूप से सभी निर्भरता इंजेक्षन, तो आप बस कार्य करें:

$soldier = Container::getInstance('Soldier'); 
$soldier->fight(); // ! weapon is already injected 

ठीक है, लेकिन Container पता के लिए उनकी निर्भरता जो एक Soldier वर्ग है है। इसलिए, अधिकांश सामान्य ढांचे एक्सएमएल का उपयोग कॉन्फ़िगरेशन प्रारूप के रूप में करते हैं। उदाहरण विन्यास:

<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; 
    } 

    ... 

तो, कोई एक्सएमएल आवश्यक डि कंटेनर के लिए Weapon को Soldier की निर्भरता को चिह्नित करने के।

फ्लो 3 इन बुनाई का उपयोग AOP के संदर्भ में भी करता है, जो "बुनाई" (विधि से पहले या बाद में व्यवहार इंजेक्शन का मतलब है) को चिह्नित करने के लिए।


जहां तक ​​मेरा संबंध है, मैं इन एनोटेशन के बारे में उपयोगिता के बारे में भी निश्चित नहीं हूं। मुझे नहीं पता कि यह चीजों को एक अलग फ़ाइल का उपयोग करने के बजाय PHP कोड में इस प्रकार की निर्भरताओं और सेटअप को "छिपाने" को आसान या खराब बनाता है।

मैंने ई काम किया। जी। स्प्रिंग.NET, NHibernate में और PHP में एक डी फ्रेमवर्क (FLOW3 नहीं) के साथ एक्सएमएल कॉन्फ़िगरेशन फ़ाइलों के आधार पर और यह नहीं कह सकता कि यह बहुत मुश्किल था। इन सेटअप फ़ाइलों को बनाए रखना भी ठीक था।

लेकिन शायद फ्लो 3 के साथ भविष्य की परियोजना विपरीत साबित करती है और एनोटेशन जाने का असली तरीका है।

+0

देखें, तो, एनोटेशन केवल विशेष फैक्ट्रीज़ के समान ही काम करता है, केवल विशेष PHPDoc की एक पंक्ति के माध्यम से। –

+0

मैंने यह जवाब चुना क्योंकि इसने एनोटेशन के संभावित उपयोगों में से एक का ठोस परीक्षण मामला दिया। –

+0

'व्यवहार' से हम परंपरागत 'चार गिरोह' 'व्यवहार' डिजाइन पैटर्न या कुछ खोने वाले के बारे में बात कर रहे हैं? – Snowcrash

6

वास्तव में यह क्या अच्छा है?

एनोटेशन मूल रूप से आपको व्यवहार को इंजेक्ट करने देते हैं और decoupling को बढ़ावा दे सकते हैं। एक उदाहरण सिद्धांत ओआरएम होगा। एनोटेशन के उपयोग के कारण आपको प्रोपेल ओआरएम के विपरीत एक सिद्धांत-विशिष्ट वर्ग से उत्तराधिकारी नहीं होना चाहिए।

आलसी लोडिंग गतिशील कोडिंग डीबग करना मुश्किल है?

दुर्भाग्य है कि ज्यादातर/डिजाइन पैटर्न, डेटा अनुवाद के रूप में ऐसी decoupling के सभी कार्यों की तरह एक पक्ष प्रभाव, आदि

हम्म है। मेरा मस्तिष्क अभी भी इसे परेशान नहीं कर रहा है। - hopeseekr

आप एक सिद्धांत वर्ग से विरासत में नहीं आया, तो आप चाहते सबसे अधिक संभावना निर्दिष्ट करने के लिए है कि एक विशेष संपत्ति रिकॉर्ड की आईडी है एक विन्यास फाइल की तरह, कुछ अन्य मेटाडाटा विनिर्देश उपयोग करने के लिए, है। उस स्थिति में, यह वाक्यविन्यास से बहुत दूर हटा दिया जाएगा जो एनोटेशन (मेटाडाटा) का वर्णन करता है।

+0

हम्म। मेरा मस्तिष्क अभी भी इसे परेशान नहीं कर रहा है। –

+0

@hopeseekr - मेरा नवीनतम संपादन –

2

पूर्णता के लिए, यहां एनोटेशन का उपयोग करने के साथ-साथ PHP भाषा का विस्तार करने के लिए दोनों का एक उदाहरण उदाहरण है, सभी एक ही फ़ाइल में।

ये 'वास्तविक' एनोटेशन हैं, जिसका अर्थ है भाषा स्तर पर घोषित किया गया है, और टिप्पणियों में छिपी नहीं है। इन तरह 'जावा' स्टाइल एनोटेशन का उपयोग करने का लाभ यह है कि टिप्पणियों को अनदेखा करने वाले पार्सर्स द्वारा उन्हें अनदेखा नहीं किया जा सकता है।

शीर्ष भाग, __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(); 

एक डि कंटेनर का उदाहरण (जो मूल रूप से जो भी कुछ भी नहीं एनोटेशन के साथ क्या करना है) के साथ रहते हुए, ऊपर दृष्टिकोण भी किसी भी निर्भरता इंजेक्शन लगाने का ख्याल रखना है, जो उपयोग करता है वर्ग कंस्ट्रक्टर्स संशोधित करने के लिए इस्तेमाल किया जा सकता घटकों के पूरी तरह से पारदर्शी। मूल्यांकन से पहले स्रोत कोड को संशोधित करने का दृष्टिकोण लगभग कस्टम जावा क्लासलोडर में 'बाइटकोड उपकरण' के बराबर है। (मैं AFAIK के बाद जावा का उल्लेख करता हूं, जहां एनोटेशन पहली बार पेश किए गए थे)।

इस विशेष उदाहरण की उपयोगिता यह है कि प्रत्येक विधि के लिए मैन्युअल रूप से कैशिंग कोड लिखने के बजाय, आप आसानी से कैश होने, दोहराए जाने वाले काम की मात्रा को कम करने और कोड को स्पष्ट करने के तरीके को चिह्नित कर सकते हैं। साथ ही, किसी भी एनोटेशन के प्रभाव रनटाइम पर चालू और बंद हो सकते हैं।

+0

यह बहुत दिलचस्प है –

+0

आप आईडीई कैसे इसका समर्थन कर सकते हैं? – tonix

0

phpDocumentor और आधुनिक आईडीई विधि पैरामीटर प्रकार (@param), वापसी मान (@return) आदि को निर्धारित करने के लिए एनोटेशन का उपयोग करते हैं।

PhpUnit परीक्षण समूह परीक्षणों के लिए एनोटेशन का उपयोग, निर्भरताओं को परिभाषित करें।

संबंधित मुद्दे