From 59742cec100c73ca277eb02c86295fbdec8ca2f7 Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Sat, 18 Sep 2021 22:59:05 +0100 Subject: [PATCH 1/7] #2276 - Remove deprecated method `isStringOperation()` --- Library/Expression.php | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/Library/Expression.php b/Library/Expression.php index b9fd3f0aa..3c80c9d1a 100644 --- a/Library/Expression.php +++ b/Library/Expression.php @@ -11,6 +11,7 @@ namespace Zephir; +use ReflectionException; use Zephir\Exception\CompilerException; use Zephir\Expression\Closure; use Zephir\Expression\ClosureArrow; @@ -200,19 +201,6 @@ public function setStringOperation($stringOperation) $this->stringOperation = $stringOperation; } - /** - * Checks if the result of the evaluated expression is intended to be used - * in a string operation like "concat". - * - * @deprecated - * - * @return bool - */ - public function isStringOperation() - { - return $this->stringOperation; - } - /** * Sets if the expression is being evaluated in an evaluation like the ones in 'if' and 'while' statements. * @@ -226,14 +214,14 @@ public function setEvalMode($evalMode) /** * Compiles foo = []. * - * @param array $expression + * @param array $expression * @param CompilationContext $compilationContext * * @return CompiledExpression */ - public function emptyArray($expression, CompilationContext $compilationContext) + public function emptyArray(array $expression, CompilationContext $compilationContext): CompiledExpression { - /* + /** * Resolves the symbol that expects the value */ if ($this->expecting) { @@ -254,7 +242,7 @@ public function emptyArray($expression, CompilationContext $compilationContext) throw new CompilerException('Cannot use variable: '.$symbolVariable->getName().'('.$symbolVariable->getType().') to create empty array', $expression); } - /* + /** * Mark the variable as an 'array' */ $symbolVariable->setDynamicTypes('array'); @@ -269,15 +257,14 @@ public function emptyArray($expression, CompilationContext $compilationContext) * * @param CompilationContext $compilationContext * - * @throws CompilerException|Exception - * * @return CompiledExpression + * @throws Exception + * @throws ReflectionException */ public function compile(CompilationContext $compilationContext): CompiledExpression { $expression = $this->expression; $type = $expression['type']; - $compilableExpression = null; switch ($type) { case 'null': From 41d1ca6e89c0407902feb6887b5104ae6e61ebce Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Sat, 18 Sep 2021 23:43:52 +0100 Subject: [PATCH 2/7] #2276 - Add new property `mixed` in `ClassMethod` with own logic --- Library/ClassMethod.php | 69 ++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/Library/ClassMethod.php b/Library/ClassMethod.php index 2973b13bd..fd4acb45c 100644 --- a/Library/ClassMethod.php +++ b/Library/ClassMethod.php @@ -96,6 +96,15 @@ class ClassMethod */ protected bool $void = false; + /** + * Whether the variable is mixed. + * + * Only for PHP >= 8.0 + * + * @var bool + */ + protected bool $mixed = false; + /** * Whether the method is public or not. * @@ -276,34 +285,42 @@ public function setReturnTypes(?array $returnType = null): void $castTypes = []; foreach ($returnType['list'] as $returnTypeItem) { - if (isset($returnTypeItem['cast'])) { - if (isset($returnTypeItem['cast']['collection'])) { - continue; - } + /** + * We continue the loop, because it only works for PHP >= 8.0. + */ + if (isset($returnTypeItem['data-type']) && $returnTypeItem['data-type'] === 'mixed') { + $this->mixed = true; + } - if (isset($returnTypeItem['collection']) && $returnTypeItem['collection']) { - $types['array'] = [ - 'type' => 'return-type-parameter', - 'data-type' => 'array', - 'mandatory' => 0, - 'file' => $returnTypeItem['cast']['file'], - 'line' => $returnTypeItem['cast']['line'], - 'char' => $returnTypeItem['cast']['char'], - ]; - } else { - $castTypes[$returnTypeItem['cast']['value']] = $returnTypeItem['cast']['value']; - } - } else { + if (!isset($returnTypeItem['cast'])) { $types[$returnTypeItem['data-type']] = $returnTypeItem; + continue; + } + + if (isset($returnTypeItem['cast']['collection'])) { + continue; + } + + if (isset($returnTypeItem['collection']) && $returnTypeItem['collection']) { + $types['array'] = [ + 'type' => 'return-type-parameter', + 'data-type' => 'array', + 'mandatory' => 0, + 'file' => $returnTypeItem['cast']['file'], + 'line' => $returnTypeItem['cast']['line'], + 'char' => $returnTypeItem['cast']['char'], + ]; + } else { + $castTypes[$returnTypeItem['cast']['value']] = $returnTypeItem['cast']['value']; } } - if (count($castTypes)) { + if (count($castTypes) > 0) { $types['object'] = []; $this->returnClassTypes = $castTypes; } - if (count($types)) { + if (count($types) > 0) { $this->returnTypes = $types; } } @@ -514,7 +531,7 @@ public function setupOptimized(CompilationContext $compilationContext): self return $this; } - public function getOptimizedMethod() + public function getOptimizedMethod(): ClassMethod { $optimizedName = $this->getName().'_zephir_internal_call'; $optimizedMethod = $this->classDefinition->getMethod($optimizedName, false); @@ -633,7 +650,7 @@ public function areReturnTypesNullCompatible(): bool */ public function areReturnTypesIntCompatible(): bool { - $types = ['int', 'uint', 'char', 'uchar', 'long', 'ulong']; + $types = ['int', 'uint', 'char', 'uchar', 'long', 'ulong', 'mixed']; foreach ($this->returnTypes as $returnType => $definition) { if (in_array($returnType, $types)) { @@ -847,6 +864,16 @@ public function isVoid(): bool return $this->void; } + /** + * Checks if the methods return type is `mixed`. + * + * @return bool + */ + public function isMixed(): bool + { + return $this->mixed; + } + /** * Checks if the method is inline. * From cd331d15625e2b3648214a1f5f7fa0d18f11bf03 Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Sat, 18 Sep 2021 23:44:41 +0100 Subject: [PATCH 3/7] #2276 - Add PHP version check for `IS_MIXED` in return types --- Library/ArgInfoDefinition.php | 50 ++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/Library/ArgInfoDefinition.php b/Library/ArgInfoDefinition.php index e8165bcca..14d69169e 100644 --- a/Library/ArgInfoDefinition.php +++ b/Library/ArgInfoDefinition.php @@ -208,16 +208,41 @@ private function richRenderStart(): void return; } - $this->codePrinter->output( - sprintf( - 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)', - $this->name, - (int) $this->returnByRef, - $this->functionLike->getNumberOfRequiredParameters(), - $this->getReturnType(), - (int) $this->functionLike->areReturnTypesNullCompatible() - ) - ); + if ($this->functionLike->isMixed()) { + $this->codePrinter->output('#if PHP_VERSION_ID >= 80000'); + $this->codePrinter->output( + sprintf( + 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, IS_MIXED, %d)', + $this->name, + (int) $this->returnByRef, + $this->functionLike->getNumberOfRequiredParameters(), + (int) $this->functionLike->areReturnTypesNullCompatible() + ) + ); + $this->codePrinter->output('#else'); + $this->codePrinter->output( + sprintf( + 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)', + $this->name, + (int) $this->returnByRef, + $this->functionLike->getNumberOfRequiredParameters(), + $this->getReturnType(), + (int) $this->functionLike->areReturnTypesNullCompatible() + ) + ); + $this->codePrinter->output('#endif'); + } else { + $this->codePrinter->output( + sprintf( + 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)', + $this->name, + (int) $this->returnByRef, + $this->functionLike->getNumberOfRequiredParameters(), + $this->getReturnType(), + (int) $this->functionLike->areReturnTypesNullCompatible() + ) + ); + } } private function renderEnd(): void @@ -396,6 +421,11 @@ private function passByReference(array $parameter) private function getReturnType(): string { + // TODO: Come back here when PHP7.4 is deprecated. + /*if (array_key_exists('mixed', $this->functionLike->getReturnTypes())) { + return 'IS_MIXED'; + }*/ + if ($this->functionLike->areReturnTypesIntCompatible()) { return 'IS_LONG'; } From c0dd5e8a9c4ac5a7c3fc6fec384569f30e7f21e3 Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Sun, 19 Sep 2021 00:17:36 +0100 Subject: [PATCH 4/7] #2276 - Add support for `mixed` in return types --- Library/ArgInfoDefinition.php | 10 +- Library/ClassMethod.php | 2 +- Library/Exception/InvalidTypeException.php | 4 +- Library/Statements/ReturnStatement.php | 38 +++--- ext/config.m4 | 3 +- ext/config.w32 | 3 +- ext/stub.c | 2 + ext/stub.h | 1 + ext/stub/types/mixedtype.zep.c | 130 +++++++++++++++++++++ ext/stub/types/mixedtype.zep.h | 91 +++++++++++++++ stub/types/mixedtype.zep | 55 +++++++++ tests/Extension/Types/MixedTest.php | 45 +++++++ 12 files changed, 358 insertions(+), 26 deletions(-) create mode 100644 ext/stub/types/mixedtype.zep.c create mode 100644 ext/stub/types/mixedtype.zep.h create mode 100644 stub/types/mixedtype.zep create mode 100644 tests/Extension/Types/MixedTest.php diff --git a/Library/ArgInfoDefinition.php b/Library/ArgInfoDefinition.php index 14d69169e..76c54051d 100644 --- a/Library/ArgInfoDefinition.php +++ b/Library/ArgInfoDefinition.php @@ -31,7 +31,7 @@ class ArgInfoDefinition /** * @var string */ - private string $name = ''; + private string $name; /** * @var ClassMethodParameters|null @@ -61,18 +61,18 @@ class ArgInfoDefinition /** * ArgInfoDefinition constructor. * - * @param $name + * @param string $name * @param ClassMethod $functionLike * @param CodePrinter $codePrinter * @param CompilationContext $compilationContext - * @param false $returnByRef + * @param bool $returnByRef */ public function __construct( - $name, + string $name, ClassMethod $functionLike, CodePrinter $codePrinter, CompilationContext $compilationContext, - $returnByRef = false + bool $returnByRef = false ) { $this->functionLike = $functionLike; $this->codePrinter = $codePrinter; diff --git a/Library/ClassMethod.php b/Library/ClassMethod.php index fd4acb45c..1fb7583e0 100644 --- a/Library/ClassMethod.php +++ b/Library/ClassMethod.php @@ -650,7 +650,7 @@ public function areReturnTypesNullCompatible(): bool */ public function areReturnTypesIntCompatible(): bool { - $types = ['int', 'uint', 'char', 'uchar', 'long', 'ulong', 'mixed']; + $types = ['int', 'uint', 'char', 'uchar', 'long', 'ulong']; foreach ($this->returnTypes as $returnType => $definition) { if (in_array($returnType, $types)) { diff --git a/Library/Exception/InvalidTypeException.php b/Library/Exception/InvalidTypeException.php index be1542a78..fad4b82b4 100644 --- a/Library/Exception/InvalidTypeException.php +++ b/Library/Exception/InvalidTypeException.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Exception; final class InvalidTypeException extends CompilerException @@ -19,7 +21,7 @@ final class InvalidTypeException extends CompilerException * @param string $type * @param array|null $expression */ - public function __construct($type, array $expression = null) + public function __construct(string $type, array $expression = null) { $message = sprintf( 'Returning type: %s but this type is not compatible with return-type hints declared in the method', diff --git a/Library/Statements/ReturnStatement.php b/Library/Statements/ReturnStatement.php index 37f7f28d0..65877d5d3 100644 --- a/Library/Statements/ReturnStatement.php +++ b/Library/Statements/ReturnStatement.php @@ -11,13 +11,16 @@ namespace Zephir\Statements; -use function Zephir\add_slashes; +use ReflectionException; use Zephir\CompilationContext; +use Zephir\Exception; use Zephir\Exception\CompilerException; use Zephir\Exception\InvalidTypeException; use Zephir\Expression; use Zephir\Types; +use function Zephir\add_slashes; + /** * ReturnStatement. * @@ -28,9 +31,10 @@ final class ReturnStatement extends StatementAbstract /** * @param CompilationContext $compilationContext * - * @throws CompilerException + * @throws Exception + * @throws ReflectionException */ - public function compile(CompilationContext $compilationContext) + public function compile(CompilationContext $compilationContext): void { $statement = $this->statement; @@ -50,14 +54,14 @@ public function compile(CompilationContext $compilationContext) ); } - /* + /** * Use return member for properties on this */ if ('property-access' == $statement['expr']['type']) { if ('variable' == $statement['expr']['left']['type']) { if ('this' == $statement['expr']['left']['value']) { if ('variable' == $statement['expr']['right']['type']) { - /* + /** * If the property is accessed on 'this', we check if the property does exist. */ $property = $statement['expr']['right']['value']; @@ -82,7 +86,7 @@ public function compile(CompilationContext $compilationContext) } } - /* + /** * Fetches return_value and tries to return the value directly there. */ $variable = $compilationContext->symbolTable->getVariable('return_value'); @@ -92,13 +96,13 @@ public function compile(CompilationContext $compilationContext) $expr->setReadOnly(true); $resolvedExpr = $expr->compile($compilationContext); - /* + /** * Here we check if the variable returns a compatible type according to its type hints */ if ($currentMethod->hasReturnTypes()) { switch ($resolvedExpr->getType()) { case Types::T_NULL: - if (false == $currentMethod->areReturnTypesNullCompatible()) { + if (!$currentMethod->areReturnTypesNullCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; @@ -109,26 +113,26 @@ public function compile(CompilationContext $compilationContext) case Types::T_ULONG: case Types::T_CHAR: case Types::T_UCHAR: - if (false == $currentMethod->areReturnTypesIntCompatible()) { + if (!$currentMethod->areReturnTypesIntCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; case Types::T_BOOL: - if (false == $currentMethod->areReturnTypesBoolCompatible()) { + if (!$currentMethod->areReturnTypesBoolCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; case Types::T_DOUBLE: - if (false == $currentMethod->areReturnTypesDoubleCompatible()) { + if (!$currentMethod->areReturnTypesDoubleCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; case Types::T_STRING: case Types::T_ISTRING: - if (false == $currentMethod->areReturnTypesStringCompatible()) { + if (!$currentMethod->areReturnTypesStringCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; @@ -147,26 +151,26 @@ public function compile(CompilationContext $compilationContext) case Types::T_ULONG: case Types::T_CHAR: case Types::T_UCHAR: - if (false == $currentMethod->areReturnTypesIntCompatible()) { + if (!$currentMethod->areReturnTypesIntCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; case Types::T_DOUBLE: - if (false == $currentMethod->areReturnTypesDoubleCompatible()) { + if (!$currentMethod->areReturnTypesDoubleCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; case Types::T_STRING: case Types::T_ISTRING: - if (false == $currentMethod->areReturnTypesStringCompatible()) { + if (!$currentMethod->areReturnTypesStringCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; case Types::T_BOOL: - if (false == $currentMethod->areReturnTypesBoolCompatible()) { + if (!$currentMethod->areReturnTypesBoolCompatible() && !$currentMethod->isMixed()) { throw new InvalidTypeException($resolvedExpr->getType(), $statement['expr']); } break; @@ -314,7 +318,7 @@ public function compile(CompilationContext $compilationContext) return; } - /* + /** * Return without an expression */ $codePrinter->output('RETURN_MM_NULL();'); diff --git a/ext/config.m4 b/ext/config.m4 index cceffb657..8552cc50a 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -212,6 +212,7 @@ if test "$PHP_STUB" = "yes"; then stub/typehinting/testabstract.zep.c stub/typeinstances.zep.c stub/typeoff.zep.c + stub/types/mixedtype.zep.c stub/unknownclass.zep.c stub/unsettest.zep.c stub/usetest.zep.c @@ -232,7 +233,7 @@ if test "$PHP_STUB" = "yes"; then stub/13__closure.zep.c " PHP_NEW_EXTENSION(stub, $stub_sources, $ext_shared,, ) PHP_ADD_BUILD_DIR([$ext_builddir/kernel/]) - for dir in "stub stub/bench stub/builtin stub/constructors stub/flow stub/globals stub/globals/session stub/integration/psr stub/integration/psr/http/message stub/interfaces stub/invokes stub/issue2165 stub/mcall stub/namespaces stub/namespaces/a/b stub/oo stub/oo/extend stub/oo/extend/db stub/oo/extend/db/query stub/oo/extend/db/query/placeholder stub/oo/extend/spl stub/oo/scopes stub/ooimpl stub/optimizers stub/properties stub/requires stub/router stub/typehinting"; do + for dir in "stub stub/bench stub/builtin stub/constructors stub/flow stub/globals stub/globals/session stub/integration/psr stub/integration/psr/http/message stub/interfaces stub/invokes stub/issue2165 stub/mcall stub/namespaces stub/namespaces/a/b stub/oo stub/oo/extend stub/oo/extend/db stub/oo/extend/db/query stub/oo/extend/db/query/placeholder stub/oo/extend/spl stub/oo/scopes stub/ooimpl stub/optimizers stub/properties stub/requires stub/router stub/typehinting stub/types"; do PHP_ADD_BUILD_DIR([$ext_builddir/$dir]) done PHP_SUBST(STUB_SHARED_LIBADD) diff --git a/ext/config.w32 b/ext/config.w32 index 12199ac28..7cc2da406 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -37,7 +37,8 @@ if (PHP_STUB != "no") { ADD_SOURCES(configure_module_dirname + "/stub/optimizers", "isscalar.zep.c acos.zep.c arraymerge.zep.c asin.zep.c cos.zep.c createarray.zep.c ldexp.zep.c sin.zep.c sqrt.zep.c strreplace.zep.c substr.zep.c tan.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/requires", "external3.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/router", "exception.zep.c route.zep.c", "stub"); - ADD_SOURCES(configure_module_dirname + "/stub/typehinting", "testabstract.zep.c", "stub"); + ADD_SOURCES(configure_module_dirname + "/stub/typehinting", "testabstract.zep.c", "stub"); + ADD_SOURCES(configure_module_dirname + "/stub/types", "mixedtype.zep.c", "stub"); ADD_FLAG("CFLAGS_STUB", "/D ZEPHIR_RELEASE /Oi /Ot /Oy /Ob2 /Gs /GF /Gy /GL"); ADD_FLAG("CFLAGS", "/D ZEPHIR_RELEASE /Oi /Ot /Oy /Ob2 /Gs /GF /Gy /GL"); ADD_FLAG("LDFLAGS", "/LTCG"); diff --git a/ext/stub.c b/ext/stub.c index e1f353ba9..c4e86bcae 100644 --- a/ext/stub.c +++ b/ext/stub.c @@ -240,6 +240,7 @@ zend_class_entry *stub_trytest_ce; zend_class_entry *stub_typehinting_testabstract_ce; zend_class_entry *stub_typeinstances_ce; zend_class_entry *stub_typeoff_ce; +zend_class_entry *stub_types_mixedtype_ce; zend_class_entry *stub_unknownclass_ce; zend_class_entry *stub_unsettest_ce; zend_class_entry *stub_usetest_ce; @@ -465,6 +466,7 @@ static PHP_MINIT_FUNCTION(stub) ZEPHIR_INIT(Stub_TypeHinting_TestAbstract); ZEPHIR_INIT(Stub_TypeInstances); ZEPHIR_INIT(Stub_Typeoff); + ZEPHIR_INIT(Stub_Types_MixedType); ZEPHIR_INIT(Stub_UnknownClass); ZEPHIR_INIT(Stub_Unsettest); ZEPHIR_INIT(Stub_UseTest); diff --git a/ext/stub.h b/ext/stub.h index da8ea50cb..cf7b456b2 100644 --- a/ext/stub.h +++ b/ext/stub.h @@ -207,6 +207,7 @@ #include "stub/typehinting/testabstract.zep.h" #include "stub/typeinstances.zep.h" #include "stub/typeoff.zep.h" +#include "stub/types/mixedtype.zep.h" #include "stub/unknownclass.zep.h" #include "stub/unsettest.zep.h" #include "stub/usetest.zep.h" diff --git a/ext/stub/types/mixedtype.zep.c b/ext/stub/types/mixedtype.zep.c new file mode 100644 index 000000000..3659d7286 --- /dev/null +++ b/ext/stub/types/mixedtype.zep.c @@ -0,0 +1,130 @@ + +#ifdef HAVE_CONFIG_H +#include "../../ext_config.h" +#endif + +#include +#include "../../php_ext.h" +#include "../../ext.h" + +#include +#include +#include + +#include "kernel/main.h" +#include "kernel/object.h" +#include "kernel/operators.h" +#include "kernel/memory.h" + + +ZEPHIR_INIT_CLASS(Stub_Types_MixedType) +{ + ZEPHIR_REGISTER_CLASS(Stub\\Types, MixedType, stub, types_mixedtype, stub_types_mixedtype_method_entry, 0); + + return SUCCESS; +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedObject) +{ + zval *this_ptr = getThis(); + + + + object_init(return_value); + return; +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedArray) +{ + zval *this_ptr = getThis(); + + + + array_init(return_value); + return; +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedString) +{ + zval *this_ptr = getThis(); + + + + RETURN_STRING("mixed string"); +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedInt) +{ + zval *this_ptr = getThis(); + + + + RETURN_LONG(1); +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedFloat) +{ + zval *this_ptr = getThis(); + + + + RETURN_DOUBLE(3.14); +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedBool) +{ + zval *this_ptr = getThis(); + + + + RETURN_BOOL(1); +} + +PHP_METHOD(Stub_Types_MixedType, returnMixedNull) +{ + zval *this_ptr = getThis(); + + + + RETURN_NULL(); +} + +PHP_METHOD(Stub_Types_MixedType, returnMixed74) +{ + zval *diff_param = NULL; + zend_bool diff; + zval *this_ptr = getThis(); + +#if PHP_VERSION_ID >= 80000 + bool is_null_true = 1; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_BOOL(diff) + ZEND_PARSE_PARAMETERS_END(); +#endif + + + zephir_fetch_params_without_memory_grow(0, 1, &diff_param); + if (!diff_param) { + diff = 0; + } else { + diff = zephir_get_boolval(diff_param); + } + + + if (diff) { + RETURN_STRING("string"); + } + object_init(return_value); + return; +} + +PHP_METHOD(Stub_Types_MixedType, returnMultiButAlwaysMixed) +{ + zval *this_ptr = getThis(); + + + + RETURN_STRING("IS_MIXED in header"); +} + diff --git a/ext/stub/types/mixedtype.zep.h b/ext/stub/types/mixedtype.zep.h new file mode 100644 index 000000000..929cb0536 --- /dev/null +++ b/ext/stub/types/mixedtype.zep.h @@ -0,0 +1,91 @@ + +extern zend_class_entry *stub_types_mixedtype_ce; + +ZEPHIR_INIT_CLASS(Stub_Types_MixedType); + +PHP_METHOD(Stub_Types_MixedType, returnMixedObject); +PHP_METHOD(Stub_Types_MixedType, returnMixedArray); +PHP_METHOD(Stub_Types_MixedType, returnMixedString); +PHP_METHOD(Stub_Types_MixedType, returnMixedInt); +PHP_METHOD(Stub_Types_MixedType, returnMixedFloat); +PHP_METHOD(Stub_Types_MixedType, returnMixedBool); +PHP_METHOD(Stub_Types_MixedType, returnMixedNull); +PHP_METHOD(Stub_Types_MixedType, returnMixed74); +PHP_METHOD(Stub_Types_MixedType, returnMultiButAlwaysMixed); + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedobject, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedobject, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedarray, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedarray, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedstring, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedstring, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedint, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedint, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedfloat, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedfloat, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedbool, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedbool, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixednull, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixednull, 0, 0, IS_NULL, 0) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixed74, 0, 0, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixed74, 0, 0, IS_NULL, 0) +#endif + ZEND_ARG_TYPE_INFO(0, diff, _IS_BOOL, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_types_mixedtype_returnmultibutalwaysmixed, 0, 0, 0) +ZEND_END_ARG_INFO() + +ZEPHIR_INIT_FUNCS(stub_types_mixedtype_method_entry) { + PHP_ME(Stub_Types_MixedType, returnMixedObject, arginfo_stub_types_mixedtype_returnmixedobject, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixedArray, arginfo_stub_types_mixedtype_returnmixedarray, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixedString, arginfo_stub_types_mixedtype_returnmixedstring, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixedInt, arginfo_stub_types_mixedtype_returnmixedint, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixedFloat, arginfo_stub_types_mixedtype_returnmixedfloat, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixedBool, arginfo_stub_types_mixedtype_returnmixedbool, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixedNull, arginfo_stub_types_mixedtype_returnmixednull, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, returnMixed74, arginfo_stub_types_mixedtype_returnmixed74, ZEND_ACC_PUBLIC) +#if PHP_VERSION_ID >= 80000 + PHP_ME(Stub_Types_MixedType, returnMultiButAlwaysMixed, arginfo_stub_types_mixedtype_returnmultibutalwaysmixed, ZEND_ACC_PUBLIC) +#else + PHP_ME(Stub_Types_MixedType, returnMultiButAlwaysMixed, NULL, ZEND_ACC_PUBLIC) +#endif + PHP_FE_END +}; diff --git a/stub/types/mixedtype.zep b/stub/types/mixedtype.zep new file mode 100644 index 000000000..655336044 --- /dev/null +++ b/stub/types/mixedtype.zep @@ -0,0 +1,55 @@ + +namespace Stub\Types; + +class MixedType +{ + public function returnMixedObject() -> mixed + { + return new \stdClass(); + } + + public function returnMixedArray() -> mixed + { + return []; + } + + public function returnMixedString() -> mixed + { + return "mixed string"; + } + + public function returnMixedInt() -> mixed + { + return 1; + } + + public function returnMixedFloat() -> mixed + { + return 3.14; + } + + public function returnMixedBool() -> mixed + { + return true; + } + + public function returnMixedNull() -> mixed + { + return null; + } + + public function returnMixed74(bool diff = false) -> mixed + { + if (diff) { + return "string"; + } + + return new \stdClass(); + } + + public function returnMultiButAlwaysMixed() -> mixed | var | int + { + // Maybe in future make sense to generate with `IS_MIXED`. + return "ZEND_BEGIN_ARG_INFO_EX"; + } +} diff --git a/tests/Extension/Types/MixedTest.php b/tests/Extension/Types/MixedTest.php new file mode 100644 index 000000000..cf87d94b3 --- /dev/null +++ b/tests/Extension/Types/MixedTest.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Extension\Types; + +use PHPUnit\Framework\TestCase; +use Stub\Types\MixedType; + +final class MixedTest extends TestCase +{ + public function testReturnsOfMixedType(): void + { + $returns = new MixedType(); + + $this->assertEquals((new \stdClass()), $returns->returnMixedObject()); + $this->assertSame([], $returns->returnMixedArray()); + $this->assertSame('mixed string', $returns->returnMixedString()); + $this->assertSame(1, $returns->returnMixedInt()); + $this->assertSame(3.14, $returns->returnMixedFloat()); + $this->assertSame(true, $returns->returnMixedBool()); + $this->assertSame(null, $returns->returnMixedNull()); + + $this->assertNotNull($returns->returnMixedObject()); + $this->assertNotNull($returns->returnMixedArray()); + $this->assertNotNull($returns->returnMixedString()); + $this->assertNotNull($returns->returnMixedInt()); + $this->assertNotNull($returns->returnMixedFloat()); + $this->assertNotNull($returns->returnMixedBool()); + $this->assertNull($returns->returnMixedNull()); + + $this->assertEquals((new \stdClass()), $returns->returnMixed74()); + $this->assertSame('string', $returns->returnMixed74(true)); + $this->assertEquals((new \stdClass()), $returns->returnMixed74(false)); + } +} From 1d1784013dca13833cbf75f650816365fff64c26 Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Mon, 20 Sep 2021 14:13:45 +0100 Subject: [PATCH 5/7] #2276 - Add support for `mixed` type in parameters --- Library/Backends/ZendEngine3/Backend.php | 18 ++-- Library/ClassMethod.php | 4 +- Library/ClassMethodParameters.php | 1 + Library/Expression/NativeArray.php | 1 + Library/Statements/ReturnStatement.php | 4 + Library/SymbolTable.php | 8 +- ext/stub/types/mixedtype.zep.c | 119 ++++++++++++++++++++++- ext/stub/types/mixedtype.zep.h | 31 ++++++ stub/types/mixedtype.zep | 32 ++++++ tests/Extension/Types/MixedTest.php | 45 --------- tests/Extension/Types/MixedTypeTest.php | 86 ++++++++++++++++ 11 files changed, 291 insertions(+), 58 deletions(-) delete mode 100644 tests/Extension/Types/MixedTest.php create mode 100644 tests/Extension/Types/MixedTypeTest.php diff --git a/Library/Backends/ZendEngine3/Backend.php b/Library/Backends/ZendEngine3/Backend.php index de6fa8db5..b8bf043c3 100644 --- a/Library/Backends/ZendEngine3/Backend.php +++ b/Library/Backends/ZendEngine3/Backend.php @@ -105,7 +105,7 @@ public function getStringsManager() /** * {@inheritdoc} */ - public function getTypeDefinition($type) + public function getTypeDefinition($type): array { switch ($type) { case 'zend_ulong': @@ -154,6 +154,7 @@ public function getTypeDefinition($type) case 'variable': case 'array': case 'null': + case 'mixed': $pointer = '*'; $code = 'zval'; break; @@ -316,7 +317,7 @@ public function onPostCompile(ClassMethod $method, CompilationContext $context) public function generateInitCode(&$groupVariables, $type, $pointer, Variable $variable) { - $isComplex = \in_array($type, ['variable', 'string', 'array', 'resource', 'callable', 'object'], true); + $isComplex = \in_array($type, ['variable', 'string', 'array', 'resource', 'callable', 'object', 'mixed'], true); if ($isComplex && !$variable->isDoublePointer()) { /* && $variable->mustInitNull() */ $groupVariables[] = $variable->getName(); @@ -365,6 +366,7 @@ public function generateInitCode(&$groupVariables, $type, $pointer, Variable $va case 'resource': case 'callable': case 'object': + case 'mixed': $groupVariables[] = $pointer.$variable->getName(); break; @@ -594,7 +596,7 @@ public function addArrayEntry(Variable $variable, $key, $value, CompilationConte $keyType = 'append'; } elseif ($key instanceof CompiledExpression) { $typeKey = $key->getType(); - if ('variable' == $typeKey) { + if ('variable' === $typeKey || 'mixed' === $typeKey) { $var = $context->symbolTable->getVariableForRead($key->getCode(), $context); $typeKey = $var->getType(); } @@ -629,6 +631,7 @@ public function addArrayEntry(Variable $variable, $key, $value, CompilationConte case 'variable': case 'array': + case 'mixed': $type = 'zval'; break; } @@ -846,20 +849,21 @@ public function resolveValue($value, CompilationContext $context, $usePointer = $tempVariable = new Variable('variable', $varName, $context->branchManager->getCurrentBranch()); $context->symbolTable->addRawVariable($tempVariable); } + $tempVariable = $context->symbolTable->getVariableForWrite($varName, $context); $tempVariable->increaseUses(); - $tempVariable->setUsed(true, null); + $tempVariable->setUsed(true); + if ('null' == $value) { $tempVariable->setDynamicTypes('null'); } else { $tempVariable->setDynamicTypes('bool'); } + $value = $this->getVariableCode($tempVariable); } else { if ($value instanceof CompiledExpression) { - if ('array' == $value->getType()) { - $value = $context->symbolTable->getVariableForWrite($value->getCode(), $context, null); - } elseif ('variable' == $value->getType()) { + if (in_array($value->getType(), ['array', 'variable', 'mixed'])) { $value = $context->symbolTable->getVariableForWrite($value->getCode(), $context); } else { return $value->getCode(); diff --git a/Library/ClassMethod.php b/Library/ClassMethod.php index 1fb7583e0..003fe71f6 100644 --- a/Library/ClassMethod.php +++ b/Library/ClassMethod.php @@ -1447,7 +1447,7 @@ public function assignZvalValue(array $parameter, CompilationContext $compilatio { $dataType = $this->getParamDataType($parameter); - if (in_array($dataType, ['variable', 'callable', 'object', 'resource'])) { + if (in_array($dataType, ['variable', 'callable', 'object', 'resource', 'mixed'])) { return ""; } @@ -1652,6 +1652,7 @@ public function compile(CompilationContext $compilationContext): void case 'callable': case 'resource': case 'variable': + case 'mixed': $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext); /* TODO: Move this to the respective backend, which requires refactoring how this works */ if ($compilationContext->backend->isZE3()) { @@ -1878,6 +1879,7 @@ public function compile(CompilationContext $compilationContext): void case 'callable': case 'resource': case 'variable': + case 'mixed': $name = $parameter['name']; break; diff --git a/Library/ClassMethodParameters.php b/Library/ClassMethodParameters.php index 18de1afad..13e54efb5 100644 --- a/Library/ClassMethodParameters.php +++ b/Library/ClassMethodParameters.php @@ -115,6 +115,7 @@ public function fetchParameters(bool $isMethodInternal): array case 'callable': case 'resource': case 'variable': + case 'mixed': $parameters[] = $isMethodInternal ? $name : '&'.$name; break; diff --git a/Library/Expression/NativeArray.php b/Library/Expression/NativeArray.php index 23382881c..8e61539da 100644 --- a/Library/Expression/NativeArray.php +++ b/Library/Expression/NativeArray.php @@ -152,6 +152,7 @@ public function getArrayValue(CompiledExpression $exprCompiled, CompilationConte case 'string': case 'variable': case 'array': + case 'mixed': return $itemVariable; default: diff --git a/Library/Statements/ReturnStatement.php b/Library/Statements/ReturnStatement.php index 65877d5d3..5ffc637de 100644 --- a/Library/Statements/ReturnStatement.php +++ b/Library/Statements/ReturnStatement.php @@ -138,6 +138,7 @@ public function compile(CompilationContext $compilationContext): void break; case Types::T_VARIABLE: + case Types::T_MIXED: $symbolVariable = $compilationContext->symbolTable->getVariableForRead( $resolvedExpr->getCode(), $compilationContext, @@ -176,6 +177,7 @@ public function compile(CompilationContext $compilationContext): void break; case Types::T_VARIABLE: + case Types::T_MIXED: break; } break; @@ -221,6 +223,7 @@ public function compile(CompilationContext $compilationContext): void break; case Types::T_VARIABLE: + case Types::T_MIXED: if (!isset($symbolVariable)) { $symbolVariable = $compilationContext->symbolTable->getVariableForRead( $resolvedExpr->getCode(), @@ -256,6 +259,7 @@ public function compile(CompilationContext $compilationContext): void break; case Types::T_VARIABLE: + case Types::T_MIXED: if ('this_ptr' == $symbolVariable->getName()) { $codePrinter->output('RETURN_THIS();'); } else { diff --git a/Library/SymbolTable.php b/Library/SymbolTable.php index b451929cf..7fe44386e 100644 --- a/Library/SymbolTable.php +++ b/Library/SymbolTable.php @@ -253,7 +253,7 @@ public function getVariablesByBranch($branchId) */ public function getVariableForRead($name, CompilationContext $compilationContext = null, array $statement = null) { - /* + /** * Validate that 'this' cannot be used in a static function */ if ('this' == $name || 'this_ptr' == $name) { @@ -262,7 +262,7 @@ public function getVariableForRead($name, CompilationContext $compilationContext } } - /* + /** * Create superglobals just in time */ if ($this->globalsManager->isSuperGlobal($name)) { @@ -295,7 +295,7 @@ public function getVariableForRead($name, CompilationContext $compilationContext $variable->increaseUses(); - /* + /** * Analise branches to detect possible initialization of variables in conditional branches */ if (!$variable->isTemporal() && !$variable->getSkipVariant()) { @@ -306,6 +306,7 @@ public function getVariableForRead($name, CompilationContext $compilationContext case 'variable': case 'string': case 'array': + case 'mixed': if (!$variable->isLocalOnly()) { $variable->setMustInitNull(true); } @@ -380,7 +381,6 @@ public function getVariableForRead($name, CompilationContext $compilationContext foreach ($branches as $branch) { $graph->addLeaf($branch); } - //echo $graph->getRoot()->show(); } else { /* * Variable is assigned just once and it's assigned in a conditional branch diff --git a/ext/stub/types/mixedtype.zep.c b/ext/stub/types/mixedtype.zep.c index 3659d7286..7ec1a3d8e 100644 --- a/ext/stub/types/mixedtype.zep.c +++ b/ext/stub/types/mixedtype.zep.c @@ -15,6 +15,7 @@ #include "kernel/object.h" #include "kernel/operators.h" #include "kernel/memory.h" +#include "kernel/array.h" ZEPHIR_INIT_CLASS(Stub_Types_MixedType) @@ -24,6 +25,9 @@ ZEPHIR_INIT_CLASS(Stub_Types_MixedType) return SUCCESS; } +/** + * Mixed only as return type methods + */ PHP_METHOD(Stub_Types_MixedType, returnMixedObject) { zval *this_ptr = getThis(); @@ -125,6 +129,119 @@ PHP_METHOD(Stub_Types_MixedType, returnMultiButAlwaysMixed) - RETURN_STRING("IS_MIXED in header"); + RETURN_STRING("ZEND_BEGIN_ARG_INFO_EX"); +} + +/** + * Mixed only as parameter in method + */ +PHP_METHOD(Stub_Types_MixedType, paramMixed) +{ + zval val_sub; + zval *val; + zval *this_ptr = getThis(); + + ZVAL_UNDEF(&val_sub); +#if PHP_VERSION_ID >= 80000 + bool is_null_true = 1; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(val) + ZEND_PARSE_PARAMETERS_END(); +#endif + + + zephir_fetch_params_without_memory_grow(1, 0, &val); + + + RETVAL_ZVAL(val, 1, 0); + return; +} + +PHP_METHOD(Stub_Types_MixedType, paramMixedTwo) +{ + zval val1_sub, val2_sub; + zval *val1, *val2; + zval *this_ptr = getThis(); + + ZVAL_UNDEF(&val1_sub); + ZVAL_UNDEF(&val2_sub); +#if PHP_VERSION_ID >= 80000 + bool is_null_true = 1; + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_ZVAL(val1) + Z_PARAM_ZVAL(val2) + ZEND_PARSE_PARAMETERS_END(); +#endif + + + zephir_fetch_params_without_memory_grow(2, 0, &val1, &val2); + + + zephir_create_array(return_value, 2, 0); + zephir_array_fast_append(return_value, val1); + zephir_array_fast_append(return_value, val2); + return; +} + +PHP_METHOD(Stub_Types_MixedType, paramMixedWithMulti) +{ + zephir_method_globals *ZEPHIR_METHOD_GLOBALS_PTR = NULL; + zval *mixedVal; + zval stringVal; + zval *intVal_param = NULL, *stringVal_param = NULL, mixedVal_sub, _0; + zend_long intVal; + zval *this_ptr = getThis(); + + ZVAL_UNDEF(&mixedVal_sub); + ZVAL_UNDEF(&_0); + ZVAL_UNDEF(&stringVal); +#if PHP_VERSION_ID >= 80000 + bool is_null_true = 1; + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_LONG(intVal) + Z_PARAM_STR(stringVal) + Z_PARAM_ZVAL(mixedVal) + ZEND_PARSE_PARAMETERS_END(); +#endif + + + ZEPHIR_MM_GROW(); + zephir_fetch_params(1, 3, 0, &intVal_param, &stringVal_param, &mixedVal); + intVal = zephir_get_intval(intVal_param); + zephir_get_strval(&stringVal, stringVal_param); + + + zephir_create_array(return_value, 3, 0); + ZEPHIR_INIT_VAR(&_0); + ZVAL_LONG(&_0, intVal); + zephir_array_fast_append(return_value, &_0); + zephir_array_fast_append(return_value, &stringVal); + zephir_array_fast_append(return_value, mixedVal); + RETURN_MM(); +} + +/** + * Mixed as as parameter and return type in method + */ +PHP_METHOD(Stub_Types_MixedType, paramAndReturnMixed) +{ + zval val_sub; + zval *val; + zval *this_ptr = getThis(); + + ZVAL_UNDEF(&val_sub); +#if PHP_VERSION_ID >= 80000 + bool is_null_true = 1; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(val) + ZEND_PARSE_PARAMETERS_END(); +#endif + + + zephir_fetch_params_without_memory_grow(1, 0, &val); + + + RETVAL_ZVAL(val, 1, 0); + return; } diff --git a/ext/stub/types/mixedtype.zep.h b/ext/stub/types/mixedtype.zep.h index 929cb0536..641da1d00 100644 --- a/ext/stub/types/mixedtype.zep.h +++ b/ext/stub/types/mixedtype.zep.h @@ -12,6 +12,10 @@ PHP_METHOD(Stub_Types_MixedType, returnMixedBool); PHP_METHOD(Stub_Types_MixedType, returnMixedNull); PHP_METHOD(Stub_Types_MixedType, returnMixed74); PHP_METHOD(Stub_Types_MixedType, returnMultiButAlwaysMixed); +PHP_METHOD(Stub_Types_MixedType, paramMixed); +PHP_METHOD(Stub_Types_MixedType, paramMixedTwo); +PHP_METHOD(Stub_Types_MixedType, paramMixedWithMulti); +PHP_METHOD(Stub_Types_MixedType, paramAndReturnMixed); #if PHP_VERSION_ID >= 80000 ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_returnmixedobject, 0, 0, IS_MIXED, 0) @@ -73,6 +77,29 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_types_mixedtype_returnmultibutalwaysmixed, 0, 0, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_types_mixedtype_parammixed, 0, 0, 1) + ZEND_ARG_INFO(0, val) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_types_mixedtype_parammixedtwo, 0, 0, 2) + ZEND_ARG_INFO(0, val1) + ZEND_ARG_INFO(0, val2) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_stub_types_mixedtype_parammixedwithmulti, 0, 0, 3) + ZEND_ARG_TYPE_INFO(0, intVal, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, stringVal, IS_STRING, 0) + ZEND_ARG_INFO(0, mixedVal) +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_paramandreturnmixed, 0, 1, IS_MIXED, 0) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_mixedtype_paramandreturnmixed, 0, 1, IS_NULL, 0) +#endif + ZEND_ARG_INFO(0, val) +ZEND_END_ARG_INFO() + ZEPHIR_INIT_FUNCS(stub_types_mixedtype_method_entry) { PHP_ME(Stub_Types_MixedType, returnMixedObject, arginfo_stub_types_mixedtype_returnmixedobject, ZEND_ACC_PUBLIC) PHP_ME(Stub_Types_MixedType, returnMixedArray, arginfo_stub_types_mixedtype_returnmixedarray, ZEND_ACC_PUBLIC) @@ -87,5 +114,9 @@ ZEPHIR_INIT_FUNCS(stub_types_mixedtype_method_entry) { #else PHP_ME(Stub_Types_MixedType, returnMultiButAlwaysMixed, NULL, ZEND_ACC_PUBLIC) #endif + PHP_ME(Stub_Types_MixedType, paramMixed, arginfo_stub_types_mixedtype_parammixed, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, paramMixedTwo, arginfo_stub_types_mixedtype_parammixedtwo, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, paramMixedWithMulti, arginfo_stub_types_mixedtype_parammixedwithmulti, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_MixedType, paramAndReturnMixed, arginfo_stub_types_mixedtype_paramandreturnmixed, ZEND_ACC_PUBLIC) PHP_FE_END }; diff --git a/stub/types/mixedtype.zep b/stub/types/mixedtype.zep index 655336044..182e3d8f4 100644 --- a/stub/types/mixedtype.zep +++ b/stub/types/mixedtype.zep @@ -3,6 +3,10 @@ namespace Stub\Types; class MixedType { + /** + * Mixed only as return type methods + */ + public function returnMixedObject() -> mixed { return new \stdClass(); @@ -52,4 +56,32 @@ class MixedType // Maybe in future make sense to generate with `IS_MIXED`. return "ZEND_BEGIN_ARG_INFO_EX"; } + + /** + * Mixed only as parameter in method + */ + + public function paramMixed(mixed val) + { + return val; + } + + public function paramMixedTwo(mixed val1, mixed val2) + { + return [val1, val2]; + } + + public function paramMixedWithMulti(int intVal, string stringVal, mixed mixedVal) + { + return [intVal, stringVal, mixedVal]; + } + + /** + * Mixed as as parameter and return type in method + */ + + public function paramAndReturnMixed(mixed val) -> mixed + { + return val; + } } diff --git a/tests/Extension/Types/MixedTest.php b/tests/Extension/Types/MixedTest.php deleted file mode 100644 index cf87d94b3..000000000 --- a/tests/Extension/Types/MixedTest.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Extension\Types; - -use PHPUnit\Framework\TestCase; -use Stub\Types\MixedType; - -final class MixedTest extends TestCase -{ - public function testReturnsOfMixedType(): void - { - $returns = new MixedType(); - - $this->assertEquals((new \stdClass()), $returns->returnMixedObject()); - $this->assertSame([], $returns->returnMixedArray()); - $this->assertSame('mixed string', $returns->returnMixedString()); - $this->assertSame(1, $returns->returnMixedInt()); - $this->assertSame(3.14, $returns->returnMixedFloat()); - $this->assertSame(true, $returns->returnMixedBool()); - $this->assertSame(null, $returns->returnMixedNull()); - - $this->assertNotNull($returns->returnMixedObject()); - $this->assertNotNull($returns->returnMixedArray()); - $this->assertNotNull($returns->returnMixedString()); - $this->assertNotNull($returns->returnMixedInt()); - $this->assertNotNull($returns->returnMixedFloat()); - $this->assertNotNull($returns->returnMixedBool()); - $this->assertNull($returns->returnMixedNull()); - - $this->assertEquals((new \stdClass()), $returns->returnMixed74()); - $this->assertSame('string', $returns->returnMixed74(true)); - $this->assertEquals((new \stdClass()), $returns->returnMixed74(false)); - } -} diff --git a/tests/Extension/Types/MixedTypeTest.php b/tests/Extension/Types/MixedTypeTest.php new file mode 100644 index 000000000..58ddb11c8 --- /dev/null +++ b/tests/Extension/Types/MixedTypeTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +namespace Extension\Types; + +use PHPUnit\Framework\TestCase; +use Stub\Types\MixedType; + +final class MixedTypeTest extends TestCase +{ + public function testReturnsOfMixedType(): void + { + $returns = new MixedType(); + + $this->assertEquals((new \stdClass()), $returns->returnMixedObject()); + $this->assertSame([], $returns->returnMixedArray()); + $this->assertSame('mixed string', $returns->returnMixedString()); + $this->assertSame(1, $returns->returnMixedInt()); + $this->assertSame(3.14, $returns->returnMixedFloat()); + $this->assertSame(true, $returns->returnMixedBool()); + $this->assertSame(null, $returns->returnMixedNull()); + + $this->assertNotNull($returns->returnMixedObject()); + $this->assertNotNull($returns->returnMixedArray()); + $this->assertNotNull($returns->returnMixedString()); + $this->assertNotNull($returns->returnMixedInt()); + $this->assertNotNull($returns->returnMixedFloat()); + $this->assertNotNull($returns->returnMixedBool()); + $this->assertNull($returns->returnMixedNull()); + + $this->assertEquals((new \stdClass()), $returns->returnMixed74()); + $this->assertSame('string', $returns->returnMixed74(true)); + $this->assertEquals((new \stdClass()), $returns->returnMixed74(false)); + } + + public function testParamsOfMixedType(): void + { + $returns = new MixedType(); + + $this->assertEquals((new \stdClass()), $returns->paramMixed((new \stdClass()))); + $this->assertSame([], $returns->paramMixed([])); + $this->assertSame('mixed string', $returns->paramMixed('mixed string')); + $this->assertSame(1, $returns->paramMixed(1)); + $this->assertSame(3.14, $returns->paramMixed(3.14)); + $this->assertSame(true, $returns->paramMixed(true)); + $this->assertSame(null, $returns->paramMixed(null)); + + $this->assertEquals([(new \stdClass()), []], $returns->paramMixedTwo((new \stdClass()), [])); + $this->assertSame([[], 'mixed string'], $returns->paramMixedTwo([], 'mixed string')); + $this->assertSame([1, 3.14], $returns->paramMixedTwo(1, 3.14)); + $this->assertSame([3.14, true], $returns->paramMixedTwo(3.14, true)); + $this->assertSame([true, null], $returns->paramMixedTwo(true, null)); + $this->assertSame([null, null], $returns->paramMixedTwo(null, null)); + + $this->assertEquals([1337, 'object', (new \stdClass())], $returns->paramMixedWithMulti(1337, 'object', (new \stdClass()))); + $this->assertSame([1337, 'array', []], $returns->paramMixedWithMulti(1337, 'array', [])); + $this->assertSame([1337, 'string', 'mixed string'], $returns->paramMixedWithMulti(1337, 'string', 'mixed string')); + $this->assertSame([1337, 'int', 123], $returns->paramMixedWithMulti(1337, 'int', 123)); + $this->assertSame([1337, 'float', 2.44], $returns->paramMixedWithMulti(1337, 'float', 2.44)); + $this->assertSame([1337, 'bool', false], $returns->paramMixedWithMulti(1337, 'bool', false)); + $this->assertSame([1337, 'null', null], $returns->paramMixedWithMulti(1337, 'null', null)); + } + + public function testParamsAndReturnsOfMixedType(): void + { + $returns = new MixedType(); + + $this->assertEquals((new \stdClass()), $returns->paramAndReturnMixed((new \stdClass()))); + $this->assertSame([], $returns->paramAndReturnMixed([])); + $this->assertSame('mixed string', $returns->paramAndReturnMixed('mixed string')); + $this->assertSame(1, $returns->paramAndReturnMixed(1)); + $this->assertSame(3.14, $returns->paramAndReturnMixed(3.14)); + $this->assertSame(true, $returns->paramAndReturnMixed(true)); + $this->assertSame(null, $returns->paramAndReturnMixed(null)); + } +} From 3ecf7f34d99a5054952654e3c699d8bd2f490f33 Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Mon, 20 Sep 2021 14:15:44 +0100 Subject: [PATCH 6/7] #2276 - Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2e6245a8..6931d8434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format based on [Keep a Changelog](http://keepachangelog.com) and this project adheres to [Semantic Versioning](http://semver.org). ## [Unreleased] +### Added +- Added support for `mixed` [#2276](https://github.com/zephir-lang/zephir/issues/2276) ## [0.14.0] - 2021-09-18 ### Added From 477bc377792b7762992e7789745d62da2eaaf0eb Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Tue, 5 Oct 2021 15:41:30 +0100 Subject: [PATCH 7/7] #2276 - Refactor `Closure` class --- Library/Expression/Closure.php | 99 +++++++++++++++-------------- Library/Expression/ClosureArrow.php | 6 +- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/Library/Expression/Closure.php b/Library/Expression/Closure.php index 9f7d41e52..e7ae2e0d4 100644 --- a/Library/Expression/Closure.php +++ b/Library/Expression/Closure.php @@ -9,21 +9,23 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Expression; use Zephir\ClassDefinition; use Zephir\ClassMethod; use Zephir\ClassMethodParameters; +use Zephir\ClassProperty; use Zephir\CompilationContext; use Zephir\CompiledExpression; use Zephir\CompilerFileAnonymous; -use Zephir\Exception\CompilerException; +use Zephir\Exception; use Zephir\StatementsBlock; use Zephir\Variable; +use function is_array; /** - * Closure. - * * Creates an anonymous function within the extension simulating a closure */ class Closure @@ -31,17 +33,17 @@ class Closure /** * @var bool */ - protected $expecting = true; + protected bool $expecting = true; /** * @var bool */ - protected $readOnly = false; + protected bool $readOnly = false; /** - * @var Variable + * @var Variable|null */ - protected $expectingVariable; + protected ?Variable $expectingVariable = null; /** * Unique closure ID. @@ -52,10 +54,10 @@ class Closure * Sets if the variable must be resolved into a direct variable symbol * create a temporary value or ignore the return value. * - * @param bool $expecting - * @param Variable $expectingVariable + * @param bool $expecting + * @param Variable|null $expectingVariable */ - public function setExpectReturn($expecting, Variable $expectingVariable = null) + public function setExpectReturn(bool $expecting, ?Variable $expectingVariable = null): void { $this->expecting = $expecting; $this->expectingVariable = $expectingVariable; @@ -66,7 +68,7 @@ public function setExpectReturn($expecting, Variable $expectingVariable = null) * * @param bool $readOnly */ - public function setReadOnly($readOnly) + public function setReadOnly(bool $readOnly): void { $this->readOnly = $readOnly; } @@ -74,14 +76,13 @@ public function setReadOnly($readOnly) /** * Creates a closure. * - * @param array $expression + * @param array $expression * @param CompilationContext $compilationContext * - * @throws CompilerException - * * @return CompiledExpression + * @throws Exception */ - public function compile(array $expression, CompilationContext $compilationContext) + public function compile(array $expression, CompilationContext $compilationContext): CompiledExpression { $classDefinition = new ClassDefinition( $compilationContext->config->get('namespace'), @@ -95,27 +96,22 @@ public function compile(array $expression, CompilationContext $compilationContex $compilationContext->compiler->addClassDefinition($compilerFile, $classDefinition); + $parameters = null; if (isset($expression['left'])) { $parameters = new ClassMethodParameters($expression['left']); - } else { - $parameters = null; } - if (isset($expression['right'])) { - $block = $expression['right']; - } else { - $block = []; - } + $block = $expression['right'] ?? []; $staticVariables = []; - if (isset($expression['use']) && \is_array($expression['use'])) { + if (isset($expression['use']) && is_array($expression['use'])) { foreach ($expression['use'] as $parameter) { $staticVariables[$parameter['name']] = $compilationContext->symbolTable->getVariable($parameter['name']); } } foreach ($staticVariables as $var) { - $classDefinition->addProperty(new \Zephir\ClassProperty( + $classDefinition->addProperty(new ClassProperty( $classDefinition, ['public', 'static'], $var->getName(), @@ -153,35 +149,40 @@ public function compile(array $expression, CompilationContext $compilationContex $symbolVariable->initVariant($compilationContext); $compilationContext->backend->createClosure($symbolVariable, $classDefinition, $compilationContext); $compilationContext->headersManager->add('kernel/object'); + foreach ($staticVariables as $var) { - if ('variable' == $var->getType() || 'array' == $var->getType()) { + if (in_array($var->getType(), ['variable', 'array'])) { $compilationContext->backend->updateStaticProperty($classDefinition->getClassEntry(), $var->getName(), $var, $compilationContext); - } else { - $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); - switch ($var->getType()) { - case 'int': - case 'uint': - case 'long': - case 'ulong': - case 'char': - case 'uchar': - $compilationContext->backend->assignLong($tempVariable, $var, $compilationContext); - break; - case 'double': - $compilationContext->backend->assignDouble($tempVariable, $var, $compilationContext); - break; - case 'bool': - $compilationContext->backend->assignBool($tempVariable, $var, $compilationContext); - break; - case 'string': - $compilationContext->backend->assignString($tempVariable, $var, $compilationContext); - break; - default: - break; - } - $compilationContext->backend->updateStaticProperty($classDefinition->getClassEntry(), $var->getName(), $tempVariable, $compilationContext); + continue; + } + + $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); + + switch ($var->getType()) { + case 'int': + case 'uint': + case 'long': + case 'ulong': + case 'char': + case 'uchar': + $compilationContext->backend->assignLong($tempVariable, $var, $compilationContext); + break; + case 'double': + $compilationContext->backend->assignDouble($tempVariable, $var, $compilationContext); + break; + case 'bool': + $compilationContext->backend->assignBool($tempVariable, $var, $compilationContext); + break; + case 'string': + $compilationContext->backend->assignString($tempVariable, $var, $compilationContext); + break; + default: + break; } + + $compilationContext->backend->updateStaticProperty($classDefinition->getClassEntry(), $var->getName(), $tempVariable, $compilationContext); } + ++self::$id; return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); diff --git a/Library/Expression/ClosureArrow.php b/Library/Expression/ClosureArrow.php index d6a7cd6e2..6c8688c48 100644 --- a/Library/Expression/ClosureArrow.php +++ b/Library/Expression/ClosureArrow.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Expression; use Zephir\ClassDefinition; @@ -22,8 +24,6 @@ use Zephir\StatementsBlock; /** - * ClosureArrow. - * * Creates an anonymous function within the extension simulating a closure using the arrow syntax */ class ClosureArrow extends Closure @@ -38,7 +38,7 @@ class ClosureArrow extends Closure * * @return CompiledExpression */ - public function compile(array $expression, CompilationContext $compilationContext) + public function compile(array $expression, CompilationContext $compilationContext): CompiledExpression { $classDefinition = new ClassDefinition( $compilationContext->config->get('namespace'),