/** * Compiles traversing of hash values * * - Evaluated expression must be a zval * - A key must be a zval * - A value must be a zval * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileHashTraverse($expression, CompilationContext $compilationContext, Variable $exprVariable) { $codePrinter = $compilationContext->codePrinter; /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { if ($this->_statement['key'] != '_') { $keyVariable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['key'], $compilationContext, $this->_statement['expr']); if ($keyVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in hash traversal", $this->_statement['expr']); } } else { $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); $keyVariable->setDynamicTypes('undefined'); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { if ($this->_statement['value'] != '_') { $variable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['value'], $compilationContext, $this->_statement['expr']); if ($variable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['value'] . " type: " . $variable->getType() . " as value in hash traversal", $this->_statement['expr']); } } else { $variable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); } $variable->setMustInitNull(true); $variable->setIsInitialized(true, $compilationContext, $this->_statement); $variable->setDynamicTypes('undefined'); } /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; $compilationContext->headersManager->add('kernel/hash'); $duplicateHash = '0'; $duplicateKey = true; /** * We have to check if hashes are modified within the for's block */ if (isset($this->_statement['statements'])) { /** * Create the statements block here to obtain the last use line */ $st = new StatementsBlock($this->_statement['statements']); $detector = new ForValueUseDetector(); if ($detector->detect($exprVariable->getName(), $this->_statement['statements'])) { $duplicateHash = '1'; } /** * Detect if the key is modified or passed to an external scope */ if (isset($this->_statement['key'])) { if (!$keyVariable->isTemporal()) { $detector->setDetectionFlags(ForValueUseDetector::DETECT_ALL); if ($detector->detect($keyVariable->getName(), $this->_statement['statements'])) { $loopContext = $compilationContext->currentMethod->getLocalContextPass(); //echo $st->getLastLine(); //echo $loopContext->getLastVariableUseLine($keyVariable->getName()); $duplicateKey = true; } } } } $compilationContext->backend->forStatement($exprVariable, isset($this->_statement['key']) ? $keyVariable : null, isset($this->_statement['value']) ? $variable : null, $duplicateKey, $duplicateHash, $this->_statement, $st, $compilationContext); /** * Restore the cycle counter */ $compilationContext->insideCycle--; }
/** * Compiles traversing of hash values * - Evaluated expression must be a zval * - Every key must be a zval * - Every value must be a zval * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileHashTraverse($expression, $compilationContext, $exprVariable) { $codePrinter = $compilationContext->codePrinter; /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { $keyVariable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['key'], $compilationContext, $this->_statement['expr']); if ($keyVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in hash traversal", $this->_statement['expr']); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); $keyVariable->setDynamicTypes('undefined'); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { $variable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['value'], $compilationContext, $this->_statement['expr']); if ($variable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['value'] . " type: " . $variable->getType() . " as value in hash traversal", $this->_statement['expr']); } $variable->setMustInitNull(true); $variable->setIsInitialized(true, $compilationContext, $this->_statement); $variable->setDynamicTypes('undefined'); } /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; /** * Create a hash table and hash pointer temporary variables */ $arrayPointer = $compilationContext->symbolTable->addTemp('HashPosition', $compilationContext); $arrayHash = $compilationContext->symbolTable->addTemp('HashTable', $compilationContext); /** * Create a temporary zval to fetch the items from the hash */ $tempVariable = $compilationContext->symbolTable->addTemp('variable', $compilationContext); $tempVariable->setIsDoublePointer(true); $compilationContext->headersManager->add('kernel/hash'); /** * We have to check if hashes are modified within the for's block */ $duplicateHash = '0'; if (isset($this->_statement['statements'])) { $detector = new ForValueUseDetector(); if ($detector->detect($exprVariable->getName(), $this->_statement['statements'])) { $duplicateHash = '1'; } } $codePrinter->output('zephir_is_iterable(' . $expression->getCode() . ', &' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ', ' . $duplicateHash . ', ' . $this->_statement['reverse'] . ', "' . Compiler::getShortUserPath($this->_statement['file']) . '", ' . $this->_statement['line'] . ');'); $codePrinter->output('for ('); $codePrinter->output(' ; zephir_hash_get_current_data_ex(' . $arrayHash->getName() . ', (void**) &' . $tempVariable->getName() . ', &' . $arrayPointer->getName() . ') == SUCCESS'); if ($this->_statement['reverse']) { $codePrinter->output(' ; zephir_hash_move_backwards_ex(' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ')'); } else { $codePrinter->output(' ; zephir_hash_move_forward_ex(' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ')'); } $codePrinter->output(') {'); if (isset($this->_statement['key'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HMKEY(' . $this->_statement['key'] . ', ' . $arrayHash->getName() . ', ' . $arrayPointer->getName() . ');'); } if (isset($this->_statement['value'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HVALUE(' . $this->_statement['value'] . ', ' . $tempVariable->getName() . ');'); } /** * Compile statements in the 'for' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->compile($compilationContext); } /** * Restore the cycle counter */ $compilationContext->insideCycle--; $codePrinter->output('}'); }