diff --git a/CHANGELOG.md b/CHANGELOG.md index d43b4c719..43d8674ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ The format based on [Keep a Changelog](https://keepachangelog.com) and this project adheres to [Semantic Versioning](https://semver.org). ## [Unreleased] +### Fixed +- Fixed generation of `ARG_INFO` for nullable object (`?object`) [#2374](https://github.com/zephir-lang/zephir/issues/2374) ## [0.16.0] - 2022-03-20 ### Added diff --git a/Library/ArgInfoDefinition.php b/Library/ArgInfoDefinition.php index 489bf8b59..8e55bc213 100644 --- a/Library/ArgInfoDefinition.php +++ b/Library/ArgInfoDefinition.php @@ -167,13 +167,9 @@ public function render(): void private function richRenderStart(): void { - if (array_key_exists('object', $this->functionLike->getReturnTypes())) { - $class = 'NULL'; - - if (1 === count($this->functionLike->getReturnClassTypes())) { - $class = key($this->functionLike->getReturnClassTypes()); - $class = escape_class($this->compilationContext->getFullName($class)); - } + if (array_key_exists('object', $this->functionLike->getReturnTypes()) && 1 === count($this->functionLike->getReturnClassTypes())) { + $class = key($this->functionLike->getReturnClassTypes()); + $class = escape_class($this->compilationContext->getFullName($class)); $this->codePrinter->output( sprintf( @@ -237,6 +233,33 @@ private function richRenderStart(): void return; } + if ($this->functionLike->isReturnTypeNullableObject()) { + $this->codePrinter->output('#if PHP_VERSION_ID >= 80000'); + $this->codePrinter->output( + sprintf( + 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)', + $this->name, + (int) $this->returnByRef, + $this->functionLike->getNumberOfRequiredParameters(), + 'MAY_BE_NULL|MAY_BE_OBJECT', + ) + ); + $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(), + 'IS_OBJECT', + 1, + ) + ); + $this->codePrinter->output('#endif'); + + return; + } + if (count($this->functionLike->getReturnTypes()) > 1) { $types = []; $mayBeTypes = $this->functionLike->getMayBeArgTypes(); @@ -446,7 +469,7 @@ private function allowNull(array $parameter): bool return false; } - if ('null' == $parameter['default']['type']) { + if ('null' === $parameter['default']['type']) { return true; } diff --git a/Library/ClassMethod.php b/Library/ClassMethod.php index 8c1ec4748..7802cf7ce 100644 --- a/Library/ClassMethod.php +++ b/Library/ClassMethod.php @@ -2340,6 +2340,16 @@ public function isReturnTypesHintDetermined(): bool return true; } + /** + * Checks if method's return type is nullable object `?object`. + * + * @return bool + */ + public function isReturnTypeNullableObject(): bool + { + return count($this->returnTypes) === 2 && isset($this->returnTypes['object']) && isset($this->returnTypes['null']); + } + /** * Checks if the method have compatible return types. * diff --git a/Library/Expression/NativeArray.php b/Library/Expression/NativeArray.php index 2b73152b6..72c87e03f 100644 --- a/Library/Expression/NativeArray.php +++ b/Library/Expression/NativeArray.php @@ -221,7 +221,7 @@ public function compile(array $expression, CompilationContext $compilationContex */ $arrayLength = count($expression['left']); if ($arrayLength >= 33 && function_exists('gmp_nextprime')) { - $arrayLength = gmp_strval(gmp_nextprime($arrayLength - 1)); + $arrayLength = (int) gmp_strval(gmp_nextprime($arrayLength - 1)); } if ($this->expectingVariable && $symbolVariable->getVariantInits() >= 1) { diff --git a/ext/config.m4 b/ext/config.m4 index f654c649f..c1fb9625c 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -215,6 +215,7 @@ if test "$PHP_STUB" = "yes"; then stub/typeoff.zep.c stub/types/maybe.zep.c stub/types/mixedtype.zep.c + stub/types/obj.zep.c stub/unknownclass.zep.c stub/unsettest.zep.c stub/usetest.zep.c diff --git a/ext/config.w32 b/ext/config.w32 index cea2bd118..505cec607 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -38,7 +38,7 @@ if (PHP_STUB != "no") { 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/types", "maybe.zep.c mixedtype.zep.c", "stub"); + ADD_SOURCES(configure_module_dirname + "/stub/types", "maybe.zep.c mixedtype.zep.c obj.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 aa6294f0a..23752125f 100644 --- a/ext/stub.c +++ b/ext/stub.c @@ -243,6 +243,7 @@ zend_class_entry *stub_typeinstances_ce; zend_class_entry *stub_typeoff_ce; zend_class_entry *stub_types_maybe_ce; zend_class_entry *stub_types_mixedtype_ce; +zend_class_entry *stub_types_obj_ce; zend_class_entry *stub_unknownclass_ce; zend_class_entry *stub_unsettest_ce; zend_class_entry *stub_usetest_ce; @@ -473,6 +474,7 @@ static PHP_MINIT_FUNCTION(stub) ZEPHIR_INIT(Stub_Typeoff); ZEPHIR_INIT(Stub_Types_MayBe); ZEPHIR_INIT(Stub_Types_MixedType); + ZEPHIR_INIT(Stub_Types_Obj); ZEPHIR_INIT(Stub_UnknownClass); ZEPHIR_INIT(Stub_Unsettest); ZEPHIR_INIT(Stub_UseTest); diff --git a/ext/stub.h b/ext/stub.h index 942bbaba8..48be14ef4 100644 --- a/ext/stub.h +++ b/ext/stub.h @@ -210,6 +210,7 @@ #include "stub/typeoff.zep.h" #include "stub/types/maybe.zep.h" #include "stub/types/mixedtype.zep.h" +#include "stub/types/obj.zep.h" #include "stub/unknownclass.zep.h" #include "stub/unsettest.zep.h" #include "stub/usetest.zep.h" diff --git a/ext/stub/types/obj.zep.c b/ext/stub/types/obj.zep.c new file mode 100644 index 000000000..d2b119f0c --- /dev/null +++ b/ext/stub/types/obj.zep.c @@ -0,0 +1,43 @@ + +#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" + + +ZEPHIR_INIT_CLASS(Stub_Types_Obj) +{ + ZEPHIR_REGISTER_CLASS(Stub\\Types, Obj, stub, types_obj, stub_types_obj_method_entry, 0); + + return SUCCESS; +} + +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnNull) +{ + zval *this_ptr = getThis(); + + + + RETURN_NULL(); +} + +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnObj) +{ + zval *this_ptr = getThis(); + + + + object_init(return_value); + return; +} + diff --git a/ext/stub/types/obj.zep.h b/ext/stub/types/obj.zep.h new file mode 100644 index 000000000..6854113fa --- /dev/null +++ b/ext/stub/types/obj.zep.h @@ -0,0 +1,27 @@ + +extern zend_class_entry *stub_types_obj_ce; + +ZEPHIR_INIT_CLASS(Stub_Types_Obj); + +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnNull); +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnObj); + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stub_types_obj_nullableobjectreturnnull, 0, 0, MAY_BE_NULL|MAY_BE_OBJECT) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_obj_nullableobjectreturnnull, 0, 0, IS_OBJECT, 1) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stub_types_obj_nullableobjectreturnobj, 0, 0, MAY_BE_NULL|MAY_BE_OBJECT) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_obj_nullableobjectreturnobj, 0, 0, IS_OBJECT, 1) +#endif +ZEND_END_ARG_INFO() + +ZEPHIR_INIT_FUNCS(stub_types_obj_method_entry) { + PHP_ME(Stub_Types_Obj, nullableObjectReturnNull, arginfo_stub_types_obj_nullableobjectreturnnull, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_Obj, nullableObjectReturnObj, arginfo_stub_types_obj_nullableobjectreturnobj, ZEND_ACC_PUBLIC) + PHP_FE_END +}; diff --git a/stub/types/obj.zep b/stub/types/obj.zep new file mode 100644 index 000000000..65134b3ee --- /dev/null +++ b/stub/types/obj.zep @@ -0,0 +1,15 @@ + +namespace Stub\Types; + +class Obj +{ + public function nullableObjectReturnNull() -> object | null + { + return null; + } + + public function nullableObjectReturnObj() -> object | null + { + return new \stdClass(); + } +} diff --git a/tests/Extension/Types/ObjTypeTest.php b/tests/Extension/Types/ObjTypeTest.php new file mode 100644 index 000000000..0cab73519 --- /dev/null +++ b/tests/Extension/Types/ObjTypeTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Extension\Types; + +use PHPUnit\Framework\TestCase; +use stdClass; +use Stub\Types\Obj; + +final class ObjTypeTest extends TestCase +{ + public function testIntFalse(): void + { + $class = new Obj(); + + $this->assertNull($class->nullableObjectReturnNull()); + $this->assertInstanceOf(stdClass::class, $class->nullableObjectReturnObj()); + } +}