/** * The internal magic shared by the selector elements. Locates the node elements * and constructs the switch() statement for them. * @internal * @param Opt_Xml_Element $node The found element * @param array $section The reference to the section data. * @param string $type The name of the selection item. * @throws Opt_InstructionTooManyItems_Exception */ private function _internalMagic($node, &$section, $type) { $section['format']->assign('item', !$type ? $section['test'] : 'item'); // Check, if there are no instruction tags in the children list. $instructions = array(); $cases = array(); $alternative = null; foreach ($node as $subnode) { if ($subnode instanceof Opt_Xml_Element && $this->_compiler->isNamespace($subnode->getNamespace())) { if ($this->_compiler->isInstruction($subnode->getXmlName()) || $subnode->getXmlName() == 'opt:separator') { if ($subnode != 'opt:selectorelse') { $instructions[] = $subnode; } else { if (!is_null($alternative)) { throw new Opt_InstructionTooManyItems_Exception('opt:selectorelse', $node->getXmlName(), 'Zero or one'); } $alternative = $subnode; } } else { $cases[] = $subnode; } } else { $node->removeChild($subnode); } } if (sizeof($instructions) > 0) { // There are instructions in opt:selector. We have to move the // cases to a fake node in order to sanitize them. $node->removeChildren(); foreach ($instructions as $instruction) { $node->appendChild($instruction); } $fake = new Opt_Xml_Element('opt:_'); foreach ($cases as $case) { $fake->appendChild($case); } $fake->set('processAll', true); $fake->set('hidden', false); $node->appendChild($fake); if (!is_null($alternative)) { $node->appendChild($alternative); } } else { $fake = $node; } $fake->addAfter(Opt_Xml_Buffer::TAG_CONTENT_BEFORE, 'switch(' . $section['format']->get('section:variable') . '){'); // If opt:selectorelse is used, the ending curly bracket is created by // _processSelectorelse(). if (is_null($alternative)) { $fake->addBefore(Opt_Xml_Buffer::TAG_CONTENT_AFTER, ' } '); } foreach ($cases as $case) { if ($case->getXmlName() == 'opt:separator') { Opl_Debug::write('Print shit'); } if ($case->getName() == 'default') { $case->addAfter(Opt_Xml_Buffer::TAG_CONTENT_BEFORE, ' default: '); } else { $case->addAfter(Opt_Xml_Buffer::TAG_CONTENT_BEFORE, ' case \'' . $case->getName() . '\': '); } $case->addBefore(Opt_Xml_Buffer::TAG_CONTENT_AFTER, ' break; '); $case->set('processAll', true); $case->set('hidden', false); } }
/** * Adds the show condition PHP code to the specified node, using the * information from the section record. * * @internal * @param Opt_Xml_Element $node The node, where to add the show condition. * @param Array &$section The section info record */ private function _createShowCondition(Opt_Xml_Element $node, &$section) { // First, try to check for the call:use tag variable. if ($node->get('call:use') !== NULL) { $section['node']->set('call:use', $node->get('call:use')); } // Deal with the data formats $format = $section['format']; $format->assign('section', $section); $request = $format->property('section:anyRequests'); if (!is_null($request)) { // Send the requested data, if the data format needs any. switch ($request) { case 'ancestorNames': self::$_stack->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP); $list = array(); $parent = $section['parent']; foreach (self::$_stack as $up) { if ($up == $parent) { $parent = self::$_sections[$up]['parent']; $list[] = self::$_sections[$up]['name']; } } $format->assign('requestedData', array_reverse($list)); break; case 'ancestorNumbers': self::$_stack->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP); $list = array(); $parent = $section['parent']; $iteration = self::$_stack->count(); foreach (self::$_stack as $up) { if ($up == $parent) { $parent = self::$_sections[$up]['parent']; $list[] = $iteration; } $iteration--; } $format->assign('requestedData', array_reverse($list)); break; } } $code = $format->get('section:init'); if (is_null($section['display'])) { $code .= ' if(' . $format->get('section:isNotEmpty') . '){ '; } else { $code .= ' if(' . $format->get('section:isNotEmpty') . ' && ' . $section['display'] . '){ '; } $code .= $format->get('section:started'); $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, $code); $code = $format->get('section:finished') . ' } ' . $format->get('section:done'); $node->addAfter(Opt_Xml_Buffer::TAG_AFTER, $code); $node->set('priv:initialized', true); }
/** * Processes the opt:insert element. * @internal * @param Opt_Xml_Element $node The found element */ public function _processInsert(Opt_Xml_Element $node) { // A support for the dynamically chosen part captured by opt:capture if ($node->getAttribute('captured') !== NULL) { $params = array('captured' => array(0 => self::REQUIRED, self::EXPRESSION)); $this->_extractAttributes($node, $params); if ($node->hasChildren()) { $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'if(!isset(self::$_capture[' . $params['captured'] . '])){ '); $node->addAfter(Opt_Xml_Buffer::TAG_AFTER, '} else { echo self::$_capture[' . $params['captured'] . ']; } '); $this->_process($node); } else { $node->addBefore(Opt_Xml_Buffer::TAG_BEFORE, 'if(isset(self::$_capture[' . $params['captured'] . '])){ echo self::$_capture[' . $params['captured'] . ']; } '); } } else { // Snippet insertion $params = array('snippet' => array(0 => self::REQUIRED, self::ID), 'ignoredefault' => array(0 => self::OPTIONAL, self::BOOL, false)); $this->_extractAttributes($node, $params); if (in_array($params['snippet'], $this->_current)) { array_push($this->_current, $params['snippet']); $err = new Opt_SnippetRecursion_Exception($params['snippet']); throw $err->setData($this->_current); } if (isset($this->_snippets[$params['snippet']])) { array_push($this->_current, $params['snippet']); $snippet =& $this->_snippets[$params['snippet']]; // Move all the stuff to the fake node. if ($node->hasChildren() && $params['ignoredefault'] == false) { $newNode = new Opt_Xml_Element('opt:_'); $newNode->set('escaping', $this->_compiler->get('escaping')); $newNode->moveChildren($node); $size = sizeof($snippet); $snippet[$size] = $newNode; $node->set('insertSize', $size); } // We must do the cleaning for the inserted node. $node->removeChildren(); // Process the snippets $node->set('escaping', $this->_compiler->get('escaping')); $this->_compiler->set('escaping', $snippet[0]->get('escaping')); foreach ($snippet[0] as $subnode) { $node->appendChild(clone $subnode); } $this->_process($node); $node->set('insertSnippet', $params['snippet']); $node->set('postprocess', true); } else { // Processing the default content - snippet not found. $this->_process($node); } } }
/** * Parses the input code and returns the OPT XML tree. * * @param String $filename The file name (for debug purposes) * @param String &$code The code to parse * @return Opt_Xml_Root */ public function parse($filename, &$code) { $current = $tree = new Opt_Xml_Root(); $codeSize = strlen($code); $encoding = $this->_tpl->charset; // First we have to find the prolog and DTD. Then we will be able to parse tags. if ($this->_mode == 0) { // Find and parse XML prolog $endProlog = 0; $endDoctype = 0; if (substr($code, 0, 5) == '<?xml') { $endProlog = strpos($code, '?>', 5); if ($endProlog === false) { throw new Opt_XmlInvalidProlog_Exception('prolog ending is missing'); } $values = $this->_compileProlog(substr($code, 5, $endProlog - 5)); $endProlog += 2; if (!$this->_tpl->prologRequired) { // The prolog must be displayed $tree->setProlog(new Opt_Xml_Prolog($values)); } } // Skip white spaces for ($i = $endProlog; $i < $codeSize; $i++) { if ($code[$i] != ' ' && $code[$i] != ' ' && $code[$i] != "\r" && $code[$i] != "\n") { break; } } // Try to find doctype at the new position. $possibleDoctype = substr($code, $i, 9); if ($possibleDoctype == '<!doctype' || $possibleDoctype == '<!DOCTYPE') { // OK, we've found it, now determine the doctype end. $bracketCounter = 0; $doctypeStart = $i; for ($i += 9; $i < $codeSize; $i++) { if ($code[$i] == '<') { $bracketCounter++; } else { if ($code[$i] == '>') { if ($bracketCounter == 0) { $endDoctype = $i; break; } $bracketCounter--; } } } if ($endDoctype == 0) { throw new Opt_XmlInvalidDoctype_Exception('doctype ending is missing'); } if (!$this->_tpl->prologRequired) { $tree->setDtd(new Opt_Xml_Dtd(substr($code, $doctypeStart, $i - $doctypeStart + 1))); } $endDoctype++; } else { $endDoctype = $endProlog; } // OK, now skip that part. $code = substr($code, $endDoctype, $codeSize); // In the quirks mode, some results from the regular expression parser are // moved by one position, so we must add some dynamics here. $attributeCell = 5; $endingSlashCell = 6; $tagExpression = $this->_rXmlTagExpression; } else { $tagExpression = $this->_rQuirksTagExpression; $attributeCell = 6; $endingSlashCell = 7; } // Split through the general groups (cdata-content) $groups = preg_split($this->_rCDataExpression, $code, 0, PREG_SPLIT_DELIM_CAPTURE); $groupCnt = sizeof($groups); $groupState = 0; Opt_Xml_Cdata::$mode = $this->_mode; for ($k = 0; $k < $groupCnt; $k++) { // Process CDATA if ($groupState == 0 && $groups[$k] == '<![CDATA[') { $cdata = new Opt_Xml_Cdata(''); $cdata->set('cdata', true); $groupState = 1; continue; } if ($groupState == 1) { if ($groups[$k] == ']]>') { $current = $this->_treeTextAppend($current, $cdata); $groupState = 0; } else { $cdata->appendData($groups[$k]); } continue; } $subgroups = preg_split($this->_rCommentExpression, $groups[$k], 0, PREG_SPLIT_DELIM_CAPTURE); $subgroupCnt = sizeof($subgroups); $subgroupState = 0; for ($i = 0; $i < $subgroupCnt; $i++) { // Process comments if ($subgroupState == 0 && $subgroups[$i] == '<!--') { $commentNode = new Opt_Xml_Comment(); $subgroupState = 1; continue; } if ($subgroupState == 1) { if ($subgroups[$i] == '-->') { $current->appendChild($commentNode); $subgroupState = 0; } else { $commentNode->appendData($subgroups[$i]); } continue; } elseif ($subgroups[$i] == '-->') { throw new Opt_XmlInvalidCharacter_Exception('-->'); } // Find XML tags preg_match_all($tagExpression, $subgroups[$i], $result, PREG_SET_ORDER); /* * Output field description for $result array: * 0 - original content * 1 - tag content (without delimiters) * 2 - /, if enclosing tag * 3 - name * 4 - arguments (5 in quirks mode) * 5 - /, if enclosing tag without subcontent (6 in quirks mode) */ $resultSize = sizeof($result); $offset = 0; for ($j = 0; $j < $resultSize; $j++) { // Copy the remaining text to the text node $id = strpos($subgroups[$i], $result[$j][0], $offset); if ($id > $offset) { $current = $this->_treeTextCompile($current, substr($subgroups[$i], $offset, $id - $offset)); } $offset = $id + strlen($result[$j][0]); if (!isset($result[$j][$endingSlashCell])) { $result[$j][$endingSlashCell] = ''; } // Process the argument list $attributes = array(); if (!empty($result[$j][$attributeCell])) { // Just for sure... $result[$j][$attributeCell] = trim($result[$j][$attributeCell]); $oldLength = strlen($result[$j][$attributeCell]); $result[$j][$attributeCell] = rtrim($result[$j][$attributeCell], '/'); if (strlen($result[$j][$attributeCell]) != $oldLength) { $result[$j][$endingSlashCell] = '/'; } $attributes = $this->_compileAttributes($result[$j][$attributeCell]); if (!is_array($attributes)) { throw new Opt_XmlInvalidAttribute_Exception($result[$j][0]); } } // Recognize the tag type if ($result[$j][3] != '/') { // Opening tag $node = new Opt_Xml_Element($result[$j][4]); $node->set('single', $result[$j][$endingSlashCell] == '/'); foreach ($attributes as $name => $value) { $node->addAttribute($anode = new Opt_Xml_Attribute($name, $value)); } $current = $this->_treeNodeAppend($current, $node, $result[$j][$endingSlashCell] != '/'); } elseif ($result[$j][3] == '/') { if (sizeof($attributes) > 0) { throw new Opt_XmlInvalidTagStructure_Exception($result[$j][0]); } if ($current instanceof Opt_Xml_Element) { if ($current->getXmlName() != $result[$j][4]) { throw new Opt_XmlInvalidOrder_Exception($result[$j][4], $current->getXmlName()); } } else { throw new Opt_XmlInvalidOrder_Exception($result[$j][4], 'NULL'); } $current = $this->_treeJumpOut($current); } else { throw new Opt_XmlInvalidTagStructure_Exception($result[$j][0]); } } if (strlen($subgroups[$i]) > $offset) { $current = $this->_treeTextCompile($current, substr($subgroups[$i], $offset, strlen($subgroups[$i]) - $offset)); } } } // Testing if everything was closed. if ($current !== $tree) { // Error handling - determine the name of the unclosed tag. while (!$current instanceof Opt_Xml_Element) { $current = $current->getParent(); } throw new Opt_UnclosedTag_Exception($current->getXmlName()); } if ($this->_mode == 0 && $this->_tpl->singleRootNode) { // TODO: The current code does not check the contents of Opt_Text_Nodes and other root elements // that may contain invalid and valid XML syntax at the same time. // For now, this code is frozen, we'll think a bit about it in the future. Maybe nobody // will notice this :) $elementFound = false; foreach ($tree as $item) { if ($item instanceof Opt_Xml_Element) { if ($elementFound) { // Oops, there is already another root node! throw new Opt_XmlRootElement_Exception($item->getXmlName()); } $elementFound = true; } } } return $tree; }
/** * Parses the input code and returns the OPT XML tree. * * @param String $filename The file name (for debug purposes) * @param String &$code The code to parse * @return Opt_Xml_Root */ public function parse($filename, &$code) { $debug = array(XMLReader::NONE => 'NONE', XMLReader::ELEMENT => 'ELEMENT', XMLReader::ATTRIBUTE => 'ATTRIBUTE', XMLReader::TEXT => 'TEXT', XMLReader::CDATA => 'CDATA', XMLReader::ENTITY_REF => 'ENTITY_REF', XMLReader::ENTITY => 'ENTITY', XMLReader::PI => 'PI', XMLReader::COMMENT => 'COMMENT', XMLReader::DOC => 'DOC', XMLReader::DOC_TYPE => 'DOC_TYPE', XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', XMLReader::NOTATION => 'NOTATION', XMLReader::WHITESPACE => 'WHITESPACE', XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', XMLReader::END_ELEMENT => 'END_ELEMENT', XMLReader::END_ENTITY => 'END_ENTITY', XMLReader::XML_DECLARATION => 'XML_DECLARATION'); libxml_use_internal_errors(true); $reader = new XMLReader(); $reader->xml($code); // $reader->setParserProperty(XMLReader::LOADDTD, true); // $reader->setParserProperty(XMLReader::VALIDATE, true); $reader->setParserProperty(XMLReader::SUBST_ENTITIES, true); $root = $current = new Opt_Xml_Root(); $firstElementMatched = false; $depth = 0; // Thanks, Oh Great PHP for your excellent WARNINGS!!! >:( while (@$reader->read()) { if ($reader->depth < $depth) { $current = $current->getParent(); } elseif ($reader->depth > $depth) { $current = $optNode; } // Opl_Debug::write($debug[$reader->nodeType].': '.$reader->name.', '.$reader->value); switch ($reader->nodeType) { // XML elements case XMLReader::ELEMENT: $optNode = new Opt_Xml_Element($reader->name); // Parse element attributes, if you manage to get there if ($reader->moveToFirstAttribute()) { do { // "xmlns" special namespace must be handler somehow differently. if ($reader->prefix == 'xmlns') { $ns = str_replace('xmlns:', '', $reader->name); $root->addNamespace($ns, $reader->value); // Let this attribute to appear, if it does not represent an OPT special // namespace if (!$this->_compiler->isNamespace($ns)) { $optAttribute = new Opt_Xml_Attribute($reader->name, $reader->value); $optNode->addAttribute($optAttribute); } } else { $optAttribute = new Opt_Xml_Attribute($reader->name, $reader->value); $optNode->addAttribute($optAttribute); } } while ($reader->moveToNextAttribute()); $reader->moveToElement(); } // Set "rootNode" flag if (!$firstElementMatched) { $optNode->set('rootNode', true); $firstElementMatched = true; } // Set "single" flag if ($reader->isEmptyElement) { $optNode->set('single', true); } $current->appendChild($optNode); break; case XMLReader::TEXT: $this->_treeTextCompile($current, $reader->value); break; case XMLReader::COMMENT: $optNode = new Opt_Xml_Comment($reader->value); $current->appendChild($optNode); break; case XMLReader::CDATA: $cdata = new Opt_Xml_Cdata($reader->value); $cdata->set('cdata', true); if ($current instanceof Opt_Xml_Text) { $current->appendChild($cdata); } else { $text = new Opt_Xml_Text(); $text->appendChild($cdata); $current->appendChild($text); $current = $text; } break; /* case XMLReader::SIGNIFICANT_WHITESPACE: $cdata = new Opt_Xml_Cdata($reader->value); $cdata->set('cdata', true); if($current instanceof Opt_Xml_Text) { $current->appendChild($cdata); } else { $text = new Opt_Xml_Text(); $text->appendChild($cdata); $current->appendChild($text); $current = $text; } break; */ } $depth = $reader->depth; } // Error checking $errors = libxml_get_errors(); if (sizeof($errors) > 0) { libxml_clear_errors(); foreach ($errors as $error) { echo $error->message . ' (' . $error->line . ')<br/>'; } } return $root; }