diff --git a/CHANGELOG.md b/CHANGELOG.md index eb9187429d..6167c90428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org). ## [Unreleased] # Added - Added support for `string` type in php.ini [#2280](https://github.com/zephir-lang/zephir/issues/2280) +- Added support for `mixed` [#2276](https://github.com/zephir-lang/zephir/issues/2276) ### Fixed - Fixed multiple return types in stubs [#2283](https://github.com/zephir-lang/zephir/issues/2283) diff --git a/Library/ArgInfoDefinition.php b/Library/ArgInfoDefinition.php index 6bf99c659c..edb46ff093 100644 --- a/Library/ArgInfoDefinition.php +++ b/Library/ArgInfoDefinition.php @@ -28,7 +28,7 @@ class ArgInfoDefinition /** * @var string */ - private string $name = ''; + private string $name; /** * @var ClassMethodParameters|null @@ -58,14 +58,14 @@ class ArgInfoDefinition /** * ArgInfoDefinition constructor. * - * @param $name + * @param string $name * @param ClassMethod $functionLike * @param CodePrinter $codePrinter * @param CompilationContext $compilationContext * @param bool $returnByRef */ public function __construct( - $name, + string $name, ClassMethod $functionLike, CodePrinter $codePrinter, CompilationContext $compilationContext, @@ -205,16 +205,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 @@ -393,6 +418,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'; } diff --git a/Library/Backends/ZendEngine3/Backend.php b/Library/Backends/ZendEngine3/Backend.php index 011bb1a644..915f562d1e 100644 --- a/Library/Backends/ZendEngine3/Backend.php +++ b/Library/Backends/ZendEngine3/Backend.php @@ -106,7 +106,7 @@ public function getStringsManager() /** * {@inheritdoc} */ - public function getTypeDefinition($type) + public function getTypeDefinition($type): array { switch ($type) { case 'zend_ulong': @@ -155,6 +155,7 @@ public function getTypeDefinition($type) case 'variable': case 'array': case 'null': + case 'mixed': $pointer = '*'; $code = 'zval'; break; @@ -317,7 +318,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(); @@ -366,6 +367,7 @@ public function generateInitCode(&$groupVariables, $type, $pointer, Variable $va case 'resource': case 'callable': case 'object': + case 'mixed': $groupVariables[] = $pointer.$variable->getName(); break; @@ -595,7 +597,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(); } @@ -630,6 +632,7 @@ public function addArrayEntry(Variable $variable, $key, $value, CompilationConte case 'variable': case 'array': + case 'mixed': $type = 'zval'; break; } @@ -847,20 +850,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 123aeb9b4e..6278ca38fd 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; } } @@ -515,7 +532,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); @@ -850,6 +867,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. * @@ -1427,7 +1454,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 ''; } @@ -1634,6 +1661,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()) { @@ -1860,6 +1888,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 18de1afad3..13e54efb5d 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/Exception/InvalidTypeException.php b/Library/Exception/InvalidTypeException.php index be1542a788..fad4b82b46 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/Expression.php b/Library/Expression.php index 3339675f31..0a2dd42d88 100644 --- a/Library/Expression.php +++ b/Library/Expression.php @@ -189,7 +189,7 @@ public function setEvalMode(bool $evalMode): void /** * Compiles foo = []. * - * @param array $expression + * @param array $expression * @param CompilationContext $compilationContext * * @return CompiledExpression @@ -231,6 +231,8 @@ public function emptyArray(array $expression, CompilationContext $compilationCon * * @param CompilationContext $compilationContext * + * @throws CompilerException|Exception + * * @return CompiledExpression * * @throws Exception diff --git a/Library/Expression/Closure.php b/Library/Expression/Closure.php index 9f7d41e529..e7ae2e0d40 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 d6a7cd6e2b..6c8688c481 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'), diff --git a/Library/Expression/NativeArray.php b/Library/Expression/NativeArray.php index 23382881cb..8e61539da7 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 7ade66fe89..5ffc637dec 100644 --- a/Library/Statements/ReturnStatement.php +++ b/Library/Statements/ReturnStatement.php @@ -11,7 +11,9 @@ namespace Zephir\Statements; +use ReflectionException; use Zephir\CompilationContext; +use Zephir\Exception; use Zephir\Exception\CompilerException; use Zephir\Exception\InvalidTypeException; use Zephir\Expression; @@ -29,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; @@ -51,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']; @@ -83,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'); @@ -93,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; @@ -110,31 +113,32 @@ 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; case Types::T_VARIABLE: + case Types::T_MIXED: $symbolVariable = $compilationContext->symbolTable->getVariableForRead( $resolvedExpr->getCode(), $compilationContext, @@ -148,31 +152,32 @@ 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; case Types::T_VARIABLE: + case Types::T_MIXED: break; } break; @@ -218,6 +223,7 @@ public function compile(CompilationContext $compilationContext) break; case Types::T_VARIABLE: + case Types::T_MIXED: if (!isset($symbolVariable)) { $symbolVariable = $compilationContext->symbolTable->getVariableForRead( $resolvedExpr->getCode(), @@ -253,6 +259,7 @@ public function compile(CompilationContext $compilationContext) break; case Types::T_VARIABLE: + case Types::T_MIXED: if ('this_ptr' == $symbolVariable->getName()) { $codePrinter->output('RETURN_THIS();'); } else { @@ -315,7 +322,7 @@ public function compile(CompilationContext $compilationContext) return; } - /* + /** * Return without an expression */ $codePrinter->output('RETURN_MM_NULL();'); diff --git a/Library/SymbolTable.php b/Library/SymbolTable.php index b451929cf0..7fe44386e9 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/config.m4 b/ext/config.m4 index cceffb6578..8552cc50ac 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 12199ac28c..7cc2da4067 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 3e43e6fcb1..26e7660a02 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 da8ea50cb6..cf7b456b20 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 0000000000..7ec1a3d8ee --- /dev/null +++ b/ext/stub/types/mixedtype.zep.c @@ -0,0 +1,247 @@ + +#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" +#include "kernel/array.h" + + +ZEPHIR_INIT_CLASS(Stub_Types_MixedType) +{ + ZEPHIR_REGISTER_CLASS(Stub\\Types, MixedType, stub, types_mixedtype, stub_types_mixedtype_method_entry, 0); + + return SUCCESS; +} + +/** + * Mixed only as return type methods + */ +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("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 new file mode 100644 index 0000000000..641da1d00f --- /dev/null +++ b/ext/stub/types/mixedtype.zep.h @@ -0,0 +1,122 @@ + +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); +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) +#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() + +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) + 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_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 new file mode 100644 index 0000000000..182e3d8f41 --- /dev/null +++ b/stub/types/mixedtype.zep @@ -0,0 +1,87 @@ + +namespace Stub\Types; + +class MixedType +{ + /** + * Mixed only as return type methods + */ + + 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"; + } + + /** + * 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/MixedTypeTest.php b/tests/Extension/Types/MixedTypeTest.php new file mode 100644 index 0000000000..58ddb11c82 --- /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)); + } +}