Changeset 2642

Show
Ignore:
Timestamp:
08/05/08 13:23:02 (5 months ago)
Author:
impl
Message:

branches/david-xml_only_config_system (refs #519): Refactor AgaviXmlConfigParser? to follow new execution flow (see http://trac.agavi.org/wiki/Cleanup/Configuration/1.0#Processflow); implement AgaviConfigHandlersConfigHandler? as an XML configuration handler; bugfixes

Location:
branches/david-xml_only_config_system/src/config
Files:
2 added
8 modified

Legend:

Unmodified
Added
Removed
  • branches/david-xml_only_config_system/src/config/AgaviConfigCache.class.php

    r2641 r2642  
    311311      'parameters' => array( 
    312312      ), 
     313      'transformations' => array( 
     314        AgaviXmlConfigParser::STAGE_SINGLE => array( 
     315          $agaviDir . '/config/xsl/config_handlers.xsl', 
     316        ), 
     317        AgaviXmlConfigParser::STAGE_COMPILATION => array( 
     318        ), 
     319      ), 
    313320      'validations' => array( 
    314         AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 
    315           $agaviDir . '/config/xsd/config_handlers.xsd', 
     321        AgaviXmlConfigParser::STAGE_SINGLE => array( 
     322          AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array( 
     323          ), 
     324          AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array( 
     325            AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG => array( 
     326              $agaviDir . '/config/rng/config_handlers.rng', 
     327            ), 
     328          ), 
     329        ), 
     330        AgaviXmlConfigParser::STAGE_COMPILATION => array( 
     331          AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(), 
     332          AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array() 
    316333        ), 
    317334      ), 
  • branches/david-xml_only_config_system/src/config/AgaviConfigHandlersConfigHandler.class.php

    r2632 r2642  
    3030 * @version    $Id$ 
    3131 */ 
    32 class AgaviConfigHandlersConfigHandler extends AgaviConfigHandler 
     32class AgaviConfigHandlersConfigHandler extends AgaviXmlConfigHandler 
    3333{ 
     34  const NAMESPACE = 'http://agavi.org/agavi/config/item/config_handlers/1.0'; 
     35   
    3436  /** 
    3537   * Execute this configuration handler. 
    3638   * 
    37    * @param      string An absolute filesystem path to a configuration file. 
    38    * @param      string An optional context in which we are currently running. 
     39   * @param      AgaviXmlConfigDomDocument The document to handle. 
    3940   * 
    4041   * @return     string Data to be written to a cache file. 
     
    4748   * 
    4849   * @author     Dominik del Bondio <ddb@bitxtender.com> 
     50   * @author     Noah Fontes <noah.fontes@bitextender.com> 
    4951   * @since      0.11.0 
    5052   */ 
    51   public function execute($config, $context = null) 
     53  public function execute(AgaviXmlConfigDomDocument $document) 
    5254  { 
    53     // parse the config file 
    54     $configurations = $this->orderConfigurations(AgaviConfigCache::parseConfig($config, false, $this->getValidationFile(), $this->parser)->configurations, AgaviConfig::get('core.environment')); 
    55                                                                                                                                                                               
     55    // set up our default namespace 
     56    $document->setDefaultNamespace(self::NAMESPACE, 'config_handlers'); 
     57     
    5658    // init our data arrays 
    5759    $handlers = array(); 
    5860     
    59     foreach($configurations as $cfg) { 
    60       if(!isset($cfg->handlers)) { 
     61    foreach($document->getConfigurationElements() as $configuration) { 
     62      if(!$configuration->hasChildren('handlers')) { 
    6163        continue; 
    6264      } 
    6365       
    6466      // let's do our fancy work 
    65       foreach($cfg->handlers as $handler) { 
     67      foreach($configuration->getChildren('handlers') as $handler) { 
    6668        $pattern = $handler->getAttribute('pattern'); 
    6769         
     
    7072        $class = $handler->getAttribute('class'); 
    7173         
    72         $transformations = array(); 
    73         if(isset($handler->transformations)) { 
    74           foreach($handler->transformations as $transformation) { 
    75             $transformationPath = AgaviToolkit::literalize($transformation->getValue()); 
    76             $transformations[] = $transformationPath; 
     74        $transformations = array( 
     75          AgaviXmlConfigParser::STAGE_SINGLE => array(), 
     76          AgaviXmlConfigParser::STAGE_COMPILATION => array(), 
     77        ); 
     78        if($handler->hasChildren('transformations')) { 
     79          foreach($handler->getChildren('transformations') as $transformation) { 
     80            $path = AgaviToolkit::literalize($transformation->getValue()); 
     81            $for = $transformation->getAttribute('for', AgaviXmlConfigParser::STAGE_SINGLE); 
     82            $transformations[$for][] = $path; 
    7783          } 
    7884        } 
    7985         
    8086        $validations = array( 
    81           AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG    => array( 
     87          AgaviXmlConfigParser::STAGE_SINGLE => array( 
     88            AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array( 
     89              AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG => array( 
     90              ), 
     91              AgaviXmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array( 
     92              ), 
     93              AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 
     94              ), 
     95            ), 
     96            AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array( 
     97              AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG => array( 
     98              ), 
     99              AgaviXmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array( 
     100              ), 
     101              AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 
     102              ), 
     103            ), 
    82104          ), 
    83           AgaviXmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array( 
    84           ), 
    85           AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA  => array( 
     105          AgaviXmlConfigParser::STAGE_COMPILATION => array( 
     106            AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array( 
     107              AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG => array( 
     108              ), 
     109              AgaviXmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array( 
     110              ), 
     111              AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 
     112              ), 
     113            ), 
     114            AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array( 
     115              AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG => array( 
     116              ), 
     117              AgaviXmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array( 
     118              ), 
     119              AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 
     120              ), 
     121            ), 
    86122          ), 
    87123        ); 
    88         // legacy: via attribute 
    89         if($handler->hasAttribute('validate')) { 
    90           $validations[AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][] = AgaviToolkit::literalize($handler->getAttribute('validate')); 
    91         } 
    92         if(isset($handler->validations)) { 
    93           foreach($handler->validations as $validation) { 
    94             $validationPath = AgaviToolkit::literalize($validation->getValue()); 
     124        if($handler->hasChildren('validations')) { 
     125          foreach($handler->getChildren('validations') as $validation) { 
     126            $path = AgaviToolkit::literalize($validation->getValue()); 
     127            $type = null; 
    95128            if(!$validation->hasAttribute('type')) { 
    96               $validationType = $this->guessValidationType($validationPath); 
     129              $type = $this->guessValidationType($path); 
    97130            } else { 
    98               $validationType = $validation->getAttribute('type'); 
     131              $type = $validation->getAttribute('type'); 
    99132            } 
    100             $validations[$validationType][] = $validationPath; 
     133            $for = $validation->getAttribute('for', AgaviXmlConfigParser::STAGE_SINGLE); 
     134            $step = $validation->getAttribute('step', AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER); 
     135            $validations[$for][$step][$type][] = $path; 
    101136          } 
    102137        } 
    103138         
     139        $handlers[$category] = isset($handlers[$category]) 
     140          ? $handlers[$category] 
     141          : array( 
     142            'parameters' => array(), 
     143            ); 
    104144        $handlers[$category] = array( 
    105145          'class' => $class, 
    106           'parameters' => $this->getItemParameters($handler), 
     146          'parameters' => $handler->getAgaviParameters($handlers[$category]['parameters']), 
    107147          'transformations' => $transformations, 
    108148          'validations' => $validations, 
  • branches/david-xml_only_config_system/src/config/AgaviConfigParser.class.php

    r2641 r2642  
    5252    $parser = new AgaviXmlConfigParser($config, AgaviConfig::get('core.environment'), null); 
    5353     
    54     $validation = array(); 
     54    $validation = array( 
     55      AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(), 
     56      AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array( 
     57        AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array(), 
     58      ), 
     59    ); 
    5560    if($validationFile !== null) { 
    56       $validation[AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA] = array($validationFile); 
     61      $validation[AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER][AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][] = array($validationFile); 
    5762    } 
    5863    $doc = $parser->execute(array(), $validation); 
  • branches/david-xml_only_config_system/src/config/AgaviFactoryConfigHandler.class.php

    r2641 r2642  
    3838   * Execute this configuration handler. 
    3939   * 
    40    * @param      DOMDocument The document to parse. 
     40   * @param      AgaviXmlConfigDomDocument The document to parse. 
    4141   * 
    4242   * @return     string Data to be written to a cache file. 
     
    4646   * 
    4747   * @author     David Zülke <dz@bitxtender.com> 
     48   * @author     Dominik del Bondio <ddb@bitxtender.com> 
    4849   * @author     Noah Fontes <noah.fontes@bitextender.com> 
    4950   * @since      0.11.0 
    5051   */ 
    51   public function execute(DOMDocument $doc) 
     52  public function execute(AgaviXmlConfigDomDocument $document) 
    5253  { 
    5354    // set up our default namespace 
    54     $doc->setDefaultNamespace(self::NAMESPACE, 'factories'); 
    55      
    56     $config = $doc->documentURI; 
     55    $document->setDefaultNamespace(self::NAMESPACE, 'factories'); 
     56     
     57    $config = $document->documentURI; 
    5758    $data = array(); 
    5859     
     
    192193    ); 
    193194     
    194     foreach($doc->getConfigurationElements() as $configuration) { 
     195    foreach($document->getConfigurationElements() as $configuration) { 
    195196      foreach($factories as $factory => $info) { 
    196197        if(is_array($info) && $info['required'] && $configuration->hasChild($factory)) { 
  • branches/david-xml_only_config_system/src/config/AgaviIXmlConfigHandler.interface.php

    r2468 r2642  
    4949   * Execute this configuration handler. 
    5050   * 
    51    * @param      DOMDocument The document to parse. 
     51   * @param      AgaviXmlConfigDomDocument The document to parse. 
    5252   * 
    5353   * @return     string Data to be written to a cache file. 
     
    5959   * @since      0.11.0 
    6060   */ 
    61   public function execute(DOMDocument $doc); 
     61  public function execute(AgaviXmlConfigDomDocument $document); 
    6262} 
    6363 
  • branches/david-xml_only_config_system/src/config/AgaviXmlConfigParser.class.php

    r2641 r2642  
    2323 * 
    2424 * @author     David Zülke <dz@bitxtender.com> 
     25 * @author     Noah Fontes <noah.fontes@bitextender.com> 
    2526 * @copyright  Authors 
    2627 * @copyright  The Agavi Project 
     
    5455  const NAMESPACE_XSL_1999 = 'http://www.w3.org/1999/XSL/Transform'; 
    5556   
     57  const STAGE_SINGLE = 'single'; 
     58   
     59  const STAGE_COMPILATION = 'compilation'; 
     60   
     61  const STEP_TRANSFORMATIONS_BEFORE = 'before-transformations'; 
     62   
     63  const STEP_TRANSFORMATIONS_AFTER = 'after-transformations'; 
     64   
    5665  /** 
    5766   * @var        array A list of XML namespaces for Agavi configuration files as 
     
    179188    $xpath->registerNamespace('agavi_annotation_latest', self::NAMESPACE_AGAVI_ANNOTATION_LATEST); 
    180189  } 
    181    
     190                                                    
    182191  /** 
    183192   * @param      string An absolute filesystem path to a configuration file. 
     
    191200   * @author     David Zülke <dz@bitxtender.com> 
    192201   * @author     Dominik del Bondio <ddb@bitxtender.com> 
     202   * @author     Noah Fontes <noah.fontes@bitextender.com> 
    193203   * @since      0.11.0 
    194204   */ 
     
    200210    $nextPath = $path; 
    201211    while($nextPath !== null) { 
     212      // run the single stage parser 
    202213      $parser = new AgaviXmlConfigParser($nextPath, $environment, $context); 
    203        
    204       $doc = $parser->execute($transformationInfo, $validationInfo); 
     214      $doc = $parser->execute($transformationInfo[self::STAGE_SINGLE], $validationInfo[self::STAGE_SINGLE]); 
     215       
    205216      // put the new document in the list 
    206217      $docs[] = $doc; 
     
    271282        } 
    272283      } 
     284       
     285      // run the compilation stage parser 
     286      self::executeCompilation($retval, $environment, $context, $transformationInfo[self::STAGE_COMPILATION], $validationInfo[self::STAGE_COMPILATION]); 
    273287    } else { 
    274288      // it's not an agavi config file. just pass it through then 
     
    362376   * @author     David Zülke <dz@bitxtender.com> 
    363377   * @author     Dominik del Bondio <ddb@bitxtender.com> 
     378   * @author     Noah Fontes <noah.fontes@bitextender.com> 
    364379   * @since      0.11.0 
    365380   */ 
    366381  public function execute(array $transformationInfo = array(), array $validationInfo = array()) 
    367382  { 
    368     // prepare the doc (resolve xincludes, validate against XSI declarations and evaluate <configuration> attributes for environment and context targeting) 
    369     $this->prepare(); 
     383    // resolve xincludes 
     384    self::xinclude($this->doc); 
     385     
     386    // validate XMLSchema-instance declarations 
     387    self::validateXsi($this->doc); 
     388     
     389    // mark document for merging 
     390    self::match($this->doc, $this->environment, $this->context); 
     391     
     392    // validate pre-transformation 
     393    self::validate($this->doc, $this->environment, $this->context, $validationInfo[AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE]); 
     394     
     395    if(!AgaviConfig::get('core.skip_config_transformations', false)) { 
     396      // run inline transformations 
     397      self::transformProcessingInstructions($this->doc); 
     398       
     399      // perform XSL transformations 
     400      $this->doc = self::transform($this->doc, $this->environment, $this->context, $transformationInfo); 
     401       
     402      // resolve xincludes again, since transformations may have introduced some 
     403      self::xinclude($this->doc); 
     404    } 
     405     
     406    // validate post-transformation 
     407    self::validate($this->doc, $this->environment, $this->context, $validationInfo[AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER]); 
     408     
     409    // clean up the document 
     410    self::cleanup($this->doc); 
     411     
     412    return $this->doc; 
     413  } 
     414   
     415  /** 
     416   * Executes the parser for a compilation document. 
     417   * 
     418   * @param      AgaviXmlConfigDomDocument The document to act upon. 
     419   * @param      string The environment name. 
     420   * @param      string The context name. 
     421   * @param      array An array of XSL paths for transformation. 
     422   * @param      array An associative array of validation information. 
     423   * 
     424   * @author     Noah Fontes <noah.fontes@bitextender.com> 
     425   * @since      1.0.0 
     426   */ 
     427  public static function executeCompilation(AgaviXmlConfigDomDocument $document, $environment, $context, array $transformationInfo = array(), array $validationInfo = array()) 
     428  { 
     429    // resolve xincludes 
     430    self::xinclude($document); 
     431     
     432    // validate pre-transformation 
     433    self::validate($document, $environment, $context, $validationInfo[AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE]); 
    370434     
    371435    if(!AgaviConfig::get('core.skip_config_transformations', false)) { 
    372436      // perform XSL transformations 
    373       $this->transform($transformationInfo); 
    374     } 
    375      
    376     // validate against WXS/RNG/SCH 
    377     $this->validate($validationInfo); 
    378      
    379     // clean up the document 
    380     $this->cleanup(); 
    381      
    382     return $this->doc; 
    383   } 
    384    
    385   /** 
    386    * Prepare the configuration file: resolve XIncludes, validate against XML 
    387    * Schema instances declared on the document, and set processing information 
    388    * flags on <configuration> elements. 
    389    * 
    390    * @author     David Zülke <dz@bitxtender.com> 
    391    * @author     Noah Fontes <noah.fontes@bitextender.com> 
    392    * @since      0.11.0 
    393    */ 
    394   public function prepare() 
     437      $document = self::transform($document, $environment, $context, $transformationInfo); 
     438       
     439      // resolve xincludes again, since transformations may have introduced some 
     440      self::xinclude($document); 
     441    } 
     442     
     443    // validate post-transformation 
     444    self::validate($document, $environment, $context, $validationInfo[AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER]); 
     445  } 
     446   
     447  /** 
     448   * Resolve xinclude directives on a given document. 
     449   * 
     450   * @param      AgaviXmlConfigDomDocument The document to act upon. 
     451   * 
     452   * @author     David Zülke <dz@bitxtender.com> 
     453   * @author     Noah Fontes <noah.fontes@bitextender.com> 
     454   * @since      1.0.0 
     455   */ 
     456  public static function xinclude(AgaviXmlConfigDomDocument $document) 
    395457  { 
    396458    // replace %lala% directives in XInclude href attributes 
    397     foreach($this->doc->getElementsByTagNameNS('http://www.w3.org/2001/XInclude', '*') as $element) { 
     459    foreach($document->getElementsByTagNameNS('http://www.w3.org/2001/XInclude', '*') as $element) { 
    398460      if($element->hasAttribute('href')) { 
    399461        $attribute = $element->getAttributeNode('href'); 
     
    406468    // perform xincludes 
    407469    try { 
    408       $this->doc->xinclude(); 
     470      $document->xinclude(); 
    409471    } catch(DOMException $dome) { 
    410       throw new AgaviParseException(sprintf('Configuration file "%s" could not be parsed: %s', $this->path, $dome->getMessage())); 
    411     } 
    412      
     472      throw new AgaviParseException(sprintf('Configuration file "%s" could not be parsed: %s', $document->documentURI, $dome->getMessage())); 
     473    } 
     474     
     475    /* 
    413476    // remove all xml:base attributes inserted by XIncludes 
    414     $nodes = $this->doc->getXpath()->query('//@xml:base', $this->doc); 
     477    $nodes = $document->getXpath()->query('//@xml:base', $document); 
    415478    foreach($nodes as $node) { 
    416479      $node->ownerElement->removeAttributeNode($node); 
    417480    } 
     481    */ 
    418482     
    419483    // necessary due to a PHP bug, see http://trac.agavi.org/ticket/621 and http://bugs.php.net/bug.php?id=43364 
    420484    if(version_compare(PHP_VERSION, '5.2.6', '<')) { 
    421485      // we need to remember the document URI and restore it, just in case 
    422       $documentUri = $this->doc->documentURI; 
     486      $documentUri = $document->documentURI; 
    423487      // reload, and all is good 
    424       $this->doc->loadXML($this->doc->saveXML()); 
    425       $this->doc->documentURI = $documentUri; 
    426     } 
    427      
    428     // next, find (and validate against) XML schema instance declarations 
    429     $sources = array(); 
    430     if($this->doc->documentElement->hasAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { 
    431       // find locations. for namespaces, they are space separated pairs of a namespace URI and a schema location 
    432       $locations = preg_split('/\s+/', $this->doc->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')); 
    433       for($i = 1; $i < count($locations); $i = $i + 2) { 
    434         $sources[] = $locations[$i]; 
    435       } 
    436     } 
    437     // no namespace? then it's only one schema location in this attribute 
    438     if($this->doc->documentElement->hasAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'noNamespaceSchemaLocation')) { 
    439       $sources[] = $this->doc->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'noNamespaceSchemaLocation'); 
    440     } 
    441     if($sources) { 
    442       // we have instances to validate against... 
    443       $schemas = array(); 
    444       foreach($sources as &$source) { 
    445         // so for each location, we need to grab the file and validate against this grabbed source code, as libxml often has a hard time retrieving stuff over HTTP 
    446         $source = AgaviToolkit::expandDirectives($source); 
    447         if(parse_url($source, PHP_URL_SCHEME) === null && !AgaviToolkit::isPathAbsolute($source)) { 
    448           // the schema location is relative to the XML file 
    449           $source = dirname($this->path) . DIRECTORY_SEPARATOR . $source; 
    450         } 
    451         $schema = @file_get_contents($source); 
    452         if($schema === false) { 
    453           throw new AgaviUnreadableException(sprintf('XML Schema validation file "%s" for configuration file "%s" does not exist or is unreadable', $source, $this->path)); 
    454         } 
    455         $schemas[] = $schema; 
    456       } 
    457       // now validate them all 
    458       $this->validateXmlschemaSource($schemas); 
    459     } 
    460      
    461     if($this->doc->isAgaviConfiguration()) { 
     488      $document->loadXML($document->saveXML()); 
     489      $document->documentURI = $documentUri; 
     490    } 
     491  } 
     492   
     493  /** 
     494   * Annotate the document with matched attributes against each configuration 
     495   * element that matches the given context and environment. 
     496   * 
     497   * @param      AgaviXmlConfigDomDocument The document to act upon. 
     498   * @param      string The environment name. 
     499   * @param      string The context name. 
     500   * 
     501   * @author     David Zülke <dz@bitxtender.com> 
     502   * @author     Noah Fontes <noah.fontes@bitextender.com> 
     503   * @since      1.0.0 
     504   */ 
     505  public static function match(AgaviXmlConfigDomDocument $document, $environment, $context) 
     506  { 
     507    if($document->isAgaviConfiguration()) { 
    462508      // it's an agavi config, so we need to set "matched" flags on all <configuration> elements where "context" and "environment" attributes match the values below 
    463509      $testAttributes = array( 
    464         'context' => $this->context, 
    465         'environment' => $this->environment, 
     510        'context' => $context, 
     511        'environment' => $environment, 
    466512      ); 
    467513       
    468       foreach($this->doc->getConfigurationElements() as $configuration) { 
     514      foreach($document->getConfigurationElements() as $configuration) { 
    469515        // assume that the element counts as matched, in case it doesn't have "context" or "environment" attributes 
    470516        $matched = true; 
     
    486532   * and given stylesheets. 
    487533   * 
     534   * @param      AgaviXmlConfigDomDocument The document to act upon. 
     535   * @param      string The environment name. 
     536   * @param      string The context name. 
    488537   * @param      array  An array of transformation information. 
    489538   * 
     539   * @return     AgaviXmlConfigDomDocument The transformed document. 
     540   * 
    490541   * @author     David Zülke <dz@bitxtender.com> 
    491542   * @author     Noah Fontes <noah.fontes@bitextender.com> 
    492543   * @since      0.11.0 
    493544   */ 
    494   public function transform(array $transformationInfo = array()) 
     545  public static function transform(AgaviXmlConfigDomDocument $document, $environment, $context, array $transformationInfo = array()) 
    495546  { 
    496547    $transformations = array(); 
    497548     
    498     $xpath = $this->doc->getXpath(); 
     549    // loop over all the paths we found and load the files 
     550    foreach($transformationInfo as $href) { 
     551      try { 
     552        $xsl = new AgaviXmlConfigDomDocument(); 
     553        $xsl->load($href); 
     554      } catch(DOMException $dome) { 
     555        throw new AgaviParseException(sprintf('Configuration file "%s" could not be parsed: Could not load XSL stylesheet "%s": %s', $document->documentURI, $href, $dome->getMessage())); 
     556      } 
     557       
     558      // add them to the list of transformations to be done 
     559      $transformations[] = $xsl; 
     560    } 
     561     
     562    // now let's perform the transformations 
     563    foreach($transformations as $xsl) { 
     564      // load the stylesheet document into an XSLTProcessor instance 
     565      try { 
     566        $proc = new AgaviXmlConfigXsltProcessor(); 
     567        $proc->importStylesheet($xsl); 
     568      } catch(Exception $e) { 
     569        throw new AgaviParseException(sprintf('Configuration file "%s" could not be parsed: Could not import XSL stylesheet "%s": %s', $document->documentURI, $xsl->documentURI, $e->getMessage())); 
     570      } 
     571       
     572      // set some info (config file path, context name, environment name) as params 
     573      // first arg is the namespace URI, which PHP doesn't support. awesome. see http://bugs.php.net/bug.php?id=30622 for the sad details 
     574      // we could use "agavi:context" etc, that does work even without such a prefix being declared in the stylesheet, but that would be completely non-XML-ish, confusing, and against the spec. so we use dots instead. 
     575      $proc->setParameter('', array( 
     576        'agavi.config_path' => $document->documentURI, 
     577        'agavi.environment' => $environment, 
     578        'agavi.context' => $context, 
     579      )); 
     580       
     581      try { 
     582        // transform the doc 
     583        $newdoc = $proc->transformToDoc($document); 
     584      } catch(Exception $e) { 
     585        throw new AgaviParseException(sprintf('Configuration file "%s" could not be parsed: Could not transform the document using the XSL stylesheet "%s": %s', $document->documentURI, $xsl->documentURI, $e->getMessage())); 
     586      } 
     587       
     588      // no errors and we got a document back? excellent. this will be our new baby from now. time to kill the old one 
     589       
     590      // get the old document URI 
     591      $documentUri = $document->documentURI; 
     592       
     593      // and assign the new document to the old one 
     594      $document = $newdoc; 
     595       
     596      // save the old document URI just in case 
     597      $document->documentURI = $documentUri; 
     598    } 
     599    <