Changeset 1920
- Timestamp:
- 05/10/07 14:52:23 (20 months ago)
- Location:
- branches/david-xml_config_handlers/src
- Files:
-
- 6 modified
-
config/AgaviConfigCache.class.php (modified) (7 diffs)
-
config/AgaviConfigHandlersConfigHandler.class.php (modified) (1 diff)
-
config/AgaviConfigParser.class.php (modified) (2 diffs)
-
config/AgaviXmlConfigHandler.class.php (modified) (1 diff)
-
config/AgaviXmlConfigParser.class.php (modified) (12 diffs)
-
core/Agavi.class.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
branches/david-xml_config_handlers/src/config/AgaviConfigCache.class.php
r1917 r1920 56 56 * @since 0.9.0 57 57 */ 58 private static function callHandler($handler, $config, $cache, $context) 59 { 60 58 private static function callHandler($name, $config, $cache, $context) 59 { 61 60 if(self::$handlers === null) { 62 61 // we need to load the handlers first … … 64 63 self::loadConfigHandlers(); 65 64 } 66 65 67 66 // grab the base name of the handler 68 $basename = basename($handler); 69 70 if(isset(self::$handlers[$handler])) { 67 $basename = basename($name); 68 69 $handlerInfo = null; 70 71 if(isset(self::$handlers[$name])) { 71 72 // we have a handler associated with the full configuration path 72 // call the handler and retrieve the cache data 73 $data = self::$handlers[$handler]->execute($config, $context); 74 self::writeCacheFile($config, $cache, $data, false); 75 return; 73 $handlerInfo = self::$handlers[$name]; 76 74 } elseif(isset(self::$handlers[$basename])) { 77 75 // we have a handler associated with the configuration base name 78 // call the handler and retrieve the cache data 79 $data = self::$handlers[$basename]->execute($config, $context); 80 self::writeCacheFile($config, $cache, $data, false); 81 return; 76 $handlerInfo = self::$handlers[$basename]; 82 77 } else { 83 78 // let's see if we have any wildcard handlers registered that match 84 79 // this basename 85 foreach(self::$handlers as $key => $ handlerInstance) {80 foreach(self::$handlers as $key => $value) { 86 81 // replace wildcard chars in the configuration and create the pattern 87 82 $pattern = sprintf('#%s#', str_replace('\*', '.*?', preg_quote($key))); 88 89 if(preg_match($pattern, $handler)) { 90 // call the handler and retrieve the cache data 91 $data = $handlerInstance->execute($config, $context); 92 self::writeCacheFile($config, $cache, $data, false); 93 return; 83 84 if(preg_match($pattern, $name)) { 85 $handlerInfo = $value; 86 break; 94 87 } 95 88 } 96 89 } 97 98 // we do not have a registered handler for this file 99 $error = 'Configuration file "%s" does not have a registered handler'; 100 $error = sprintf($error, $config); 101 throw new AgaviConfigurationException($error); 90 91 if($handlerInfo === null) { 92 // we do not have a registered handler for this file 93 $error = 'Configuration file "%s" does not have a registered handler'; 94 $error = sprintf($error, $config); 95 throw new AgaviConfigurationException($error); 96 } 97 98 // call the handler and retrieve the cache data 99 $handler = new $handlerInfo['class']; 100 if($handler instanceof AgaviIXmlConfigHandler) { 101 102 } else { 103 if(isset($handlerInfo['validation'])) 104 $handler->initialize($handlerInfo['validation'], null, $handlerInfo['parameters']); 105 } 106 107 $data = $handler->execute($config, $context); 108 self::writeCacheFile($config, $cache, $data, false); 102 109 } 103 110 … … 141 148 142 149 return $cache; 143 144 150 } 145 151 … … 234 240 // since we only need the parser and handlers when the config is not cached 235 241 // it is sufficient to include them at this stage 242 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviBaseConfigHandler.class.php'); 243 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviConfigHandler.class.php'); 244 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviIXmlConfigHandler.class.php'); 245 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviXmlConfigHandler.class.php'); 246 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviAutoloadConfigHandler.class.php'); 236 247 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviConfigHandlersConfigHandler.class.php'); 237 248 require(AgaviConfig::get('core.agavi_dir') . '/config/AgaviConfigValueHolder.class.php'); … … 240 251 241 252 // manually create our config_handlers.xml handler 242 self::$handlers['config_handlers.xml'] = new AgaviConfigHandlersConfigHandler(); 243 self::$handlers['config_handlers.xml']->initialize(AgaviConfig::get('core.agavi_dir') . '/config/xsd/config_handlers.xsd'); 253 self::$handlers['config_handlers.xml'] = array( 254 'class' => 'AgaviConfigHandlersConfigHandler', 255 'parameters' => array( 256 ), 257 'validation' => array( 258 AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 259 AgaviConfig::get('core.agavi_dir') . '/config/xsd/config_handlers.xsd', 260 ), 261 ), 262 ); 244 263 245 264 $cfg = AgaviConfig::get('core.config_dir') . '/config_handlers.xml'; … … 303 322 * extension couldn't be found. 304 323 * 324 * @deprecated New-style config handlers don't call this method anymore. 325 * 305 326 * @author Dominik del Bondio <ddb@bitxtender.com> 306 327 * @author David Zülke <dz@bitxtender.com> … … 309 330 public static function parseConfig($config, $autoloadParser = true, $validateFile = null, $parserClass = null) 310 331 { 311 $parser = new AgaviXmlConfigParser(); 312 313 return $parsers->parse($config, $validateFile); 314 } 315 332 $parser = new AgaviConfigParser(); 333 334 return $parser->parse($config, $validateFile); 335 } 316 336 } 317 337 -
branches/david-xml_config_handlers/src/config/AgaviConfigHandlersConfigHandler.class.php
r1737 r1920 53 53 // parse the config file 54 54 $configurations = $this->orderConfigurations(AgaviConfigCache::parseConfig($config, false, $this->getValidationFile(), $this->parser)->configurations, AgaviConfig::get('core.environment')); 55 55 56 56 // init our data arrays 57 $data = array();58 57 $data = array(); 58 59 59 foreach($configurations as $cfg) { 60 60 // let's do our fancy work 61 61 foreach($cfg->handlers as $handler) { 62 62 $pattern = $handler->getAttribute('pattern'); 63 63 64 64 $category = var_export(AgaviToolkit::normalizePath($this->replaceConstants($pattern)), true); 65 66 $class = $handler->getAttribute('class'); 67 68 $parameters = $this->getItemParameters($handler); 69 65 66 $class = var_export($handler->getAttribute('class'), true); 67 68 $validation = array( 69 AgaviXmlConfigParser::VALIDATION_TYPE_RELAXNG => array( 70 ), 71 AgaviXmlConfigParser::VALIDATION_TYPE_SCHEMATRON => array( 72 ), 73 AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array( 74 ), 75 ); 76 if($handler->hasAttribute('validate')) { 77 $validation[AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][] = $this->literalize($handler->getAttribute('validate')); 78 } elseif(false) { 79 // TODO: check for <validations><validation type="schematron"> children here 80 } 81 $validation = var_export($validation, true); 82 83 $parameters = var_export($this->getItemParameters($handler), true); 84 70 85 // append new data 71 $tmp = "self::\$handlers[%s] = new %s();"; 72 $data[] = sprintf($tmp, $category, $class); 73 74 $tmp = "self::\$handlers[%s]->initialize(%s, %s, %s);"; 75 $data[] = sprintf($tmp, $category, var_export($this->literalize($handler->getAttribute('validate')), true), var_export($handler->getAttribute('parser'), true), var_export($parameters, true)); 86 $tmp = "self::\$handlers[%s] = array('class' => %s, 'parameters' => %s, 'validation' => %s);"; 87 $data[] = sprintf($tmp, $category, $class, $parameters, $validation); 76 88 } 77 89 } -
branches/david-xml_config_handlers/src/config/AgaviConfigParser.class.php
r1656 r1920 16 16 17 17 /** 18 * AgaviConfig Handler allows a developer to create a custom formatted19 * configuration file format.18 * AgaviConfigParser parses XML files using AgaviXmlConfigParser, but returns 19 * old-style ConfigValueHolders. 20 20 * 21 21 * @package agavi 22 22 * @subpackage config 23 23 * 24 * @author D ominik del Bondio <ddb@bitxtender.com>24 * @author David Zülke <dz@bitxtender.com> 25 25 * @copyright Authors 26 26 * @copyright The Agavi Project 27 * 28 * @deprecated Superseded by AgaviXmlConfigParser 27 29 * 28 30 * @since 0.11.0 … … 30 32 * @version $Id$ 31 33 */ 32 abstractclass AgaviConfigParser34 class AgaviConfigParser 33 35 { 34 36 /** 35 * Execute this configuration parser. 37 * @var string The encoding of the DOMDocument 38 */ 39 protected $encoding = 'utf-8'; 40 41 /** 42 * @param string An absolute filesystem path to a configuration file. 43 * @param array An associative array of validation information. 36 44 * 37 * @ param string An absolute filesystem path to a configuration file.45 * @return AgaviConfigValueHolder The data handlers use to perform tasks. 38 46 * 39 * @return AgaviConfigValueHolder Data to be written to a cache file. 47 * @author David Zülke <dz@bitxtender.com> 48 * @since 0.11.0 49 */ 50 public function parse($config, array $validationInfo = array()) 51 { 52 $parser = new AgaviXmlConfigParser(); 53 54 $doc = $parser->parse($config, $validationInfo); 55 56 $this->encoding = $doc->encoding; 57 58 $rootRes = new AgaviConfigValueHolder(); 59 60 if($doc->documentElement) { 61 $this->parseNodes(array($doc->documentElement), $rootRes); 62 } 63 64 return $rootRes; 65 } 66 67 /** 68 * Iterates thru a list of nodes and stores to each node in the 69 * ConfigValueHolder 40 70 * 41 * @throws <b>AgaviUnreadableException</b> If a requested configuration 42 * file does not exist or is not 43 * readable. 44 * @throws <b>AgaviParseException</b> If a requested configuration 45 * file is improperly formatted. 71 * @param mixed An array or an object that can be iterated over 72 * @param AgaviXmlValueHolder The storage for the info from the nodes 73 * @param bool Whether this list is the singular form of the parent node 46 74 * 75 * @author David Zülke <dz@bitxtender.com> 47 76 * @author Dominik del Bondio <ddb@bitxtender.com> 48 77 * @since 0.11.0 49 78 */ 50 public abstract function parse($config); 79 protected function parseNodes($nodes, AgaviConfigValueHolder $parentVh, $isSingular = false) 80 { 81 foreach($nodes as $node) { 82 if($node->nodeType == XML_ELEMENT_NODE && (!$node->namespaceURI || $node->namespaceURI == AgaviXmlConfigParser::XML_NAMESPACE)) { 83 $vh = new AgaviConfigValueHolder(); 84 $nodeName = $this->convertEncoding($node->localName); 85 $vh->setName($nodeName); 86 $parentVh->addChildren($nodeName, $vh); 51 87 88 foreach($node->attributes as $attribute) { 89 if((!$attribute->namespaceURI || $attribute->namespaceURI == AgaviXmlConfigParser::XML_NAMESPACE)) { 90 $vh->setAttribute($this->convertEncoding($attribute->localName), $this->convertEncoding($attribute->nodeValue)); 91 } 92 } 93 94 // there are no child nodes so we set the node text contents as the value for the valueholder 95 if($node->getElementsByTagName('*')->length == 0) { 96 $vh->setValue($this->convertEncoding($node->nodeValue)); 97 } 98 99 if($node->hasChildNodes()) { 100 $this->parseNodes($node->childNodes, $vh); 101 } 102 } 103 } 104 } 105 106 /** 107 * Handle encoding for a value, i.e. translate from UTF-8 if necessary. 108 * 109 * @param string A UTF-8 string value from the DomDocument. 110 * 111 * @return string A value in the correct encoding of the parsed document. 112 * 113 * @author David Zülke <dz@bitxtender.com> 114 * @since 0.11.0 115 */ 116 protected function convertEncoding($value) 117 { 118 if($this->encoding == 'utf-8') { 119 return $value; 120 } elseif($this->encoding == 'iso-8859-1') { 121 return utf8_decode($value); 122 } elseif(function_exists('iconv')) { 123 return iconv('UTF-8', $this->encoding, $value); 124 } else { 125 throw new AgaviParseException('No iconv module available, configuration file "' . $this->config . '" with input encoding "' . $this->encoding . '" cannot be parsed.'); 126 } 127 } 52 128 } 53 129 -
branches/david-xml_config_handlers/src/config/AgaviXmlConfigHandler.class.php
r1917 r1920 30 30 class AgaviXmlConfigHandler extends AgaviBaseConfigHandler implements AgaviIXmlConfigHandler 31 31 { 32 public function execute($config, $context = null) 33 { 34 } 32 35 } -
branches/david-xml_config_handlers/src/config/AgaviXmlConfigParser.class.php
r1913 r1920 29 29 * @version $Id$ 30 30 */ 31 32 31 class AgaviXmlConfigParser extends AgaviConfigParser 33 32 { 34 33 const XML_NAMESPACE = 'http://agavi.org/agavi/1.0/config'; 35 34 36 /** 37 * @var DomXPath A DomXPath instance used to parse this document. 38 */ 39 protected $xpath = null; 40 41 /** 42 * @var string The encoding of the file that's being parsed here. 43 */ 44 protected $encoding = 'utf-8'; 45 46 /** 47 * @var string The name of the config file we're parsing. 48 */ 49 protected $config = ''; 50 51 /** 52 * @see AgaviConfigParser::parse() 35 const VALIDATION_TYPE_XMLSCHEMA = 'xml_schema'; 36 37 const VALIDATION_TYPE_RELAXNG = 'relax_ng'; 38 39 const VALIDATION_TYPE_SCHEMATRON = 'schematron'; 40 41 /** 42 * @param string An absolute filesystem path to a configuration file. 43 * @param array An associative array of validation information. 44 * 45 * @return array An array of DOMDocuments (from child to parent). 53 46 * 54 47 * @author David Zülke <dz@bitxtender.com> … … 56 49 * @since 0.11.0 57 50 */ 58 public function parse($config, $validationFile = null)51 public function parse($config, array $validation = array()) 59 52 { 60 53 if(!is_readable($config)) { … … 63 56 } 64 57 65 $doc = $this->loadAndTransform($config, $validationFile = null); 66 67 $rootRes = new AgaviConfigValueHolder(); 68 69 if($doc->documentElement) { 70 $this->parseNodes(array($doc->documentElement), $rootRes); 71 } 72 73 return $rootRes; 74 } 75 76 /** 77 * Load the file into DOM, resolve XIncludes, apply XSL, validate against XSD. 78 * 79 * @param string The path to the XML file 80 * @param string The path to the validation file. 81 * 82 * @return DOMDocument The fully loaded and transformed DOM document. 83 * 84 * @author David Zülke <dz@bitxtender.com> 85 * @since 0.11.0 86 */ 87 public function loadAndTransform($config, $validationFile = null) 88 { 89 $this->config = $config; 90 58 $doc = $this->load($config); 59 60 $this->transform($doc); 61 62 $this->validate($doc, $validation); 63 64 $this->cleanup($doc); 65 66 return $doc; 67 } 68 69 /** 70 * Create and return a DOMXPath object for the document. 71 * 72 * @param DOMDocument The document to create the DOMXPath object for. 73 * @param bool If the XML namespace from the document element 74 * should be registered as 'agavi'. 75 * 76 * @return DOMXPath A DOMXPath instance for the document. 77 * 78 * @author David Zülke <dz@bitxtender.com> 79 * @since 0.11.0 80 */ 81 public function createXpath(DOMDocument $doc, $registerNamespace = true) 82 { 83 $xpath = new DOMXPath($doc); 84 85 if($registerNamespace && $doc->documentElement) { 86 $xpath->registerNamespace('agavi', $doc->documentElement->namespaceURI); 87 } 88 89 return $xpath; 90 } 91 92 /** 93 * Load the configuration file into DOM and resolve XIncludes. 94 * 95 * @param string The path to the configuration file. 96 * 97 * @return DOMDocument The loaded document. 98 * 99 * @author David Zülke <dz@bitxtender.com> 100 * @since 0.11.0 101 */ 102 public function load($config) 103 { 91 104 $luie = libxml_use_internal_errors(true); 92 105 libxml_clear_errors(); … … 110 123 } 111 124 112 $this->encoding = strtolower($doc->encoding);113 114 125 // replace %lala% directives in XInclude href attributes 115 126 foreach($doc->getElementsByTagNameNS('http://www.w3.org/2001/XInclude', '*') as $element) { … … 156 167 } 157 168 158 $this->xpath = new DOMXPath($doc); 159 160 $stylesheetProcessingInstructions = $this->xpath->query("//processing-instruction('xml-stylesheet')", $doc); 169 libxml_use_internal_errors($luie); 170 171 return $doc; 172 } 173 174 /** 175 * Transform the document using info from embedded processing instructions. 176 * 177 * @param DOMDocument The document to transform. 178 * 179 * @author David Zülke <dz@bitxtender.com> 180 * @since 0.11.0 181 */ 182 public function transform(DOMDocument $doc) 183 { 184 $luie = libxml_use_internal_errors(true); 185 186 $xpath = $this->createXpath($doc, false); 187 188 $stylesheetProcessingInstructions = $xpath->query("//processing-instruction('xml-stylesheet')", $doc); 161 189 foreach($stylesheetProcessingInstructions as $pi) { 162 190 $fragment = $doc->createDocumentFragment(); … … 168 196 if(strpos($href, '#') === 0) { 169 197 // embedded XSL 170 $stylesheets = $ this->xpath->query("//*[@id='" . substr($href, 1) . "']", $doc);198 $stylesheets = $xpath->query("//*[@id='" . substr($href, 1) . "']", $doc); 171 199 if($stylesheets->length) { 172 200 $xsl = new DomDocument(); … … 220 248 } 221 249 } 222 250 223 251 $proc = new XSLTProcessor(); 224 252 $proc->importStylesheet($xsl); … … 242 270 ); 243 271 } 244 245 $this->xpath = null;272 273 unset($xpath); 246 274 247 275 $newdoc = $proc->transformToDoc($doc); … … 264 292 ); 265 293 } 266 294 267 295 if($newdoc) { 268 296 $doc = $newdoc; 269 297 } 270 298 271 $this->xpath = new DOMXPath($doc);272 273 299 $pi->parentNode->removeChild($pi); 274 300 … … 277 303 } 278 304 279 if($doc->documentElement) { 280 $this->xpath->registerNamespace('agavi', $doc->documentElement->namespaceURI); 281 282 // remove top-level <sandbox> elements 283 $sandboxes = $this->xpath->query('/agavi:configurations/agavi:sandbox', $doc); 284 foreach($sandboxes as $sandbox) { 285 $sandbox->parentNode->removeChild($sandbox); 286 } 287 } 288 289 if($validationFile) { 305 libxml_use_internal_errors($luie); 306 } 307 308 /** 309 * Load the file into DOM, resolve XIncludes, apply XSL, validate against XSD. 310 * 311 * @param string The path to the XML file 312 * @param string The path to the validation file. 313 * 314 * @return DOMDocument The fully loaded and transformed DOM document. 315 * 316 * @author David Zülke <dz@bitxtender.com> 317 * @since 0.11.0 318 */ 319 public function validate(DOMDocument $doc, array $validationInfo = array()) 320 { 321 foreach($validationInfo as $type => $files) { 322 switch($type) { 323 case self::VALIDATION_TYPE_XMLSCHEMA: 324 $this->validateXmlschema($doc, (array) $files); 325 break; 326 case self::VALIDATION_TYPE_RELAXNG: 327 $this->validateRelaxng($doc, (array) $files); 328 break; 329 case self::VALIDATION_TYPE_SCHEMATRON: 330 $this->validateSchematron($doc, (array) $files); 331 break; 332 } 333 } 334 } 335 336 /** 337 * Clean up the document. 338 * 339 * @param DOMDocument The document to clean up. 340 * 341 * @author David Zülke <dz@bitxtender.com> 342 * @since 0.11.0 343 */ 344 public function cleanup(DOMDocument $doc) 345 { 346 $xpath = $this->createXpath($doc); 347 348 // remove top-level <sandbox> elements 349 $sandboxes = $xpath->query('/agavi:configurations/agavi:sandbox', $doc); 350 foreach($sandboxes as $sandbox) { 351 $sandbox->parentNode->removeChild($sandbox); 352 } 353 354 unset($xpath); 355 } 356 357 /** 358 * Validate the document against the given list of XML Schema files. 359 * 360 * @param DOMDocument The document to validate. 361 * @param array An array of file names to validate. 362 * 363 * @author David Zülke <dz@bitxtender.com> 364 * @since 0.11.0 365 */ 366 public function validateXmlschema(DOMDocument $doc, array $validationFiles = array()) 367 { 368 $luie = libxml_use_internal_errors(true); 369 370 foreach($validationFiles as $validationFile) { 290 371 if(!is_readable($validationFile)) { 291 372 libxml_use_internal_errors($luie); … … 293 374 throw new AgaviUnreadableException($error); 294 375 } 376 295 377 if(!$doc->schemaValidate($validationFile)) { 296 378 $errors = array(); … … 312 394 313 395 libxml_use_internal_errors($luie); 314 315 return $doc; 316 } 396 } 397 398 /** 399 * Validate the document against the given list of RELAX NG files. 400 * 401 * @param DOMDocument The document to validate. 402 * @param array An array of file names to validate. 403 * 404 * @author David Zülke <dz@bitxtender.com> 405 * @since 0.11.0 406 */ 407 public function validateRelaxng(DOMDocument $doc, array $validationFiles = array()) 408 { 409 $luie = libxml_use_internal_errors(true); 410 411 foreach($validationFiles as $validationFile) { 412 if(!is_readable($validationFile)) { 413 libxml_use_internal_errors($luie); 414 $error = 'Validation file "' . $validationFile . '" for configuration file "' . $config . '" does not exist or is unreadable'; 415 throw new AgaviUnreadableException($error); 416 } 417 418 if(!$doc->relaxNGValidate($validationFile)) {
