[ Index ]
 

Code source de Symfony 1.0.0

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/lib/vendor/phing/ -> IntrospectionHelper.php (source)

   1  <?php
   2  
   3  /*
   4   *  $Id: IntrospectionHelper.php 3076 2006-12-18 08:52:12Z fabien $
   5   *
   6   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   7   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   8   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   9   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  10   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  11   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  12   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  13   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  14   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  15   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  16   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  17   *
  18   * This software consists of voluntary contributions made by many individuals
  19   * and is licensed under the LGPL. For more information please see
  20   * <http://phing.info>.
  21   */
  22  
  23  include_once 'phing/types/Reference.php';
  24  include_once 'phing/types/Path.php';
  25  include_once 'phing/util/StringHelper.php';
  26  
  27  /**
  28   * Helper class that collects the methods that a task or nested element
  29   * holds to set attributes, create nested elements or hold PCDATA
  30   * elements.
  31   *
  32   *<ul>
  33   * <li><strong>SMART-UP INLINE DOCS</strong></li>
  34   * <li><strong>POLISH-UP THIS CLASS</strong></li>
  35   *</ul>
  36   *
  37   * @author    Andreas Aderhold <andi@binarycloud.com>
  38   * @author    Hans Lellelid <hans@xmpl.org>
  39   * @copyright © 2001,2002 THYRELL. All rights reserved
  40   * @version   $Revision: 1.19 $
  41   * @package   phing
  42   */
  43  class IntrospectionHelper {
  44  
  45  
  46  
  47      /** 
  48       * Holds the attribute setter methods.
  49       * 
  50       * @var array string[]
  51       */
  52      private $attributeSetters = array();
  53  
  54      /**  
  55       * Holds methods to create nested elements. 
  56       *
  57       * @var array string[]
  58       */
  59      private $nestedCreators = array();
  60  
  61      /**
  62       * Holds methods to store configured nested elements. 
  63       *
  64       * @var array string[]
  65       */
  66      private $nestedStorers = array();
  67      
  68      /**
  69       * Map from attribute names to nested types.
  70       */
  71      private $nestedTypes = array();
  72          
  73      /**
  74       * New idea in phing: any class can register certain
  75       * keys -- e.g. "task.current_file" -- which can be used in
  76       * task attributes, if supported.  In the build XML these
  77       * are referred to like this:
  78       *         <regexp pattern="\n" replace="%{task.current_file}"/>
  79       * In the type/task a listener method must be defined:
  80       *         function setListeningReplace($slot) {}
  81       * @var array string[]
  82        */
  83      private $slotListeners = array();
  84      
  85      /** 
  86       * The method to add PCDATA stuff. 
  87       *
  88       * @var string Method name of the addText (redundant?) method, if class supports it :)
  89       */
  90      private $methodAddText = null;
  91  
  92      /**
  93       * The Class that's been introspected.
  94       *
  95       * @var     object
  96       * @access  private
  97       */
  98      private $bean;
  99      
 100      /**
 101       * The cache of IntrospectionHelper classes instantiated by getHelper().
 102       * @var array IntrospectionHelpers[]
 103       */
 104      private static $helpers = array();
 105      
 106      /** 
 107       * Factory method for helper objects. 
 108       *
 109       * @param string $class The class to create a Helper for
 110       */
 111      public static function getHelper($class) {
 112          if (!isset(self::$helpers[$class])) {
 113              self::$helpers[$class] = new IntrospectionHelper($class);
 114          }
 115          return self::$helpers[$class];
 116      }
 117  
 118      /**
 119       * This function constructs a new introspection helper for a specific class.
 120       * 
 121       * This method loads all methods for the specified class and categorizes them
 122       * as setters, creators, slot listeners, etc.  This way, the setAttribue() doesn't
 123       * need to perform any introspection -- either the requested attribute setter/creator
 124       * exists or it does not & a BuildException is thrown.
 125       * 
 126       * @param string $bean The classname for this IH.
 127       */
 128      function __construct($class) {
 129      
 130          $this->bean = new ReflectionClass($class);
 131          
 132          //$methods = get_class_methods($bean);
 133          foreach($this->bean->getMethods() as $method) {
 134          
 135              if ($method->isPublic()) {                
 136              
 137                  // We're going to keep case-insensitive method names
 138                  // for as long as we're allowed :)  It makes it much
 139                  // easier to map XML attributes to PHP class method names.
 140                  $name = strtolower($method->getName());
 141                  
 142                  // There are a few "reserved" names that might look like attribute setters
 143                  // but should actually just be skipped.  (Note: this means you can't ever
 144                  // have an attribute named "location" or "tasktype" or a nested element named "task".)
 145                  if ($name === "setlocation" || $name === "settasktype" || $name === "addtask") {
 146                      continue;
 147                  }
 148                  
 149                  if ($name === "addtext") {
 150                      
 151                      $this->methodAddText = $method;
 152                      
 153                  } elseif (strpos($name, "setlistening") === 0) {
 154                      
 155                      // Phing supports something unique called "RegisterSlots"
 156                      // These are dynamic values that use a basic slot system so that
 157                      // classes can register to listen to specific slots, and the value
 158                      // will always be grabbed from the slot (and never set in the project
 159                      // component).  This is useful for things like tracking the current
 160                      // file being processed by a filter (e.g. AppendTask sets an append.current_file
 161                      // slot, which can be ready by the XSLTParam type.)
 162                      
 163                      if (count($method->getParameters()) !== 1) {
 164                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter.");
 165                      }
 166                                                  
 167                      $this->slotListeners[$name] = $method;
 168                      
 169                  } elseif (strpos($name, "set") === 0) {
 170                      
 171                      // A standard attribute setter.
 172                      
 173                      if (count($method->getParameters()) !== 1) {
 174                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take exactly one parameter.");
 175                      }
 176                      
 177                      $this->attributeSetters[$name] = $method;
 178                      
 179                  } elseif (strpos($name, "create") === 0) {                            
 180                      
 181                      if (count($method->getParameters()) > 0) {
 182                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() may not take any parameters.");
 183                      }
 184                      
 185                      // Because PHP doesn't support return types, we are going to do
 186                      // two things here to guess return type:
 187                      //     1) parse comments for an explicit value
 188                      //     2) if that fails, assume that the part of the method after "create"
 189                      //    is the name of the return type (in many cases it is not)
 190                      
 191                      // This isn't super important -- i.e. we're not instantaiting classes
 192                      // based on this information.  It's more just so that IntrospectionHelper
 193                      // can keep track of all the nested types -- and provide more helpful
 194                      // exception messages, etc.
 195                                  
 196                      preg_match('/@return[\s]+([\w]+)/', $method->getDocComment(), $matches);
 197                      if (!empty($matches[1]) && class_exists($matches[1], false)) {
 198                          $this->nestedTypes[$name] = $matches[1];
 199                      } else {                    
 200                          // assume that method createEquals() creates object of type "Equals"
 201                          // (that example would be false, of course)                    
 202                          $this->nestedTypes[$name] = $this->getPropertyName($name, "create");
 203                      }
 204                      
 205                      $this->nestedCreators[$name] = $method;
 206                      
 207                  } elseif (strpos($name, "addconfigured") === 0) {
 208                      
 209                      // *must* use class hints if using addConfigured ...
 210                      
 211                      // 1 param only
 212                      $params = $method->getParameters();
 213                      
 214                      if (count($params) < 1) {
 215                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter.");
 216                      }
 217                      
 218                      if (count($params) > 1) {
 219                          $this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)");
 220                      }
 221                      
 222                      $classname = null;
 223                      
 224                      if (($hint = $params[0]->getClass()) !== null) { 
 225                          $classname = $hint->getName();    
 226                      }                    
 227                      
 228                      if ($classname === null) {
 229                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter.");
 230                      }
 231                          
 232                      $this->nestedTypes[$name] = $classname;
 233                  
 234                      $this->nestedStorers[$name] = $method;
 235                      
 236                  } elseif (strpos($name, "add") === 0) {
 237                      
 238                      // *must* use class hints if using add ...
 239                      
 240                      // 1 param only
 241                      $params = $method->getParameters();
 242                      if (count($params) < 1) {
 243                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() must take at least one parameter.");
 244                      }
 245                      
 246                      if (count($params) > 1) {
 247                          $this->warn($method->getDeclaringClass()->getName()."::".$method->getName()."() takes more than one parameter. (IH only uses the first)");
 248                      }
 249  
 250                      $classname = null;
 251                      
 252                      if (($hint = $params[0]->getClass()) !== null) { 
 253                          $classname = $hint->getName();    
 254                      }                    
 255                      
 256                      // we don't use the classname here, but we need to make sure it exists before
 257                      // we later try to instantiate a non-existant class
 258                      if ($classname === null) {
 259                          throw new BuildException($method->getDeclaringClass()->getName()."::".$method->getName()."() method MUST use a class hint to indicate the class type of parameter.");
 260                      }
 261                  
 262                      $this->nestedCreators[$name] = $method;
 263                  } 
 264              } // if $method->isPublic()        
 265          } // foreach        
 266      }
 267  
 268  
 269      /** Sets the named attribute. */
 270      function setAttribute(Project $project, $element, $attributeName, &$value) {
 271          
 272          // we want to check whether the value we are setting looks like
 273          // a slot-listener variable:  %{task.current_file}
 274          //
 275          // slot-listener variables are not like properties, in that they cannot be mixed with
 276          // other text values.  The reason for this disparity is that properties are only
 277          // set when first constructing objects from XML, whereas slot-listeners are always dynamic.
 278          //
 279          // This is made possible by PHP5 (objects automatically passed by reference) and PHP's loose
 280          // typing.
 281          
 282          if (StringHelper::isSlotVar($value)) {
 283              
 284              $as = "setlistening" . strtolower($attributeName);
 285  
 286              if (!isset($this->slotListeners[$as])) {
 287                  $msg = $this->getElementName($project, $element) . " doesn't support a slot-listening '$attributeName' attribute.";
 288                  throw new BuildException($msg);
 289              }
 290              
 291              $method = $this->slotListeners[$as];
 292              
 293              $key = StringHelper::slotVar($value);
 294              $value = Register::getSlot($key); // returns a RegisterSlot object which will hold current value of that register (accessible using getValue())
 295              
 296          } else {
 297              
 298              // Traditional value options
 299              
 300              $as = "set".strtolower($attributeName);
 301              
 302              if (!isset($this->attributeSetters[$as])) {
 303                  $msg = $this->getElementName($project, $element) . " doesn't support the '$attributeName' attribute.";
 304                  throw new BuildException($msg);
 305              }
 306              
 307              $method = $this->attributeSetters[$as];            
 308              
 309              if ($as == "setrefid") {            
 310                  $value = new Reference($value);
 311              } else {
 312              
 313                  // decode any html entities in string
 314                  $value = html_entity_decode($value);                
 315                  
 316                  // value is a string representation of a boolean type,
 317                  // convert it to primitive
 318                  if (StringHelper::isBoolean($value)) {
 319  
 320                      $value = StringHelper::booleanValue($value);
 321                  }
 322                  
 323                  // does method expect a PhingFile object? if so, then 
 324                  // pass a project-relative file.
 325                  $params = $method->getParameters();
 326  
 327                  $classname = null;
 328                  
 329                  if (($hint = $params[0]->getClass()) !== null) { 
 330                      $classname = $hint->getName();    
 331                  }
 332                  
 333                  // there should only be one param; we'll just assume ....
 334                  if ($classname !== null) {
 335                      switch(strtolower($classname)) {
 336                          case "phingfile":
 337                              $value = $project->resolveFile($value);
 338                              break;
 339                          case "path":
 340                              $value = new Path($project, $value);
 341                              break;
 342                          case "reference":
 343                              $value = new Reference($value);
 344                              break;            
 345                          // any other object params we want to support should go here ...
 346                      }
 347                      
 348                  } // if hint !== null
 349                  
 350              } // if not setrefid
 351              
 352          } // if is slot-listener
 353          
 354          try {
 355              $project->log("    -calling setter ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", PROJECT_MSG_DEBUG);
 356              $method->invoke($element, $value);
 357          } catch(Exception $exc) {
 358              throw new BuildException($exc);
 359          }
 360          
 361      }
 362  
 363      /** Adds PCDATA areas.*/
 364      function addText(Project $project, $element, $text) {
 365          if ($this->methodAddText === null) {
 366              $msg = $this->getElementName($project, $element)." doesn't support nested text data.";
 367              throw new BuildException($msg);
 368          }        
 369          try {
 370              $method = $this->methodAddText;
 371              $method->invoke($element, $text);
 372          } catch (Exception $exc) {
 373              throw new BuildException($exc);
 374          }
 375      }
 376  
 377      /**
 378       * Creates a named nested element. 
 379       * 
 380       * Valid creators can be in the form createFoo() or addFoo(Bar).
 381       * @return object Returns the nested element.
 382       * @throws BuildException
 383       */
 384      function createElement(Project $project, $element, $elementName) {
 385      
 386          $addMethod = "add".strtolower($elementName);
 387          $createMethod = "create".strtolower($elementName);
 388          $nestedElement = null;
 389          
 390          if (isset($this->nestedCreators[$createMethod])) {
 391              
 392              $method = $this->nestedCreators[$createMethod];
 393               try { // try to invoke the creator method on object
 394                  $project->log("    -calling creator ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", PROJECT_MSG_DEBUG);
 395                  $nestedElement = $method->invoke($element);
 396              } catch (Exception $exc) {
 397                  throw new BuildException($exc);
 398              }            
 399              
 400          } elseif (isset($this->nestedCreators[$addMethod])) {            
 401              
 402              $method = $this->nestedCreators[$addMethod];
 403              
 404              // project components must use class hints to support the add methods
 405              
 406              try { // try to invoke the adder method on object
 407              
 408                  $project->log("    -calling adder ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", PROJECT_MSG_DEBUG);
 409                  // we've already assured that correct num of params
 410                  // exist and that method is using class hints                
 411                  $params = $method->getParameters();
 412  
 413                  $classname = null;
 414              
 415                  if (($hint = $params[0]->getClass()) !== null) { 
 416                      $classname = $hint->getName();    
 417                  }                
 418                  
 419                  // create a new instance of the object and add it via $addMethod                
 420                  $nestedElement = new $classname();
 421                  
 422                  $method->invoke($element, $nestedElement);
 423                                  
 424              } catch (Exception $exc) {
 425                  throw new BuildException($exc);
 426              }
 427          } else {
 428              $msg = $this->getElementName($project, $element) . " doesn't support the '$elementName' creator/adder.";
 429              throw new BuildException($msg);
 430          }                                
 431          
 432          if ($nestedElement instanceof ProjectComponent) {
 433              $nestedElement->setProject($project);
 434          }
 435          
 436          return $nestedElement;
 437      }
 438  
 439      /**
 440       * Creates a named nested element.
 441       * @return void
 442       * @throws BuildException
 443       */
 444      function storeElement($project, $element, $child, $elementName = null) {
 445      
 446          if ($elementName === null) {
 447              return;
 448          }
 449          
 450          $storer = "addconfigured".strtolower($elementName);
 451            
 452          if (isset($this->nestedStorers[$storer])) {
 453              
 454              $method = $this->nestedStorers[$storer];
 455              
 456              try {                                
 457                  $project->log("    -calling storer ".$method->getDeclaringClass()->getName()."::".$method->getName()."()", PROJECT_MSG_DEBUG);                    
 458                  $method->invoke($element, $child);            
 459              } catch (Exception $exc) {
 460                  throw new BuildException($exc);
 461              }
 462          }
 463          
 464      }
 465  
 466      /** Does the introspected class support PCDATA? */
 467      function supportsCharacters() {
 468          return ($this->methodAddText !== null);
 469      }
 470  
 471      /** Return all attribues supported by the introspected class. */
 472      function getAttributes() {
 473          $attribs = array();
 474          foreach (array_keys($this->attributeSetters) as $setter) {
 475              $attribs[] =$this->getPropertyName($setter, "set");
 476          }
 477          return $attribs;
 478      }
 479  
 480      /** Return all nested elements supported by the introspected class. */
 481      function getNestedElements() {
 482          return $this->nestedTypes;
 483      }
 484      
 485      /**
 486       * Get the the name for an element.
 487       * When possible the full classnam (phing.tasks.system.PropertyTask) will
 488       * be returned.  If not available (loaded in taskdefs or typedefs) then the
 489       * XML element name will be returned.
 490       *
 491       * @param Project $project
 492       * @param object $element The Task or type element.
 493       * @return string Fully qualified class name of element when possible.
 494       */
 495      function getElementName(Project $project, $element) {
 496         
 497            $taskdefs = $project->getTaskDefinitions();
 498          $typedefs = $project->getDataTypeDefinitions();
 499          
 500          // check if class of element is registered with project (tasks & types)        
 501          // most element types don't have a getTag() method
 502          $elClass = get_class($element);
 503          
 504          if (!in_array('getTag', get_class_methods($elClass))) {
 505                  // loop through taskdefs and typesdefs and see if the class name
 506                  // matches (case-insensitive) any of the classes in there
 507                  foreach(array_merge($taskdefs, $typedefs) as $elName => $class) {
 508                      if (0 === strcasecmp($elClass, StringHelper::unqualify($class))) {
 509                          return $class;
 510                      }
 511                  }
 512                  return "$elClass (unknown)";
 513          } else {
 514              // ->getTag() method does exist, so use it
 515              $elName = $element->getTag();
 516              if (isset($taskdefs[$elName])) {
 517                  return $taskdefs[$elName];
 518              } elseif (isset($typedefs[$elName])) {
 519  
 520                  return $typedefs[$elName];
 521              } else {
 522                  return "$elName (unknown)";
 523              }
 524          }        
 525      }
 526  
 527      /** extract the name of a property from a method name - subtracting  a given prefix. */
 528      function getPropertyName($methodName, $prefix) {
 529          $start = strlen($prefix);
 530          return strtolower(substr($methodName, $start));
 531      }
 532      
 533      /**
 534       * Prints warning message to screen if -debug was used.
 535       */
 536      function warn($msg) {
 537          if (Phing::getMsgOutputLevel() === PROJECT_MSG_DEBUG) {
 538              print("[IntrospectionHelper] " . $msg . "\n");
 539          }
 540      }
 541  
 542  }


Généré le : Fri Mar 16 22:42:14 2007 par Balluche grâce à PHPXref 0.7