/includes/Opl/Opt/Instruction/BaseSection.php
PHP | 615 lines | 386 code | 47 blank | 182 comment | 52 complexity | 61b3e9cbd80430d8e0bb4b01b9a80f64 MD5 | raw file
Possible License(s): GPL-3.0, MIT
- <?php
- /*
- * OPEN POWER LIBS <http://www.invenzzia.org>
- *
- * This file is subject to the new BSD license that is bundled
- * with this package in the file LICENSE. It is also available through
- * WWW at this URL: <http://www.invenzzia.org/license/new-bsd>
- *
- * Copyright (c) Invenzzia Group <http://www.invenzzia.org>
- * and other contributors. See website for details.
- *
- * $Id: BaseSection.php 231 2009-09-19 07:13:14Z zyxist $
- */
- abstract class Opt_Instruction_BaseSection extends Opt_Instruction_Loop
- {
- static private $_sections = array();
- static private $_stack;
- /**
- * Initializes the section processor.
- *
- * @param Opt_Compiler_Class $compiler The compiler object
- */
- public function __construct($compiler)
- {
- parent::__construct($compiler);
- if(!is_object(self::$_stack))
- {
- self::$_stack = new SplStack;
- }
- } // end __construct();
- /**
- * Resets the processor state.
- */
- public function reset()
- {
- if(sizeof(self::$_sections) > 0)
- {
- self::$_stack = new SplStack;
- self::$_sections = array();
- }
- } // end reset();
- /**
- * Returns the information record about the specified section.
- *
- * @static
- * @param String $name Section ID
- * @return Array The information record or NULL, if the section is not found.
- */
- static public function getSection($name)
- {
- if(!isset(self::$_sections[$name]))
- {
- return NULL;
- }
- return self::$_sections[$name];
- } // end getSection();
- /**
- * Returns the currently parsed section record.
- *
- * @static
- * @return Array The information record or NULL, if no sections are active.
- */
- static public function getLastSection()
- {
- return self::getSection(self::$_stack->top());
- } // end getLastSection();
- /**
- * Returns the number of active sections.
- *
- * @static
- * @return Int The number of sections on the stack.
- */
- static public function countSections()
- {
- return self::$_stack->count();
- } // end countSections();
- /**
- * Creates a new section record, using the information from the specified node.
- * If the node is not a valid OPT instruction, the method scans the ancestors
- * to find a free opt:show node.
- *
- * The method can also initialize the attributed section, if we provide the
- * opt:section attribute object as the second argument.
- *
- * The method initializes also the neighbourhood of the section by parsing the
- * opt:show tag, if necessary and creating the enter condition. Note that it
- * does not start the section - the record is neither put onto the section stack
- * nor opt:use integration is not made. You have to use _sectionStart() in order
- * to fully initialize the section.
- *
- * @param Opt_Xml_Element $node
- * @param Opt_Xml_Attribute $attr optional
- * @param Array $extraAttributes optional
- * @return Array The section record.
- */
- protected function _sectionCreate(Opt_Xml_Element $node, $attr = NULL, $extraAttributes = NULL)
- {
- /* First, we need to determine, whether the section is associated with
- * opt:show. In case of tag sections, this is done only if the node
- * does not contain any section attributes.
- */
- $show = NULL;
- if($attr instanceof Opt_Xml_Attribute)
- {
- $show = $this->_findShowNode($node, $attr->getValue());
- if(!is_null($show))
- {
- // In this case we can obtain the attributes from opt:show.
- $section = $this->_extractSectionAttributes($show, $extraAttributes);
- $section['show'] = $show;
- $section['node'] = $node;
- $section['attribute'] = $attr;
- }
- else
- {
- // Generate a default section record, using the $attr value and
- // optionally checking for the "separator" attribute in the current node.
- $_params = array(
- 'separator' => array(0 => self::OPTIONAL, self::EXPRESSION, NULL),
- );
- $this->_extractAttributes($node, $_params);
- $section = array(
- 'name' => $attr->getValue(),
- 'order' => 'asc',
- 'parent' => NULL,
- 'datasource' => NULL,
- 'display' => NULL,
- 'separator' => $_params['separator'],
- 'show' => null,
- 'node' => $node,
- 'attribute' => $attr
- );
- }
- }
- else
- {
- if(is_null($node->getAttribute('name')))
- {
- // We must look for opt:show
- $show = $this->_findShowNode($node);
- if(is_null($show))
- {
- throw new Opt_AttributeNotDefined_Exception('name', $node->getXmlName());
- }
- $section = $this->_extractSectionAttributes($show, $extraAttributes);
- $section['show'] = $show;
- $section['node'] = $node;
- $section['attribute'] = null;
- }
- else
- {
- $section = $this->_extractSectionAttributes($node, $extraAttributes);
- $section['show'] = null;
- $section['node'] = $node;
- $section['attribute'] = null;
- }
- }
- $this->_validateSection($section);
- if(is_null($section['show']))
- {
- $this->_createShowCondition($node, $section);
- }
- elseif(is_null($section['show']->get('priv:initialized')))
- {
- $this->_createShowCondition($section['show'], $section);
- }
- // A faster way to obtain the section name associated with this section.
- $node->set('priv:section', $section['name']);
- return $section;
- } // end _sectionCreate();
- /**
- * Starts the section by putting its record on a section stack.
- *
- * @param Array $section The section record.
- */
- protected function _sectionStart(Array &$section)
- {
- self::_addSection($section);
- if(!is_null($section['node']->get('call:use')))
- {
- $this->_compiler->setConversion('##simplevar_'.$section['node']->get('call:use'), $section['name']);
- }
- // Populate the debug console.
- if($this->_tpl->debugConsole)
- {
- if(isset($section['datasource']))
- {
- $parent = '<em>Datasource</em>';
- }
- elseif(!is_null($section['parent']))
- {
- $parent = $section['parent'];
- }
- else
- {
- $parent = '-';
- }
- Opt_Support::addSection($section['name'], $parent, (string)$section['format'], $section['node']->getXmlName());
- }
- } // end _sectionStart();
- /**
- * Finalizes the section associated with the specified XML node. If the
- * node does not contain any valid section information, it generates
- * an exception.
- *
- * @param Opt_Xml_Element $node The section node
- */
- protected function _sectionEnd(Opt_Xml_Element $node)
- {
- $section = $node->get('priv:section');
- if(!is_array($section))
- {
- $section = self::getSection($section);
- }
- if(!is_null($node->get('call:use')))
- {
- $this->_compiler->unsetConversion('##simplevar_'.$node->get('priv:section'));
- }
- self::_removeSection($section['name']);
- } // end _sectionEnd();
- /**
- * 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);
- } // end _createShowCondition();
-
- /**
- * Finds the nearest free opt:show node in the ascentors of the specified node.
- *
- * @internal
- * @param Opt_Xml_Element $item The current node.
- * @param String $name optional The name that opt:show must match.
- * @return Opt_Xml_Element The opt:show node or NULL if not found.
- */
- private function _findShowNode(Opt_Xml_Element $item, $name = null)
- {
- if(!is_null($name))
- {
- // The section names must also match!
- while(!is_null($item = $item->getParent()))
- {
- if($item instanceof Opt_Xml_Element)
- {
- if($item->getXmlName() == 'opt:show')
- {
- $nameAttr = $item->getAttribute('name');
- if(!is_null($nameAttr) && $nameAttr->getValue() == $name)
- {
- return $item;
- }
- }
- }
- }
- }
- else
- {
- // Here we do not need to check, whether the name in opt:show matches the argument.
- while(!is_null($item = $item->getParent()))
- {
- if($item instanceof Opt_Xml_Element)
- {
- if($item->getXmlName() == 'opt:show')
- {
- return $item;
- }
- }
- }
- }
- return NULL;
- } // end _findShowNode();
- /**
- * The section parameter parsing is used in several places, so the
- * code was moved to a separate method.
- *
- * @internal
- * @param Opt_Xml_Element $node Parse the attributes from this node.
- * @param Array $extraAttributes=NULL Extra section attributes
- * @return Array The extracted attributes.
- */
- private function _extractSectionAttributes(Opt_Xml_Element $node, $extraAttributes = NULL)
- {
- $params = array(
- 'name' => array(0 => self::REQUIRED, self::ID),
- 'parent' => array(0 => self::OPTIONAL, self::ID_EMP, NULL),
- 'datasource' => array(0 => self::OPTIONAL, self::EXPRESSION, NULL),
- 'order' => array(0 => self::OPTIONAL, self::ID, 'asc'),
- 'display' => array(0 => self::OPTIONAL, self::EXPRESSION, NULL),
- 'separator' => array(0 => self::OPTIONAL, self::EXPRESSION, NULL),
- );
- // The instruction may add some extra attributes.
- if(!is_null($extraAttributes))
- {
- $params = array_merge($params, $extraAttributes);
- }
- $this->_extractAttributes($node, $params);
- return $params;
- } // end _extractSectionAttributes();
- /**
- * Validates the section record, determines the section parent and
- * the data format.
- *
- * @internal
- * @param Array &$section The section record.
- */
- private function _validateSection(Array &$section)
- {
- // Verify the value of the "order" attribute.
- if($section['order'] != 'asc' && $section['order'] != 'desc')
- {
- throw new Opt_InvalidAttributeType_Exception('order', $node->getXmlName(), '"asc" or "desc"');
- }
- // Determine the parent of the specified section.
- // if the attribute is not set, the default behaviour is to find the nearest
- // top-level and active section and link to it. Otherwise we must check if
- // the chosen section exists and is active.
- // Note that "parent" is ignored when we set "datasource"
- if(is_null($section['parent']))
- {
- if(self::$_stack->count() > 0)
- {
- $section['parent'] = self::$_stack->top();
- }
- }
- elseif($section['parent'] != '')
- {
- if(is_null(self::getSection($section['parent'])))
- {
- $exception = new Opt_SectionNotFound_Exception('parent', $section['parent']);
- $sections = array();
- self::$_stack->setIteratorMode(SplDoublyLinkedList::IT_MODE_LIFO | SplDoublyLinkedList::IT_MODE_KEEP);
- foreach(self::$_stack as $up)
- {
- $sections[] = $up;
- }
- $exception->setData($sections);
- throw $exception;
- }
- }
- else
- {
- // For the situation, if we had 'parent=""' in the template.
- $section['parent'] = null;
- }
- $section['nesting'] = self::countSections() + 1;
- // Now we need to obtain the information about the data format.
- $section['format'] = $this->_compiler->getFormat($section['name']);
- if(!$section['format']->supports('section'))
- {
- throw new Opt_FormatNotSupported_Exception($section['format']->getName(), 'section');
- }
- } // end _validateSection();
- /**
- * Adds the new section record to the stack.
- *
- * @static
- * @internal
- * @param Array $info The section record.
- */
- static private function _addSection(Array $info)
- {
- if(isset(self::$_sections[$info['name']]))
- {
- throw new Opt_SectionExists_Exception('adding section', $info['name']);
- }
- self::$_sections[$info['name']] = $info;
- self::$_stack->push($info['name']);
- } // end _addSectionInfo();
- /**
- * Removes the specified section from the stack. The name
- * is provided to check, if the order of the closing is
- * valid.
- *
- * @static
- * @internal
- * @param String $name The section name.
- */
- static private function _removeSection($name)
- {
- if(self::$_stack->count() == 0)
- {
- throw new Opt_ObjectNotExists_Exception('section', $name);
- }
- $name2 = self::$_stack->pop();
- if($name != $name2)
- {
- throw new Opl_Debug_Generic_Exception('OPT: Invalid section name thrown from the stack. Expected: '.$name.'; Actual: '.$name2);
- }
- unset(self::$_sections[$name]);
- } // end _removeSection;
- /**
- * A dummy opt:show processor that actually does nothing, because
- * it must wait for the right section instruction. If such instruction
- * will not appear, the node will be parsed in the postprocessing.
- *
- * @internal
- * @param Opt_Xml_Node $node The node
- */
- protected function _processShow(Opt_Xml_Node $node)
- {
- $node->set('postprocess', true);
- $this->_sortSectionContents($node, 'opt', 'showelse');
- $this->_process($node);
- } // end _processShow();
- /**
- * Finalizes the opt:show attribute. If there was no section in opt:show,
- * it initializes a section for a moment just to generate the condition,
- * but does not add it to the stack.
- *
- * @param Opt_Xml_Node $node The node.
- */
- protected function _postprocessShow(Opt_Xml_Node $node)
- {
- if(!is_null($node->get('priv:initialized')))
- {
- return;
- }
- $section = $this->_extractSectionAttributes($node, null);
- $section['show'] = $node;
- $section['node'] = null;
- $section['attribute'] = null;
- $this->_validateSection($section);
- $this->_createShowCondition($node, $section);
- } // end _postprocessShow();
- /**
- * An utility method that cleans the contents of the section node, by
- * moving the alternative section (opt:sectionelse etc. tags) code to the end
- * of the children list.
- *
- * In the parameters, we must specify the name and the namespace of the
- * tags that will be treated as the alternative content tags.
- *
- * @param Opt_Xml_Element $node The section node
- * @param String $ns The namespace
- * @param String $name The alternative section content tag name
- */
- protected function _sortSectionContents(Opt_Xml_Element $node, $ns, $name)
- {
- $else = $node->getElementsByTagNameNS($ns, $name, false);
-
- if(sizeof($else) == 1)
- {
- if(!$node->hasAttributes())
- {
- throw new Opt_InstructionTooManyItems_Exception($ns.':'.$name, $node->getXmlName(), 'Zero');
- }
- $node->bringToEnd($else[0]);
- }
- elseif(sizeof($else) > 1)
- {
- throw new Opt_InstructionTooManyItems_Exception($ns.':'.$name, $node->getXmlName(), 'Zero or one');
- }
- } // end _locateElse();
- /**
- * Processes the system variable $sys for the sections.
- *
- * @param Array $opt The system variable call splitted into separate identifiers.
- * @return String The output PHP code.
- */
- public function processSystemVar($opt)
- {
- if(sizeof($opt) < 4)
- {
- throw new Opt_SysVariableLength_Exception('$'.implode('.',$opt), 'short');
- }
- // Determine the section
- $section = self::getSection($opt[2]);
- if(is_null($section))
- {
- throw new Opt_SectionNotFound_Exception('OPT variable $'.implode('.',$opt), $opt[2]);
- }
- switch($opt[3])
- {
- case 'count':
- return $section['format']->get('section:count');
- case 'iterator':
- return $section['format']->get('section:iterator');
- case 'size':
- return $section['format']->get('section:size');
- case 'first':
- return $section['format']->get('section:isFirst');
- case 'last':
- return $section['format']->get('section:isLast');
- case 'extreme':
- return $section['format']->get('section:isExtreme');
- default:
- $result = $this->_processSystemVar($opt);
- if(is_null($result))
- {
- throw new Opt_SysVariableUnknown_Exception('$'.implode('.',$opt));
- }
- return $result;
- }
- } // end processSystemVar();
- /**
- * Allows the sections to handle specific uses of $sys special variable.
- *
- * @param Array $opt The system variable call splitted into separate identifiers.
- * @return String The output PHP code.
- */
- protected function _processSystemVar($opt)
- {
- return NULL;
- } // end _processSystemVar();
- } // end Opt_Instruction_BaseSection;