/** * Compiles ClassName::foo[index] = {expr} * * @param $className * @param $property * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * * @throws CompilerException * @internal param string $variable */ public function assignStatic($className, $property, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $compiler = $compilationContext->compiler; if (!in_array($className, array('self', 'static', 'parent'))) { $className = $compilationContext->getFullName($className); if ($compiler->isClass($className)) { $classDefinition = $compiler->getClassDefinition($className); } else { if ($compiler->isBundledClass($className)) { $classDefinition = $compiler->getInternalClassDefinition($className); } else { throw new CompilerException("Cannot locate class '" . $className . "'", $statement); } } } else { if (in_array($className, array('self', 'static'))) { $classDefinition = $compilationContext->classDefinition; } else { if ($className == 'parent') { $classDefinition = $compilationContext->classDefinition; $extendsClass = $classDefinition->getExtendsClass(); if (!$extendsClass) { throw new CompilerException('Cannot assign static property "' . $property . '" on parent because class ' . $classDefinition->getCompleteName() . ' does not extend any class', $statement); } else { $classDefinition = $classDefinition->getExtendsClassDefinition(); } } } } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement); } /** @var $propertyDefinition ClassProperty */ $propertyDefinition = $classDefinition->getProperty($property); if (!$propertyDefinition->isStatic()) { throw new CompilerException("Cannot access non-static property '" . $classDefinition->getCompleteName() . '::' . $property . "'", $statement); } if ($propertyDefinition->isPrivate()) { if ($classDefinition != $compilationContext->classDefinition) { throw new CompilerException("Cannot access private static property '" . $classDefinition->getCompleteName() . '::' . $property . "' out of its declaring context", $statement); } } $compilationContext->headersManager->add('kernel/object'); $classEntry = $classDefinition->getClassEntry($compilationContext); $this->_assignStaticPropertyArrayMultipleIndex($classEntry, $property, $resolvedExpr, $compilationContext, $statement); }
/** * Compiles ClassName::foo = {expr} * * @param $className * @param $property * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * * @throws CompilerException * @internal param string $variable */ public function assignStatic($className, $property, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $compiler = $compilationContext->compiler; if (!in_array($className, array('self', 'static', 'parent'))) { $className = $compilationContext->getFullName($className); if ($compiler->isClass($className)) { $classDefinition = $compiler->getClassDefinition($className); } else { if ($compiler->isBundledClass($className)) { $classDefinition = $compiler->getInternalClassDefinition($className); } else { throw new CompilerException("Cannot locate class '" . $className . "'", $statement); } } } else { if (in_array($className, array('self', 'static'))) { $classDefinition = $compilationContext->classDefinition; } else { if ($className == 'parent') { $classDefinition = $compilationContext->classDefinition; $extendsClass = $classDefinition->getExtendsClass(); if (!$extendsClass) { throw new CompilerException('Cannot assign static property "' . $property . '" on parent because class ' . $classDefinition->getCompleteName() . ' does not extend any class', $statement); } else { $classDefinition = $classDefinition->getExtendsClassDefinition(); } } } } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement); } /** @var $propertyDefinition ClassProperty */ $propertyDefinition = $classDefinition->getProperty($property); if (!$propertyDefinition->isStatic()) { throw new CompilerException("Cannot access non-static property '" . $classDefinition->getCompleteName() . '::' . $property . "'", $statement); } if ($propertyDefinition->isPrivate()) { if ($classDefinition != $compilationContext->classDefinition) { throw new CompilerException("Cannot access private static property '" . $classDefinition->getCompleteName() . '::' . $property . "' out of its declaring context", $statement); } } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/object'); $classEntry = $classDefinition->getClassEntry($compilationContext); switch ($resolvedExpr->getType()) { case 'null': $compilationContext->backend->updateStaticProperty($classEntry, $property, 'null', $compilationContext); break; case 'int': case 'uint': case 'long': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, '\'' . $resolvedExpr->getCode() . '\'', $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': switch ($statement['operator']) { case 'assign': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $tempVariable->initVariant($compilationContext); if ($resolvedExpr->getCode()) { $compilationContext->backend->assignString($tempVariable, $resolvedExpr->getCode(), $compilationContext); } else { $codePrinter->output('ZVAL_EMPTY_STRING(' . $tempVariable->getName() . ');'); } if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } $codePrinter->output('zephir_update_static_property_ce(' . $classEntry . ', SL("' . $property . '"), &' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'bool': if ($resolvedExpr->getBooleanCode() == '1') { $compilationContext->backend->updateStaticProperty($classEntry, $property, 'true', $compilationContext); } else { if ($resolvedExpr->getBooleanCode() == '0') { $compilationContext->backend->updateStaticProperty($classEntry, $property, 'false', $compilationContext); } else { $codePrinter->output('if (' . $resolvedExpr->getBooleanCode() . ') {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateStaticProperty($classEntry, $property, 'true', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateStaticProperty($classEntry, $property, 'false', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); } } break; case 'empty-array': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->initArray($tempVariable, $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'array': $compilationContext->backend->updateStaticProperty($classEntry, $property, $resolvedExpr, $compilationContext); break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext); if ($compilationContext->insideCycle) { $propertyCache = $compilationContext->symbolTable->getTempVariableForWrite('zend_property_info', $compilationContext); $propertyCache->setMustInitNull(true); $propertyCache->setReusable(false); $codePrinter->output('zephir_update_static_property_ce_cache(' . $classEntry . ', SL("' . $property . '"), &' . $tempVariable->getName() . ', &' . $propertyCache->getName() . ' TSRMLS_CC);'); } else { $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); } if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $variableVariable, $compilationContext); if ($compilationContext->insideCycle) { $propertyCache = $compilationContext->symbolTable->getTempVariableForWrite('zend_property_info', $compilationContext); $propertyCache->setMustInitNull(true); $propertyCache->setReusable(false); $codePrinter->output('zephir_update_static_property_ce_cache(' . $classEntry . ', SL("' . $property . '"), &' . $tempVariable->getName() . ', &' . $propertyCache->getName() . ' TSRMLS_CC);'); } else { $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); } if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignBool($tempVariable, $variableVariable, $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': switch ($statement['operator']) { case 'concat-assign': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $expression = new Expression(array('type' => 'static-property-access', 'left' => array('value' => $statement['variable']), 'right' => array('value' => $statement['property']))); $expression->setExpectReturn(true, $tempVariable); $expression->compile($compilationContext); $variableVariableCode = $compilationContext->backend->getVariableCode($variableVariable); $tempVariableCode = $compilationContext->backend->getVariableCode($tempVariable); $compilationContext->codePrinter->output('zephir_concat_function(' . $variableVariableCode . ', ' . $tempVariableCode . ', ' . $variableVariableCode . ');'); //continue //continue case 'assign': $compilationContext->backend->updateStaticProperty($classEntry, $property, $variableVariable, $compilationContext); if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'variable': case 'array': $compilationContext->backend->updateStaticProperty($classEntry, $property, $variableVariable, $compilationContext); if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } break; default: throw new CompilerException("Unknown type " . $variableVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } }
/** * Access a static property * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { $className = $expression['left']['value']; $compiler = $compilationContext->compiler; $property = $expression['right']['value']; /** * Fetch the class definition according to the class where the constant * is supposed to be declared */ if (!in_array($className, array('self', 'static', 'parent'))) { $className = $compilationContext->getFullName($className); if ($compiler->isClass($className)) { $classDefinition = $compiler->getClassDefinition($className); } else { if ($compiler->isBundledClass($className)) { $classDefinition = $compiler->getInternalClassDefinition($className); } else { throw new CompilerException("Cannot locate class '" . $className . "'", $expression['left']); } } } else { if (in_array($className, array('self', 'static'))) { $classDefinition = $compilationContext->classDefinition; } else { if ($className == 'parent') { $classDefinition = $compilationContext->classDefinition; $extendsClass = $classDefinition->getExtendsClass(); if (!$extendsClass) { throw new CompilerException('Cannot access static property "' . $property . '" on parent because class ' . $classDefinition->getCompleteName() . ' does not extend any class', $expression); } else { $classDefinition = $classDefinition->getExtendsClassDefinition(); } } } } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $expression); } /** @var $propertyDefinition ClassProperty */ $propertyDefinition = $classDefinition->getProperty($property); if (!$propertyDefinition->isStatic()) { throw new CompilerException("Cannot access non-static property '" . $classDefinition->getCompleteName() . '::' . $property . "'", $expression); } if ($propertyDefinition->isPrivate()) { if ($classDefinition != $compilationContext->classDefinition) { throw new CompilerException("Cannot access private static property '" . $classDefinition->getCompleteName() . '::' . $property . "' out of its declaring context", $expression); } } if ($propertyDefinition->isProtected()) { if ($classDefinition != $compilationContext->classDefinition && $classDefinition != $compilationContext->classDefinition->getExtendsClassDefinition()) { throw new CompilerException("Cannot access protected static property '" . $classDefinition->getCompleteName() . '::' . $property . "' out of its declaring context", $expression); } } /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; if ($symbolVariable->getName() == 'return_value') { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } /** * Variable that receives property accesses must be polymorphic */ if (!$symbolVariable->isVariable()) { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign class constants", $expression); } $symbolVariable->setDynamicTypes('undefined'); $compilationContext->headersManager->add('kernel/object'); $readOnly = $this->_readOnly; if (!$readOnly) { if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); } } $compilationContext->backend->fetchStaticProperty($symbolVariable, $classDefinition, $property, $this->_readOnly, $compilationContext); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Access a static constant class * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { $compiler =& $compilationContext->compiler; $className = $expression['left']['value']; $constant = $expression['right']['value']; /** * Fetch the class definition according to the class where the constant * is supposed to be declared */ if (!in_array($className, array('this', 'self', 'static', 'parent'))) { $className = $compilationContext->getFullName($className); if ($compiler->isClass($className) || $compiler->isInterface($className)) { $classDefinition = $compiler->getClassDefinition($className); } else { if ($compiler->isBundledClass($className) || $compiler->isBundledInterface($className)) { $classDefinition = $compiler->getInternalClassDefinition($className); } else { throw new CompilerException("Cannot locate class '" . $className . "'", $expression['left']); } } } else { if (in_array($className, array('self', 'static', 'this'))) { $classDefinition = $compilationContext->classDefinition; } else { if ($className == 'parent') { $classDefinition = $compilationContext->classDefinition; $extendsClass = $classDefinition->getExtendsClass(); if (!$extendsClass) { throw new CompilerException('Cannot find constant called "' . $constant . '" on parent because class ' . $classDefinition->getCompleteName() . ' does not extend any class', $expression); } else { $classDefinition = $classDefinition->getExtendsClassDefinition(); } } } } /** * Constants are resolved to their values at compile time * so we need to check that they effectively do exist */ if (!$classDefinition->hasConstant($constant)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a constant called: '" . $constant . "'", $expression); } /** * We can optimize the reading of constants by avoiding query their value every time */ if (!$compilationContext->config->get('static-constant-class-folding', 'optimizations')) { /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; $symbolVariable->initVariant($compilationContext); } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } /** * Variable that receives property accesses must be polymorphic */ if (!$symbolVariable->isVariable()) { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign class constants", $expression); } $symbolVariable->setDynamicTypes('undefined'); $compilationContext->headersManager->add('kernel/object'); $compilationContext->codePrinter->output('zephir_get_class_constant(' . $symbolVariable->getName() . ', ' . $classDefinition->getClassEntry($compilationContext) . ', SS("' . $constant . '") TSRMLS_CC);'); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } $constantDefinition = $classDefinition->getConstant($constant); if ($constantDefinition instanceof ClassConstant) { $value = $constantDefinition->getValueValue(); $type = $constantDefinition->getValueType(); } else { $value = $constantDefinition; $type = gettype($value); if ($type == 'integer') { $type = 'int'; } } switch ($type) { case 'string': case 'int': case 'double': case 'float': case 'bool': case 'null': break; default: $compilationContext->logger->warning($constantDefinition->getName(), 'nonexistent-constant', $expression); return new CompiledExpression('null', null, $expression); } /** * Return the value as a literal expression */ return new CompiledExpression($type, $value, $expression); }