/** * @param string An absolute filesystem path to a configuration file. * @param array An associative array of validation information. * * @return AgaviConfigValueHolder The data handlers use to perform tasks. * * @author David Zülke <*****@*****.**> * @since 0.11.0 */ public function parse($config, $validationFile = null) { // copy path in case convertEncoding() needs to complain about a missing ICONV extension $this->config = $config; $parser = new AgaviXmlConfigParser($config, AgaviConfig::get('core.environment'), null); $validation = array(AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(), AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array(AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA => array())); if ($validationFile !== null) { $validation[AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER][AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][] = $validationFile; } $doc = $parser->execute(array(), $validation); // remember encoding for convertEncoding() $this->encoding = strtolower($doc->encoding); $rootRes = new AgaviConfigValueHolder(); if ($doc->documentElement) { $this->parseNodes(array($doc->documentElement), $rootRes); } return $rootRes; }
/** * Check whether or not this is a standard Agavi configuration file, i.e. with * a <configurations> and <configuration> envelope. * * @return bool true, if it is an Agavi config structure, false otherwise. * * @author David Zülke <*****@*****.**> * @since 1.0.0 */ public function isAgaviConfiguration() { return AgaviXmlConfigParser::isAgaviConfigurationDocument($this); }
/** * @param string An absolute filesystem path to a configuration file. * @param string The environment name. * @param string The optional context name. * @param array An associative array of transformation information. * @param array An associative array of validation information. * * @return DOMDocument A properly merged DOMDocument. * * @author David Zülke <*****@*****.**> * @author Dominik del Bondio <*****@*****.**> * @author Noah Fontes <*****@*****.**> * @since 0.11.0 */ public static function run($path, $environment, $context = null, array $transformationInfo = array(), array $validationInfo = array()) { $isAgaviConfigFormat = true; // build an array of documents (this one, and the parents) $docs = array(); $previousPaths = array(); $nextPath = $path; while ($nextPath !== null) { // run the single stage parser $parser = new AgaviXmlConfigParser($nextPath, $environment, $context); $doc = $parser->execute($transformationInfo[self::STAGE_SINGLE], $validationInfo[self::STAGE_SINGLE]); // put the new document in the list $docs[] = $doc; // make sure it (still) is a <configurations> file with the proper Agavi namespace if ($isAgaviConfigFormat) { $isAgaviConfigFormat = self::isAgaviConfigurationDocument($doc); } // is it an Agavi <configurations> element? does it have a parent attribute? yes? good. parse that next // TODO: support future namespaces if ($isAgaviConfigFormat && $doc->documentElement->hasAttribute('parent')) { $theNextPath = AgaviToolkit::literalize($doc->documentElement->getAttribute('parent')); // no infinite loop plz, kthx if ($nextPath === $theNextPath) { throw new AgaviParseException(sprintf("Agavi detected an infinite loop while processing parent configuration files of \n%s\n\nFile\n%s\nincludes itself as a parent.", $path, $theNextPath)); } elseif (isset($previousPaths[$theNextPath])) { throw new AgaviParseException(sprintf("Agavi detected an infinite loop while processing parent configuration files of \n%s\n\nFile\n%s\nhas previously been included by\n%s", $path, $theNextPath, $previousPaths[$theNextPath])); } else { $previousPaths[$theNextPath] = $nextPath; $nextPath = $theNextPath; } } else { $nextPath = null; } } // TODO: use our own classes here that extend DOM* $retval = new AgaviXmlConfigDomDocument(); foreach (self::$agaviEnvelopeNamespaces as $envelopeNamespaceUri => $envelopeNamespacePrefix) { $retval->getXpath()->registerNamespace($envelopeNamespacePrefix, $envelopeNamespaceUri); } if ($isAgaviConfigFormat) { // if it is an Agavi config, we'll create a new document with all files' <configuration> blocks inside $retval->appendChild(new AgaviXmlConfigDomElement('configurations', null, self::NAMESPACE_AGAVI_ENVELOPE_LATEST)); // reverse the array - we want the parents first! $docs = array_reverse($docs); $configurationElements = array(); // TODO: I bet this leaks memory due to the nodes being taken out of the docs. beware circular refs! foreach ($docs as $doc) { // iterate over all nodes (attributes, <sandbox>, <configuration> etc) inside the document element and append them to the <configurations> element in our final document foreach ($doc->documentElement->childNodes as $node) { if ($node->nodeType == XML_ELEMENT_NODE && $node->localName == 'configuration' && self::isAgaviEnvelopeNamespace($node->namespaceURI)) { // it's a <configuration> element - put that on a stack for processing $configurationElements[] = $node; } else { // import the node, recursively, and store the imported node $importedNode = $retval->importNode($node, true); // now append it to the <configurations> element $retval->documentElement->appendChild($importedNode); } } // if it's a <configurations> element, then we need to copy the attributes from there if ($doc->isAgaviConfiguration()) { $namespaces = $doc->getXPath()->query('namespace::*'); foreach ($namespaces as $namespace) { if ($namespace->localName !== 'xml' && $namespace->localName != 'xmlns') { $retval->documentElement->setAttributeNS(self::NAMESPACE_XMLNS_2000, 'xmlns:' . $namespace->localName, $namespace->namespaceURI); } } foreach ($doc->documentElement->attributes as $attribute) { // but not the "parent" attributes... if ($attribute->namespaceURI === null && $attribute->localName === 'parent') { continue; } $importedAttribute = $retval->importNode($attribute, true); $retval->documentElement->setAttributeNode($importedAttribute); } } } // generic <configuration> first, then those with an environment attribute, then those with context, then those with both $configurationOrder = array('count(self::node()[@agavi_annotations_latest:matched and not(@environment) and not(@context)])', 'count(self::node()[@agavi_annotations_latest:matched and @environment and not(@context)])', 'count(self::node()[@agavi_annotations_latest:matched and not(@environment) and @context])', 'count(self::node()[@agavi_annotations_latest:matched and @environment and @context])'); // now we sort the nodes according to the rules foreach ($configurationOrder as $xpath) { // append all matching nodes from the order array... foreach ($configurationElements as &$element) { // ... if the xpath matches, that is! if ($element->ownerDocument->getXpath()->evaluate($xpath, $element)) { // it did, so import the node and append it to the result doc $importedNode = $retval->importNode($element, true); $retval->documentElement->appendChild($importedNode); } } } // run the compilation stage parser $retval = self::executeCompilation($retval, $environment, $context, $transformationInfo[self::STAGE_COMPILATION], $validationInfo[self::STAGE_COMPILATION]); } else { // it's not an agavi config file. just pass it through then $retval->appendChild($retval->importNode($doc->documentElement, true)); } // cleanup attempt unset($docs); // set the pseudo-document URI $retval->documentURI = $path; return $retval; }
/** * Builds a proper regular expression from the input pattern to test against * the given subject. This is for "environment" and "context" attributes of * configuration blocks in the files. * * @param string A regular expression chunk without delimiters/anchors. * * @return bool Whether or not the subject matched the pattern. * * @see AgaviXmlConfigParser::testPattern() * * @author David Zülke <*****@*****.**> * @since 0.11.0 */ public static function testPattern($pattern, $subject) { return AgaviXmlConfigParser::testPattern($pattern, $subject); }
protected function parseConfiguration($configFile, $xslFile = null, $environment = null) { return AgaviXmlConfigParser::run($configFile, $environment ? $environment : AgaviConfig::get('core.environment'), '', array(AgaviXmlConfigParser::STAGE_SINGLE => $xslFile ? array($xslFile) : array(), AgaviXmlConfigParser::STAGE_COMPILATION => array()), array(AgaviXmlConfigParser::STAGE_SINGLE => array(AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(), AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array()), AgaviXmlConfigParser::STAGE_COMPILATION => array(AgaviXmlConfigParser::STEP_TRANSFORMATIONS_BEFORE => array(), AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER => array()))); }
/** * Execute the config handler for the given file. * * @param string The path to the config file (full path). * @param string The context which we're currently running. * @param array The config handler info. * * @return string The compiled data. * * @author Felix Gilcher <*****@*****.**> * @since 1.0.0 */ protected static function executeHandler($config, $context, array $handlerInfo) { // call the handler and retrieve the cache data $handler = new $handlerInfo['class'](); if ($handler instanceof AgaviIXmlConfigHandler) { // a new-style config handler // it does not parse the config itself; instead, it is given a complete and merged DOM document $doc = AgaviXmlConfigParser::run($config, AgaviConfig::get('core.environment'), $context, $handlerInfo['transformations'], $handlerInfo['validations']); if ($context !== null) { $context = AgaviContext::getInstance($context); } $handler->initialize($context, $handlerInfo['parameters']); try { $data = $handler->execute($doc); } catch (AgaviException $e) { throw new $e(sprintf("Compilation of configuration file '%s' failed for the following reason(s):\n\n%s", $config, $e->getMessage())); } } else { $validationFile = null; if (isset($handlerInfo['validations'][AgaviXmlConfigParser::STAGE_SINGLE][AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER][AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][0])) { $validationFile = $handlerInfo['validations'][AgaviXmlConfigParser::STAGE_SINGLE][AgaviXmlConfigParser::STEP_TRANSFORMATIONS_AFTER][AgaviXmlConfigParser::VALIDATION_TYPE_XMLSCHEMA][0]; } $handler->initialize($validationFile, null, $handlerInfo['parameters']); $data = $handler->execute($config, $context); } return $data; }