/** * Uses the specified variable within the context. * * @param string $variable The variable name. * @param boolean $isGlobal Is the variable global or local? * @param string $contextFormat The format enforced by the occurence context. * @return array */ public function useVariable($variable, $type, $isGlobal, $contextFormat = null) { if (!isset($this->_variables[$type . $variable])) { // In this case the variable has not been used yet. We must check // if the user have not selected any format for it, calculate the // default format and modify the context format. $manager = $this->_compiler->getCdfManager(); try { // In case of template variables, we should not check the data formats // because we would run into several problems. We immediately jump to the // format resolution algorithm. if ($type == '@') { throw new Exception(); } // OK, look for a variable $this->_variables[$type . $variable] = $manager->getFormat('variable', $variable, $this); return array('format' => $this->_variables[$type . $variable], 'replacement' => null, 'cast' => null); } catch (Exception $exception) { if ($contextFormat === null) { // This is very strange. It seems that someone has used // an uninitialized variable, so we can set it to null and // report it as an unused variable. Opt_Support::warning('Uninitialized variable ' . $variable . ' - casting to NULL'); return array('format' => null, 'replacement' => 'null'); } else { $manager->addFormat('variable', $variable, $contextFormat, $this->getElementLocation('variable', $variable)); $this->_variables[$type . $variable] = $manager->getFormat('variable', $variable, $this); return array('format' => $this->_variables[$type . $variable], 'replacement' => null, 'cast' => null); } } } else { // The variable has already been used. We must match the // previously selected format. if ($contextFormat !== null) { return array('format' => $this->_variables[$type . $variable], 'replacement' => null, 'cast' => $this->_variables[$type . $variable]->getName()); } else { return array('format' => $this->_variables[$type . $variable], 'replacement' => null); } } }
/** * Compiles the variable call in the specified context. It processes the containers, * assignments and other stuff directly related to the variables, returning an * SplFixedArray object with token information. * * @param array $variable The list of container elements * @param string $type The variable type * @param integer $weight The expression weight * @param integer $context The variable occurence context (normal, assignment, etc.) * @param string $contextInfo The information provided by the context * @param string $extra The support for object and array calls * @return SplFixedArray */ public function _compileVariable(array $variable, $type, $weight, $context = 0, $contextInfo = null, $extra = null) { $conversion = '##simplevar_'; $manager = $this->_compiler->getCdfManager(); $defaultFormat = null; if ($type == '@') { $conversion = '##var_'; $defaultFormat = 'TemplateVariable'; } $state = array('further' => false, 'section' => null); $answer = new SplFixedArray(4); $answer[2] = 'Scalar'; $answer[3] = 0; // If we have an assignment context, we must mark it in the created // expression node for the _finalize() method in order to choose // an appropriate expression type. if ($context == self::CONTEXT_ASSIGN) { $answer[3] = 1; } // The variable scanner $proc = null; if ($this->_compiler->isProcessor('section') !== null) { $proc = $this->_compiler->processor('section'); } $count = sizeof($variable); $final = $count - 1; $localWeight = 0; $code = ''; $path = ''; $previous = null; foreach ($variable as $id => $item) { // Handle conversions $previous = $path; if ($path == '') { // Parsing the first element. First, check the conversions. if (($to = $this->_compiler->convert($conversion . $item[0])) != $conversion . $item[0]) { $item = $to; } $path = $item; $state['first'] = true; } else { // Parsing one of the later elements $path .= '.' . $item; $state['first'] = false; } // Processing section calls if ($proc !== null) { if ($state['section'] === null) { // Check if any section with the specified name exists. $sectionName = $this->_compiler->convert($item[0]); if (($section = $proc->getSection($sectionName)) !== null) { $path = $sectionName; $state['section'] = $section; if ($id == $final) { // This is the last element $hook = 'section:item' . $this->_dfCalls[$context]; if (!$section['format']->property($hook)) { throw new Opt_OperationNotSupported_Exception($name, $this->_dfCalls[$context]); } $section['format']->assign('value', $contextInfo[0]); $section['format']->assign('code', $code); $code = $section['format']->get($hook); $localWeight = self::SECTION_ITEM_WEIGHT; break; } continue; } } else { // The section has been found, we need to process the item. // TODO: Perhaps INCREMENT and DECREMENT must have here a different code... // We must remember that the container call may be longer and they may refer // to the other part of the chain. $state['section']['format']->assign('item', $item); $hook = 'section:variable'; if ($id == $final) { $hook .= $this->_dfCalls[$context]; $section['format']->assign('value', $contextInfo[0]); $section['format']->assign('code', $code); if (!$section['format']->property($hook)) { throw new Opt_OperationNotSupported($name, $this->_dfCalls[$context]); } } $code = $section['format']->get($hook); $localWeight = self::SECTION_VARIABLE_WEIGHT; $state['section'] = null; continue; } } // Now, the normal container calls if ($id == 0) { // The first element processing $info = $this->_compiler->getContextStack()->top()->useVariable($item, $type, false, null); if ($info['replacement'] !== null) { $answer[0] = $info['replacement']; $answer[1] = Opt_Expression_Standard::SCALAR_WEIGHT; return $answer; } $format = $info['format']; $answer[2] = $format->getName(); if ($format->isDecorating()) { $answer[2] = $format; } if (!$format->supports('variable')) { throw new Opt_FormatNotSupported_Exception($format->getName(), 'variable'); } // Check if the format supports capturing the whole container if ($format->property('variable:capture')) { $format->assign('items', $variable); $format->assign('dynamic', $isDynamic); $hook = 'capture'; } else { $hook = 'item'; } $format->assign('item', $item); if ($context > 0) { $format->assign('value', $contextInfo[0]); $format->assign('code', $code); if (!$format->property('variable:' . $hook . $this->_dfCalls[$context])) { throw new Opt_OperationNotSupported_Exception($path, ltrim($this->_dfCalls[$context], '.')); } } $code = $format->get('variable:' . $hook . $this->_dfCalls[$context]); $localWeight = $count * self::CONTAINER_ITEM_WEIGHT; if ($hook == 'capture') { break; } } else { $format = $manager->getFormat('variable', $previous); $answer[2] = $format->getName(); if ($format->isDecorating()) { $answer[2] = $format; } $hook = 'item:item'; $format->assign('item', $item); if ($id == $final) { $hook .= $this->_dfCalls[$context]; $format->assign('value', $contextInfo[0]); $format->assign('code', $code); if ($context > 0 && !$format->property($hook)) { throw new Opt_OperationNotSupported_Exception($path, $this->_dfCalls[$context]); } $code .= $format->get($hook); } else { $code .= $format->get($hook); } $localWeight += self::CONTAINER_ITEM_WEIGHT; } } $answer[0] = $code; $answer[1] = $localWeight + $weight; return $answer; }