From 656d611957f74d527133e55dc6589a6035bcb962 Mon Sep 17 00:00:00 2001 From: dreamsxin Date: Tue, 31 Dec 2019 16:38:04 +0800 Subject: [PATCH] Fix #2020 --- Library/Backends/ZendEngine2/Backend.php | 14 + Library/Backends/ZendEngine3/Backend.php | 12 + Library/BaseBackend.php | 2 + Library/Statements/Let/StaticPropertyAdd.php | 324 ++++++++++++++++++ Library/Statements/Let/StaticPropertySub.php | 324 ++++++++++++++++++ Library/Statements/LetStatement.php | 14 +- kernels/ZendEngine3/object.c | 16 + kernels/ZendEngine3/object.h | 2 + test/properties/staticpublicproperties.zep | 9 + .../Properties/StaticPublicPropertiesTest.php | 10 + 10 files changed, 726 insertions(+), 1 deletion(-) create mode 100644 Library/Statements/Let/StaticPropertyAdd.php create mode 100644 Library/Statements/Let/StaticPropertySub.php diff --git a/Library/Backends/ZendEngine2/Backend.php b/Library/Backends/ZendEngine2/Backend.php index 005007c1d1..55a1b136f4 100644 --- a/Library/Backends/ZendEngine2/Backend.php +++ b/Library/Backends/ZendEngine2/Backend.php @@ -704,6 +704,20 @@ public function updateStaticProperty($classEntry, $property, $value, Compilation ); } + public function addStaticProperty($classEntry, $property, $value, CompilationContext $context) + { + throw new CompilerException( + 'ZendEngine2 backend is no longer supported' + ); + } + + public function subStaticProperty($classEntry, $property, $value, CompilationContext $context) + { + throw new CompilerException( + 'ZendEngine2 backend is no longer supported' + ); + } + public function assignArrayProperty(Variable $variable, $property, $key, $value, CompilationContext $context) { throw new CompilerException( diff --git a/Library/Backends/ZendEngine3/Backend.php b/Library/Backends/ZendEngine3/Backend.php index fc64b9fc76..edddfed39f 100644 --- a/Library/Backends/ZendEngine3/Backend.php +++ b/Library/Backends/ZendEngine3/Backend.php @@ -956,6 +956,18 @@ public function updateStaticProperty($classEntry, $property, $value, Compilation $context->codePrinter->output('zephir_update_static_property_ce('.$classEntry.', ZEND_STRL("'.$property.'"), '.$value.');'); } + public function addStaticProperty($classEntry, $property, $value, CompilationContext $context) + { + $value = $this->resolveValue($value, $context); + $context->codePrinter->output('zephir_add_static_property_ce('.$classEntry.', ZEND_STRL("'.$property.'"), '.$value.');'); + } + + public function subStaticProperty($classEntry, $property, $value, CompilationContext $context) + { + $value = $this->resolveValue($value, $context); + $context->codePrinter->output('zephir_sub_static_property_ce('.$classEntry.', ZEND_STRL("'.$property.'"), '.$value.');'); + } + public function assignArrayProperty(Variable $variable, $property, $key, $value, CompilationContext $context) { $resolveValue = $this->resolveValue($value, $context); diff --git a/Library/BaseBackend.php b/Library/BaseBackend.php index 80ae94a2ef..7015c67304 100644 --- a/Library/BaseBackend.php +++ b/Library/BaseBackend.php @@ -223,6 +223,8 @@ abstract public function fetchStaticProperty(Variable $symbolVariable, $classDef abstract public function updateProperty(Variable $symbolVariable, $propertyName, $value, CompilationContext $compilationContext); abstract public function updateStaticProperty($classEntry, $property, $value, CompilationContext $context); + abstract public function addStaticProperty($classEntry, $property, $value, CompilationContext $context); + abstract public function subStaticProperty($classEntry, $property, $value, CompilationContext $context); abstract public function assignArrayProperty(Variable $variable, $property, $key, $value, CompilationContext $context); diff --git a/Library/Statements/Let/StaticPropertyAdd.php b/Library/Statements/Let/StaticPropertyAdd.php new file mode 100644 index 0000000000..3e258c904a --- /dev/null +++ b/Library/Statements/Let/StaticPropertyAdd.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Zephir\Statements\Let; + +use Zephir\CompilationContext; +use Zephir\CompiledExpression; +use Zephir\Exception; +use Zephir\Exception\CompilerException; +use Zephir\Exception\IllegalOperationException; +use Zephir\Expression; + +/** + * StaticPropertyAdd. + * + * Updates static properties + */ +class StaticPropertyAdd +{ + /** + * Compiles ClassName::foo = {expr}. + * + * @param string $className + * @param string $property + * @param CompiledExpression $resolvedExpr + * @param CompilationContext $compilationContext + * @param array $statement + * + * @throws CompilerException + * @throws IllegalOperationException + * + * @internal param string $variable + */ + public function assignStatic( + string $className, + string $property, + CompiledExpression $resolvedExpr, + CompilationContext $compilationContext, + array $statement + ) { + $classDefinition = $compilationContext->classLookup($className); + + if (!$propertyDefinition = $classDefinition->getProperty($property)) { + throw new CompilerException( + sprintf( + "Class '%s' does not have a property called: '%s", + $classDefinition->getCompleteName(), + $property + ), + $statement + ); + } + + if (!$propertyDefinition->isStatic()) { + throw new CompilerException( + sprintf( + "Cannot access non-static property '%s::%s", + $classDefinition->getCompleteName(), + $property + ), + $statement + ); + } + + if ($propertyDefinition->isPrivate()) { + if ($classDefinition->getCompleteName() != $compilationContext->classDefinition->getCompleteName()) { + throw new CompilerException( + sprintf( + "Cannot access private static property '%s::%s out of its declaring context", + $classDefinition->getCompleteName(), + $property + ), + $statement + ); + } + } + + $codePrinter = $compilationContext->codePrinter; + $compilationContext->headersManager->add('kernel/object'); + + try { + $classEntry = $classDefinition->getClassEntry($compilationContext); + } catch (Exception $e) { + throw new CompilerException($e->getMessage(), $statement, $e->getCode(), $e); + } + + 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->addStaticProperty($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->addStaticProperty($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->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'string': + // TODO: add-assign + if ('assign' !== $statement['operator']) { + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $resolvedExpr->getType(), + $statement + ); + } + + $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); + } + + $compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + break; + + case 'bool': + if ('1' == $resolvedExpr->getBooleanCode()) { + $compilationContext->backend->addStaticProperty($classEntry, $property, 'true', $compilationContext); + } else { + if ('0' == $resolvedExpr->getBooleanCode()) { + $compilationContext->backend->addStaticProperty($classEntry, $property, 'false', $compilationContext); + } else { + $codePrinter->output('if ('.$resolvedExpr->getBooleanCode().') {'); + $codePrinter->increaseLevel(); + $compilationContext->backend->addStaticProperty($classEntry, $property, 'true', $compilationContext); + $codePrinter->decreaseLevel(); + $codePrinter->output('} else {'); + $codePrinter->increaseLevel(); + $compilationContext->backend->addStaticProperty($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->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'array': + $compilationContext->backend->addStaticProperty($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': + // TODO: mul-assign, div-assign, sub-assign, add-assign + if ('assign' !== $statement['operator']) { + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $variableVariable->getType(), + $statement + ); + } + + $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable( + 'variable', + $compilationContext, + true + ); + + $compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext); + $compilationContext->backend->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'double': + // TODO: mul-assign, div-assign, sub-assign, add-assign + if ('assign' !== $statement['operator']) { + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $variableVariable->getType(), + $statement + ); + } + + $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable( + 'variable', + $compilationContext, + true + ); + + $compilationContext->backend->assignDouble($tempVariable, $variableVariable, $compilationContext); + $compilationContext->backend->addStaticProperty($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->addStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'string': + switch ($statement['operator']) { + /* @noinspection PhpMissingBreakStatementInspection */ + case 'concat-assign': + $tempVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify( + 'variable', + $compilationContext + ); + $expression = new Expression([ + 'type' => 'static-property-access', + 'left' => [ + 'value' => $statement['variable'], + ], + 'right' => [ + 'value' => $statement['property'], + ], + ]); + $expression->setExpectReturn(true, $tempVariable); + + try { + $expression->compile($compilationContext); + } catch (Exception $e) { + throw new CompilerException($e->getMessage(), $statement, $e->getCode(), $e); + } + + $variableVariableCode = $compilationContext->backend->getVariableCode($variableVariable); + $tempVariableCode = $compilationContext->backend->getVariableCode($tempVariable); + if ('&' === substr($variableVariableCode, 0, 1)) { + $compilationContext->codePrinter->output('SEPARATE_ZVAL_IF_NOT_REF('.$variableVariableCode.');'); + } else { + $compilationContext->codePrinter->output('SEPARATE_ZVAL_IF_NOT_REF(&'.$variableVariableCode.');'); + } + $compilationContext->codePrinter->output('zephir_concat_function('.$variableVariableCode.', '.$tempVariableCode.', '.$variableVariableCode.');'); + // no break + case 'assign': + $compilationContext->backend->addStaticProperty($classEntry, $property, $variableVariable, $compilationContext); + if ($variableVariable->isTemporal()) { + $variableVariable->setIdle(true); + } + break; + default: + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $variableVariable->getType(), + $statement + ); + } + break; + case 'variable': + case 'array': + $compilationContext->backend->addStaticProperty($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); + } + } +} diff --git a/Library/Statements/Let/StaticPropertySub.php b/Library/Statements/Let/StaticPropertySub.php new file mode 100644 index 0000000000..2a1711977d --- /dev/null +++ b/Library/Statements/Let/StaticPropertySub.php @@ -0,0 +1,324 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Zephir\Statements\Let; + +use Zephir\CompilationContext; +use Zephir\CompiledExpression; +use Zephir\Exception; +use Zephir\Exception\CompilerException; +use Zephir\Exception\IllegalOperationException; +use Zephir\Expression; + +/** + * StaticPropertySub. + * + * Updates static properties + */ +class StaticPropertySub +{ + /** + * Compiles ClassName::foo = {expr}. + * + * @param string $className + * @param string $property + * @param CompiledExpression $resolvedExpr + * @param CompilationContext $compilationContext + * @param array $statement + * + * @throws CompilerException + * @throws IllegalOperationException + * + * @internal param string $variable + */ + public function assignStatic( + string $className, + string $property, + CompiledExpression $resolvedExpr, + CompilationContext $compilationContext, + array $statement + ) { + $classDefinition = $compilationContext->classLookup($className); + + if (!$propertyDefinition = $classDefinition->getProperty($property)) { + throw new CompilerException( + sprintf( + "Class '%s' does not have a property called: '%s", + $classDefinition->getCompleteName(), + $property + ), + $statement + ); + } + + if (!$propertyDefinition->isStatic()) { + throw new CompilerException( + sprintf( + "Cannot access non-static property '%s::%s", + $classDefinition->getCompleteName(), + $property + ), + $statement + ); + } + + if ($propertyDefinition->isPrivate()) { + if ($classDefinition->getCompleteName() != $compilationContext->classDefinition->getCompleteName()) { + throw new CompilerException( + sprintf( + "Cannot access private static property '%s::%s out of its declaring context", + $classDefinition->getCompleteName(), + $property + ), + $statement + ); + } + } + + $codePrinter = $compilationContext->codePrinter; + $compilationContext->headersManager->add('kernel/object'); + + try { + $classEntry = $classDefinition->getClassEntry($compilationContext); + } catch (Exception $e) { + throw new CompilerException($e->getMessage(), $statement, $e->getCode(), $e); + } + + 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->subStaticProperty($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->subStaticProperty($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->subStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'string': + // TODO: add-assign + if ('assign' !== $statement['operator']) { + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $resolvedExpr->getType(), + $statement + ); + } + + $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); + } + + $compilationContext->backend->subStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + break; + + case 'bool': + if ('1' == $resolvedExpr->getBooleanCode()) { + $compilationContext->backend->subStaticProperty($classEntry, $property, 'true', $compilationContext); + } else { + if ('0' == $resolvedExpr->getBooleanCode()) { + $compilationContext->backend->subStaticProperty($classEntry, $property, 'false', $compilationContext); + } else { + $codePrinter->output('if ('.$resolvedExpr->getBooleanCode().') {'); + $codePrinter->increaseLevel(); + $compilationContext->backend->subStaticProperty($classEntry, $property, 'true', $compilationContext); + $codePrinter->decreaseLevel(); + $codePrinter->output('} else {'); + $codePrinter->increaseLevel(); + $compilationContext->backend->subStaticProperty($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->subStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'array': + $compilationContext->backend->subStaticProperty($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': + // TODO: mul-assign, div-assign, sub-assign, add-assign + if ('assign' !== $statement['operator']) { + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $variableVariable->getType(), + $statement + ); + } + + $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable( + 'variable', + $compilationContext, + true + ); + + $compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext); + $compilationContext->backend->subStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'double': + // TODO: mul-assign, div-assign, sub-assign, add-assign + if ('assign' !== $statement['operator']) { + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $variableVariable->getType(), + $statement + ); + } + + $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable( + 'variable', + $compilationContext, + true + ); + + $compilationContext->backend->assignDouble($tempVariable, $variableVariable, $compilationContext); + $compilationContext->backend->subStaticProperty($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->subStaticProperty($classEntry, $property, $tempVariable, $compilationContext); + if ($tempVariable->isTemporal()) { + $tempVariable->setIdle(true); + } + break; + + case 'string': + switch ($statement['operator']) { + /* @noinspection PhpMissingBreakStatementInspection */ + case 'concat-assign': + $tempVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify( + 'variable', + $compilationContext + ); + $expression = new Expression([ + 'type' => 'static-property-access', + 'left' => [ + 'value' => $statement['variable'], + ], + 'right' => [ + 'value' => $statement['property'], + ], + ]); + $expression->setExpectReturn(true, $tempVariable); + + try { + $expression->compile($compilationContext); + } catch (Exception $e) { + throw new CompilerException($e->getMessage(), $statement, $e->getCode(), $e); + } + + $variableVariableCode = $compilationContext->backend->getVariableCode($variableVariable); + $tempVariableCode = $compilationContext->backend->getVariableCode($tempVariable); + if ('&' === substr($variableVariableCode, 0, 1)) { + $compilationContext->codePrinter->output('SEPARATE_ZVAL_IF_NOT_REF('.$variableVariableCode.');'); + } else { + $compilationContext->codePrinter->output('SEPARATE_ZVAL_IF_NOT_REF(&'.$variableVariableCode.');'); + } + $compilationContext->codePrinter->output('zephir_concat_function('.$variableVariableCode.', '.$tempVariableCode.', '.$variableVariableCode.');'); + // no break + case 'assign': + $compilationContext->backend->subStaticProperty($classEntry, $property, $variableVariable, $compilationContext); + if ($variableVariable->isTemporal()) { + $variableVariable->setIdle(true); + } + break; + default: + throw CompilerException::illegalOperationTypeOnStaticVariable( + $statement['operator'], + $variableVariable->getType(), + $statement + ); + } + break; + case 'variable': + case 'array': + $compilationContext->backend->subStaticProperty($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); + } + } +} diff --git a/Library/Statements/LetStatement.php b/Library/Statements/LetStatement.php index 5c27a2b766..e225a1c043 100644 --- a/Library/Statements/LetStatement.php +++ b/Library/Statements/LetStatement.php @@ -30,6 +30,8 @@ use Zephir\Statements\Let\ObjectPropertyDecr as LetObjectPropertyDecr; use Zephir\Statements\Let\ObjectPropertyIncr as LetObjectPropertyIncr; use Zephir\Statements\Let\StaticProperty as LetStaticProperty; +use Zephir\Statements\Let\StaticPropertyAdd as LetStaticPropertyAdd; +use Zephir\Statements\Let\StaticPropertySub as LetStaticPropertySub; use Zephir\Statements\Let\StaticPropertyAppend as LetStaticPropertyAppend; use Zephir\Statements\Let\StaticPropertyArrayIndex as LetStaticPropertyArrayIndex; use Zephir\Statements\Let\StaticPropertyArrayIndexAppend as LetStaticPropertyArrayIndexAppend; @@ -162,7 +164,17 @@ public function compile(CompilationContext $compilationContext) break; case 'static-property': - $let = new LetStaticProperty(); + if (isset($assignment['operator'])) { + if ('add-assign' == $assignment['operator']) { + $let = new LetStaticPropertyAdd(); + } else if ('sub-assign' == $assignment['operator']) { + $let = new LetStaticPropertySub(); + } else { + $let = new LetStaticProperty(); + } + } else { + $let = new LetStaticProperty(); + } $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; diff --git a/kernels/ZendEngine3/object.c b/kernels/ZendEngine3/object.c index ae647bc1c9..07f9637e28 100644 --- a/kernels/ZendEngine3/object.c +++ b/kernels/ZendEngine3/object.c @@ -1046,6 +1046,22 @@ int zephir_update_static_property_ce(zend_class_entry *ce, const char *property_ //#endif } +int zephir_add_static_property_ce(zend_class_entry *ce, const char *property_name, uint32_t property_length, zval *value) +{ + zval tmp_value, new_value; + zephir_read_static_property_ce(&tmp_value, ce, property_name, property_length, PH_NOISY | PH_READONLY); + zephir_add_function(&new_value, &tmp_value, value); + return zend_update_static_property(ce, property_name, property_length, &new_value); +} + +int zephir_sub_static_property_ce(zend_class_entry *ce, const char *property_name, uint32_t property_length, zval *value) +{ + zval tmp_value, new_value; + zephir_read_static_property_ce(&tmp_value, ce, property_name, property_length, PH_NOISY | PH_READONLY); + zephir_sub_function(&new_value, &tmp_value, value); + return zend_update_static_property(ce, property_name, property_length, &new_value); +} + /* * Multiple array-offset update */ diff --git a/kernels/ZendEngine3/object.h b/kernels/ZendEngine3/object.h index c58edca35d..9bdf4168ac 100644 --- a/kernels/ZendEngine3/object.h +++ b/kernels/ZendEngine3/object.h @@ -65,6 +65,8 @@ int zephir_unset_property_array(zval *object, char *property, unsigned int prope /** Static properties */ int zephir_read_static_property_ce(zval *result, zend_class_entry *ce, const char *property, int len, int flags); int zephir_update_static_property_ce(zend_class_entry *ce, const char *property, uint32_t len, zval *value); +int zephir_add_static_property_ce(zend_class_entry *ce, const char *property, uint32_t len, zval *value); +int zephir_sub_static_property_ce(zend_class_entry *ce, const char *property, uint32_t len, zval *value); int zephir_update_static_property_array_multi_ce(zend_class_entry *ce, const char *property, uint32_t property_length, zval *value, const char *types, int types_length, int types_count, ...); /** Create closures */ diff --git a/test/properties/staticpublicproperties.zep b/test/properties/staticpublicproperties.zep index a82d813439..bc864c5508 100644 --- a/test/properties/staticpublicproperties.zep +++ b/test/properties/staticpublicproperties.zep @@ -39,8 +39,17 @@ class StaticPublicProperties */ public static someString = "test"; + public static someAdd = 0; + public static someSub = 0; + public static function setSomeString(val) { let self::someString = val; } + + public static function testAddAndSub() + { + let self::someAdd += 1; + let self::someSub -= 1; + } } \ No newline at end of file diff --git a/unit-tests/Extension/Properties/StaticPublicPropertiesTest.php b/unit-tests/Extension/Properties/StaticPublicPropertiesTest.php index 476d43f2a2..273ae65118 100644 --- a/unit-tests/Extension/Properties/StaticPublicPropertiesTest.php +++ b/unit-tests/Extension/Properties/StaticPublicPropertiesTest.php @@ -38,4 +38,14 @@ public function testIssues1904() // \Test\Properties\StaticPublicProperties::setSomeString('test3'); // $this->assertSame(\Test\Properties\StaticPublicProperties::$someString, $value); } + + public function testIssues2020() + { + \Test\Properties\StaticPublicProperties::testAddAndSub(); + $this->assertEquals(1, \Test\Properties\StaticPublicProperties::$someAdd); + $this->assertEquals(-1, \Test\Properties\StaticPublicProperties::$someSub); + \Test\Properties\StaticPublicProperties::testAddAndSub(); + $this->assertEquals(2, \Test\Properties\StaticPublicProperties::$someAdd); + $this->assertEquals(-2, \Test\Properties\StaticPublicProperties::$someSub); + } }