From 2713ac89ac094efd4cbf3243d2ca89021292fe2d Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sat, 2 Nov 2019 17:11:16 +0200 Subject: [PATCH 01/47] Bump version --- Library/Zephir.php | 2 +- appveyor.yml | 2 +- ext/php_test.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Library/Zephir.php b/Library/Zephir.php index ef6e336497..f7595703e4 100644 --- a/Library/Zephir.php +++ b/Library/Zephir.php @@ -16,7 +16,7 @@ */ final class Zephir { - const VERSION = '0.12.11-$Id$'; + const VERSION = '0.12.12-$Id$'; const LOGO = <<<'ASCII' _____ __ _ diff --git a/appveyor.yml b/appveyor.yml index 3f0486516c..46188ecd25 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.12.11-{build} +version: 0.12.12-{build} environment: matrix: diff --git a/ext/php_test.h b/ext/php_test.h index 78371f790a..66ac3107e1 100644 --- a/ext/php_test.h +++ b/ext/php_test.h @@ -14,7 +14,7 @@ #define PHP_TEST_VERSION "1.0.0" #define PHP_TEST_EXTNAME "test" #define PHP_TEST_AUTHOR "Zephir Team and contributors" -#define PHP_TEST_ZEPVERSION "0.12.11-$Id$" +#define PHP_TEST_ZEPVERSION "0.12.12-$Id$" #define PHP_TEST_DESCRIPTION "Description test for
Test Extension." typedef struct _zephir_struct_db { From 720e145cf310a04ab6ae5f4603d0f9c25b9390ff Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 3 Nov 2019 17:25:20 +0200 Subject: [PATCH 02/47] Use latest php-cs-fixer --- .php_cs.dist | 3 +++ Library/Console/Command/GenerateCommand.php | 3 ++- Library/Documentation.php | 1 - unit-tests/Extension/ConcatTest.php | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.php_cs.dist b/.php_cs.dist index 40ab7e9162..85a5cfca43 100644 --- a/.php_cs.dist +++ b/.php_cs.dist @@ -40,10 +40,13 @@ return PhpCsFixer\Config::create() '@Symfony' => true, '@Symfony:risky' => true, '@PHPUnit48Migration:risky' => true, + '@PSR2' => true, 'php_unit_no_expectation_annotation' => false, 'array_syntax' => ['syntax' => 'short'], 'fopen_flags' => false, 'ordered_imports' => ['sortAlgorithm' => 'alpha'], 'protected_to_private' => false, 'phpdoc_var_annotation_correct_order' => true, + 'no_superfluous_phpdoc_tags' => false, + 'single_line_throw' => false, ]); diff --git a/Library/Console/Command/GenerateCommand.php b/Library/Console/Command/GenerateCommand.php index c11325d93b..29cdecc733 100644 --- a/Library/Console/Command/GenerateCommand.php +++ b/Library/Console/Command/GenerateCommand.php @@ -53,7 +53,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $io = new SymfonyStyle($input, $output); if (\extension_loaded('timecop') && 1 == ini_get('timecop.func_override')) { - $io->getErrorStyle()->warning(<<getErrorStyle()->warning( + << check if the command line argument --output-directory was given * => if not ; check if config config[api][path] was given * - * * @param string $outputDir * * @return string|null diff --git a/unit-tests/Extension/ConcatTest.php b/unit-tests/Extension/ConcatTest.php index 210d0ed8d6..02af02682c 100644 --- a/unit-tests/Extension/ConcatTest.php +++ b/unit-tests/Extension/ConcatTest.php @@ -62,7 +62,7 @@ public function shouldConcatenateStringWithVarDouble() { $t = new Concat(); $this->assertSame( - /* @lang text */ 'SELECT * FROM TEST WHERE value <= 946.5 AND value >= 473.25', + 'SELECT * FROM TEST WHERE value <= 946.5 AND value >= 473.25', $t->testConcat4(1893) ); } From 6a6984ecb649cc3cfc56e93e6dca4299e3c9ccd3 Mon Sep 17 00:00:00 2001 From: Dreamszhu Date: Mon, 4 Nov 2019 09:48:01 +0800 Subject: [PATCH 03/47] Fix #2000 --- ext/kernel/fcall.h | 8 ++++++-- kernels/ZendEngine3/fcall.h | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/ext/kernel/fcall.h b/ext/kernel/fcall.h index 1237bf2f60..d50843c557 100644 --- a/ext/kernel/fcall.h +++ b/ext/kernel/fcall.h @@ -60,7 +60,7 @@ typedef enum _zephir_call_type { /* Saves the if pointer, and called/calling scope */ #define ZEPHIR_BACKUP_THIS_PTR() \ - zend_object *old_this_ptr = Z_OBJ(EG(current_execute_data)->This) ? Z_OBJ(EG(current_execute_data)->This) : NULL; + zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; #define ZEPHIR_RESTORE_THIS_PTR() do { \ if (old_this_ptr) { \ @@ -77,7 +77,11 @@ typedef enum _zephir_call_type { #define ZEPHIR_SET_THIS_OBJ(obj) \ if (obj) { \ - Z_OBJ_P(&EG(current_execute_data)->This) = obj; \ + if (old_this_ptr) { \ + Z_OBJ_P(&EG(current_execute_data)->This) = obj; \ + } else { \ + ZVAL_OBJ(&EG(current_execute_data)->This, obj); \ + } \ } \ else { ZEPHIR_SET_THIS_EXPLICIT_NULL(); } \ diff --git a/kernels/ZendEngine3/fcall.h b/kernels/ZendEngine3/fcall.h index 1237bf2f60..d50843c557 100644 --- a/kernels/ZendEngine3/fcall.h +++ b/kernels/ZendEngine3/fcall.h @@ -60,7 +60,7 @@ typedef enum _zephir_call_type { /* Saves the if pointer, and called/calling scope */ #define ZEPHIR_BACKUP_THIS_PTR() \ - zend_object *old_this_ptr = Z_OBJ(EG(current_execute_data)->This) ? Z_OBJ(EG(current_execute_data)->This) : NULL; + zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; #define ZEPHIR_RESTORE_THIS_PTR() do { \ if (old_this_ptr) { \ @@ -77,7 +77,11 @@ typedef enum _zephir_call_type { #define ZEPHIR_SET_THIS_OBJ(obj) \ if (obj) { \ - Z_OBJ_P(&EG(current_execute_data)->This) = obj; \ + if (old_this_ptr) { \ + Z_OBJ_P(&EG(current_execute_data)->This) = obj; \ + } else { \ + ZVAL_OBJ(&EG(current_execute_data)->This, obj); \ + } \ } \ else { ZEPHIR_SET_THIS_EXPLICIT_NULL(); } \ From 48a1c7e536e257997b5812aff3d31fa63ba010ff Mon Sep 17 00:00:00 2001 From: dreamsxin Date: Wed, 6 Nov 2019 10:44:59 +0800 Subject: [PATCH 04/47] Updated define ZEPHIR_BACKUP_THIS_PTR --- ext/kernel/fcall.h | 6 +++++- kernels/ZendEngine3/fcall.h | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/ext/kernel/fcall.h b/ext/kernel/fcall.h index d50843c557..ad466638f3 100644 --- a/ext/kernel/fcall.h +++ b/ext/kernel/fcall.h @@ -60,11 +60,15 @@ typedef enum _zephir_call_type { /* Saves the if pointer, and called/calling scope */ #define ZEPHIR_BACKUP_THIS_PTR() \ - zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; + zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; \ + zend_class_entry *old_ce = Z_TYPE(EG(current_execute_data)->This) != IS_OBJECT ? Z_CE(EG(current_execute_data)->This) : NULL; #define ZEPHIR_RESTORE_THIS_PTR() do { \ if (old_this_ptr) { \ ZEPHIR_SET_THIS_OBJ(old_this_ptr); \ + } else if (old_ce) { \ + ZVAL_UNDEF(&EG(current_execute_data)->This); \ + Z_CE(EG(current_execute_data)->This) = old_ce; \ } else { \ ZEPHIR_SET_THIS_EXPLICIT_NULL(); \ } \ diff --git a/kernels/ZendEngine3/fcall.h b/kernels/ZendEngine3/fcall.h index d50843c557..ad466638f3 100644 --- a/kernels/ZendEngine3/fcall.h +++ b/kernels/ZendEngine3/fcall.h @@ -60,11 +60,15 @@ typedef enum _zephir_call_type { /* Saves the if pointer, and called/calling scope */ #define ZEPHIR_BACKUP_THIS_PTR() \ - zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; + zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; \ + zend_class_entry *old_ce = Z_TYPE(EG(current_execute_data)->This) != IS_OBJECT ? Z_CE(EG(current_execute_data)->This) : NULL; #define ZEPHIR_RESTORE_THIS_PTR() do { \ if (old_this_ptr) { \ ZEPHIR_SET_THIS_OBJ(old_this_ptr); \ + } else if (old_ce) { \ + ZVAL_UNDEF(&EG(current_execute_data)->This); \ + Z_CE(EG(current_execute_data)->This) = old_ce; \ } else { \ ZEPHIR_SET_THIS_EXPLICIT_NULL(); \ } \ From e9ca7db2797e6f48479667a23c21622e9456ddbf Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Fri, 1 Nov 2019 23:52:07 +0200 Subject: [PATCH 05/47] Ignore prove cache --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 672a8bcbc6..cd972c64c6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ ide/ doc/ vendor/ +.prove + *.gch *.lo *.la From efc6813605ada57696854789d6a066772ae52631 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 5 Nov 2019 14:18:55 +0200 Subject: [PATCH 06/47] add comment about description in return types --- Library/Stubs/MethodDocBlock.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Library/Stubs/MethodDocBlock.php b/Library/Stubs/MethodDocBlock.php index 758f0ffc82..607979e1a1 100644 --- a/Library/Stubs/MethodDocBlock.php +++ b/Library/Stubs/MethodDocBlock.php @@ -115,6 +115,7 @@ protected function parseMethodReturnType(ClassMethod $method) } if (!empty($return)) { + // Emty line in array - it's an empty description. Don't remove it! $this->return = [implode('|', $return), '']; } } From 73088a4c080ebcfd04a941d4b69b16bf24ad5804 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 5 Nov 2019 23:44:12 +0200 Subject: [PATCH 07/47] add type compatibility resolving for integer and float --- Library/Types.php | 75 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/Library/Types.php b/Library/Types.php index 6347b8fffe..403791a02c 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -26,13 +26,86 @@ final class Types const T_BOOL = 'bool'; const T_STRING = 'string'; const T_ISTRING = 'istring'; - const T_VOID = 'void'; const T_VARIABLE = 'variable'; const T_MIXED = 'mixed'; const T_ARRAY = 'array'; + const T_VOID = 'void'; const T_OBJECT = 'object'; const T_CALLABLE = 'callable'; const T_RESOURCE = 'resource'; const T_ITERABLE = 'iterable'; const T_UNDEFINED = 'undefined'; + + public static function getTypesBySpecification(?array $returnTypes): string + { + if (!static::hasReturnType($returnTypes)) { + return ''; + } + + return static::getCompatibleReturnType($returnTypes); + } + + public static function isVoid(array $returnTypes): bool + { + return \in_array(static::T_VOID, $returnTypes, true); + } + + public static function getCompatibleReturnType(array $types): string + { + foreach ($types as $k => $type) { + $isInteger = static::isTypeIntegerCompatible($type); + $isDouble = static::isTypeDoubleCompatible($type); + + $typeInteger = isset($typeInteger) ? ($isInteger && $typeInteger) : $isInteger; + $typeDouble = isset($typeDouble) ? ($isDouble && $typeDouble) : $isDouble; + } + + if ($typeInteger) { + $compatibleType = static::T_INT; + } elseif ($typeDouble) { + $compatibleType = static::T_FLOAT; + } + + return $compatibleType ?? static::T_MIXED; + } + + private static function isTypeIntegerCompatible(string $type): bool + { + switch ($type) { + case static::T_INT: + case static::T_UINT: + case static::T_CHAR: + case static::T_UCHAR: + case static::T_LONG: + case static::T_ULONG: + return true; + default: + return false; + } + } + + private static function isTypeDoubleCompatible(string $type): bool + { + return $type === static::T_DOUBLE; + } + + /** + * Checks if the parsed returns has return-type or cast hints. + * + * @param array|null $returnTypes + * + * @return bool + */ + private static function hasReturnType(?array $returnTypes): bool + { + if (null === $returnTypes) { + return false; + } + + if (0 === \count($returnTypes)) { + return false; + } + + return true; + } } From df9ca5b88698a721d75393a507168ea1d4592b02 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 5 Nov 2019 23:51:58 +0200 Subject: [PATCH 08/47] add boolean type resolving --- Library/Types.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Library/Types.php b/Library/Types.php index 403791a02c..77a87401df 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -55,15 +55,19 @@ public static function getCompatibleReturnType(array $types): string foreach ($types as $k => $type) { $isInteger = static::isTypeIntegerCompatible($type); $isDouble = static::isTypeDoubleCompatible($type); + $isBoolean = static::isTypeBoolCompatible($type); $typeInteger = isset($typeInteger) ? ($isInteger && $typeInteger) : $isInteger; $typeDouble = isset($typeDouble) ? ($isDouble && $typeDouble) : $isDouble; + $typeBoolean = isset($typeBoolean) ? ($isBoolean && $typeBoolean) : $isBoolean; } if ($typeInteger) { $compatibleType = static::T_INT; } elseif ($typeDouble) { $compatibleType = static::T_FLOAT; + } elseif ($typeBoolean) { + $compatibleType = static::T_BOOL; } return $compatibleType ?? static::T_MIXED; @@ -89,6 +93,11 @@ private static function isTypeDoubleCompatible(string $type): bool return $type === static::T_DOUBLE; } + private static function isTypeBoolCompatible(string $type): bool + { + return $type === static::T_BOOL; + } + /** * Checks if the parsed returns has return-type or cast hints. * From e98c8a30fd934ecd514a22bc780ae716fe679472 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 5 Nov 2019 23:57:11 +0200 Subject: [PATCH 09/47] add array type resolving --- Library/Types.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Library/Types.php b/Library/Types.php index 77a87401df..793e9190c3 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -56,10 +56,12 @@ public static function getCompatibleReturnType(array $types): string $isInteger = static::isTypeIntegerCompatible($type); $isDouble = static::isTypeDoubleCompatible($type); $isBoolean = static::isTypeBoolCompatible($type); + $isArray = static::isTypeArrayCompatible($type); $typeInteger = isset($typeInteger) ? ($isInteger && $typeInteger) : $isInteger; $typeDouble = isset($typeDouble) ? ($isDouble && $typeDouble) : $isDouble; $typeBoolean = isset($typeBoolean) ? ($isBoolean && $typeBoolean) : $isBoolean; + $typeArray = isset($typeArray) ? ($isArray && $typeArray) : $isArray; } if ($typeInteger) { @@ -68,6 +70,8 @@ public static function getCompatibleReturnType(array $types): string $compatibleType = static::T_FLOAT; } elseif ($typeBoolean) { $compatibleType = static::T_BOOL; + } elseif ($typeArray) { + $compatibleType = static::T_ARRAY; } return $compatibleType ?? static::T_MIXED; @@ -98,6 +102,11 @@ private static function isTypeBoolCompatible(string $type): bool return $type === static::T_BOOL; } + private static function isTypeArrayCompatible(string $type): bool + { + return $type === static::T_ARRAY; + } + /** * Checks if the parsed returns has return-type or cast hints. * From 423f43bcc9a158c99d6946d32120260f9f5f0d28 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 6 Nov 2019 00:20:29 +0200 Subject: [PATCH 10/47] add other types resolving --- Library/Types.php | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Library/Types.php b/Library/Types.php index 793e9190c3..77892a29b2 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -57,11 +57,19 @@ public static function getCompatibleReturnType(array $types): string $isDouble = static::isTypeDoubleCompatible($type); $isBoolean = static::isTypeBoolCompatible($type); $isArray = static::isTypeArrayCompatible($type); + $isNull = static::isTypeNullCompatible($type); + $isSpecial = static::isTypeSpecialCompatible($type); + $isVoid = static::isTypeVoidCompatible($type); + $isObject = static::isTypeObjectCompatible($type); $typeInteger = isset($typeInteger) ? ($isInteger && $typeInteger) : $isInteger; $typeDouble = isset($typeDouble) ? ($isDouble && $typeDouble) : $isDouble; $typeBoolean = isset($typeBoolean) ? ($isBoolean && $typeBoolean) : $isBoolean; $typeArray = isset($typeArray) ? ($isArray && $typeArray) : $isArray; + $typeNull = isset($typeNull) ? ($isNull && $typeNull) : $isNull; + $typeSpecial = isset($typeSpecial) ? ($isSpecial && $typeSpecial) : $isSpecial; + $typeVoid = isset($typeVoid) ? ($isVoid && $typeVoid) : $isVoid; + $typeObject = isset($typeObject) ? ($isObject && $typeObject) : $isObject; } if ($typeInteger) { @@ -72,6 +80,16 @@ public static function getCompatibleReturnType(array $types): string $compatibleType = static::T_BOOL; } elseif ($typeArray) { $compatibleType = static::T_ARRAY; + } elseif ($typeNull) { + $compatibleType = static::T_NULL; + } elseif ($typeSpecial) { + // TODO: change after test + $compatibleType = static::T_UNDEFINED; + } elseif ($typeVoid) { + $compatibleType = static::T_VOID; + } elseif ($typeObject) { + // TODO: change after test + $compatibleType = static::T_UNDEFINED; } return $compatibleType ?? static::T_MIXED; @@ -107,6 +125,35 @@ private static function isTypeArrayCompatible(string $type): bool return $type === static::T_ARRAY; } + private static function isTypeNullCompatible(string $type): bool + { + return $type === static::T_NULL; + } + + private static function isTypeSpecialCompatible(string $type): bool + { + switch ($type) { + case static::T_NUMBER: + case static::T_RESOURCE: + case static::T_VARIABLE: + case static::T_CALLABLE: + case static::T_ITERABLE: + return true; + default: + return false; + } + } + + private static function isTypeVoidCompatible(string $type): bool + { + return $type === static::T_VOID; + } + + private static function isTypeObjectCompatible(string $type): bool + { + return $type === static::T_OBJECT; + } + /** * Checks if the parsed returns has return-type or cast hints. * From c1412b8237a3cdea6699bcc21c1de0b32b459631 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 6 Nov 2019 17:55:47 +0200 Subject: [PATCH 11/47] Refactor type resolving for simple types --- Library/Stubs/MethodDocBlock.php | 8 +- Library/Types.php | 204 ++++++++++++--------------- unit-tests/Zephir/Test/TypesTest.php | 124 ++++++++++++++++ 3 files changed, 223 insertions(+), 113 deletions(-) create mode 100644 unit-tests/Zephir/Test/TypesTest.php diff --git a/Library/Stubs/MethodDocBlock.php b/Library/Stubs/MethodDocBlock.php index 607979e1a1..3846fa7603 100644 --- a/Library/Stubs/MethodDocBlock.php +++ b/Library/Stubs/MethodDocBlock.php @@ -114,9 +114,11 @@ protected function parseMethodReturnType(ClassMethod $method) } } - if (!empty($return)) { - // Emty line in array - it's an empty description. Don't remove it! - $this->return = [implode('|', $return), '']; + $returnType = \Zephir\Types::getCompatibleReturnType($this->classMethod); + + if (!empty($returnType)) { + // Empty line in array - it's an empty description. Don't remove it! + $this->return = [$returnType, '']; } } diff --git a/Library/Types.php b/Library/Types.php index 77892a29b2..3486745605 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -36,141 +36,125 @@ final class Types const T_ITERABLE = 'iterable'; const T_UNDEFINED = 'undefined'; - public static function getTypesBySpecification(?array $returnTypes): string + public const COMPATIBLETYPES = [ + self::T_INT => [ + self::T_INT, + self::T_UINT, + self::T_CHAR, + self::T_UCHAR, + self::T_LONG, + self::T_ULONG, + ], + self::T_FLOAT => [ + self::T_FLOAT, + self::T_DOUBLE, + ], + self::T_BOOL => [ + self::T_BOOL, + ], + self::T_STRING => [ + self::T_STRING, + self::T_ISTRING, + ], + self::T_NULL => [ + self::T_NULL, + ], + self::T_ARRAY => [ + self::T_ARRAY, + ], + self::T_OBJECT => [ + self::T_OBJECT, + ], + self::T_ITERABLE => [ + self::T_ITERABLE, + ], + self::T_RESOURCE => [ + self::T_RESOURCE, + ], + self::T_VOID => [ + self::T_VOID, + ], + ]; + + public static function getCompatibleReturnType(ClassMethod $method): string { - if (!static::hasReturnType($returnTypes)) { + if (!$method->hasReturnTypes() && !$method->isVoid()) { return ''; } - return static::getCompatibleReturnType($returnTypes); - } + $returnTypes = $method->getReturnTypes(); + $typesCount = \count($returnTypes); + + $isNulable = $method->areReturnTypesNullCompatible() ? '|null' : ''; + $isInteger = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_INT]); + $isDouble = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_FLOAT]); + $isBool = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_BOOL]); + $isString = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_STRING]); + $isNull = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_NULL]); + $isArray = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ARRAY]); + $isObject = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_OBJECT]); + $isIterable = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ITERABLE]); + $isResource = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_RESOURCE]); + $isVoid = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_VOID]); + + if ($method->isVoid() || $isVoid) { + return static::T_VOID; + } - public static function isVoid(array $returnTypes): bool - { - return \in_array(static::T_VOID, $returnTypes, true); - } + if ($isInteger) { + return static::T_INT.$isNulable; + } - public static function getCompatibleReturnType(array $types): string - { - foreach ($types as $k => $type) { - $isInteger = static::isTypeIntegerCompatible($type); - $isDouble = static::isTypeDoubleCompatible($type); - $isBoolean = static::isTypeBoolCompatible($type); - $isArray = static::isTypeArrayCompatible($type); - $isNull = static::isTypeNullCompatible($type); - $isSpecial = static::isTypeSpecialCompatible($type); - $isVoid = static::isTypeVoidCompatible($type); - $isObject = static::isTypeObjectCompatible($type); - - $typeInteger = isset($typeInteger) ? ($isInteger && $typeInteger) : $isInteger; - $typeDouble = isset($typeDouble) ? ($isDouble && $typeDouble) : $isDouble; - $typeBoolean = isset($typeBoolean) ? ($isBoolean && $typeBoolean) : $isBoolean; - $typeArray = isset($typeArray) ? ($isArray && $typeArray) : $isArray; - $typeNull = isset($typeNull) ? ($isNull && $typeNull) : $isNull; - $typeSpecial = isset($typeSpecial) ? ($isSpecial && $typeSpecial) : $isSpecial; - $typeVoid = isset($typeVoid) ? ($isVoid && $typeVoid) : $isVoid; - $typeObject = isset($typeObject) ? ($isObject && $typeObject) : $isObject; + if ($isDouble) { + return static::T_FLOAT.$isNulable; } - if ($typeInteger) { - $compatibleType = static::T_INT; - } elseif ($typeDouble) { - $compatibleType = static::T_FLOAT; - } elseif ($typeBoolean) { - $compatibleType = static::T_BOOL; - } elseif ($typeArray) { - $compatibleType = static::T_ARRAY; - } elseif ($typeNull) { - $compatibleType = static::T_NULL; - } elseif ($typeSpecial) { - // TODO: change after test - $compatibleType = static::T_UNDEFINED; - } elseif ($typeVoid) { - $compatibleType = static::T_VOID; - } elseif ($typeObject) { - // TODO: change after test - $compatibleType = static::T_UNDEFINED; + if ($isBool) { + return static::T_BOOL.$isNulable; } - return $compatibleType ?? static::T_MIXED; - } + if ($isString) { + return static::T_STRING.$isNulable; + } - private static function isTypeIntegerCompatible(string $type): bool - { - switch ($type) { - case static::T_INT: - case static::T_UINT: - case static::T_CHAR: - case static::T_UCHAR: - case static::T_LONG: - case static::T_ULONG: - return true; - default: - return false; + if ($isNull && 1 === $typesCount) { + return static::T_NULL; } - } - private static function isTypeDoubleCompatible(string $type): bool - { - return $type === static::T_DOUBLE; - } + if ($isArray) { + return static::T_ARRAY; + } - private static function isTypeBoolCompatible(string $type): bool - { - return $type === static::T_BOOL; - } + if ($isObject) { + return static::T_OBJECT; + } - private static function isTypeArrayCompatible(string $type): bool - { - return $type === static::T_ARRAY; - } + if ($isIterable) { + return static::T_ITERABLE; + } - private static function isTypeNullCompatible(string $type): bool - { - return $type === static::T_NULL; - } + if ($isResource) { + return static::T_RESOURCE; + } - private static function isTypeSpecialCompatible(string $type): bool - { - switch ($type) { - case static::T_NUMBER: - case static::T_RESOURCE: - case static::T_VARIABLE: - case static::T_CALLABLE: - case static::T_ITERABLE: - return true; - default: - return false; + if ($method->areReturnTypesCompatible()) { + return static::T_MIXED; } - } - private static function isTypeVoidCompatible(string $type): bool - { - return $type === static::T_VOID; + return static::T_MIXED; } - private static function isTypeObjectCompatible(string $type): bool + private static function areReturnTypesCompatible(array $types, array $allowedTypes): bool { - return $type === static::T_OBJECT; - } + $result = null; + $areEquals = false; - /** - * Checks if the parsed returns has return-type or cast hints. - * - * @param array|null $returnTypes - * - * @return bool - */ - private static function hasReturnType(?array $returnTypes): bool - { - if (null === $returnTypes) { - return false; - } + foreach ($types as $type => $data) { + $areEquals = \in_array($type, $allowedTypes); - if (0 === \count($returnTypes)) { - return false; + $result = isset($result) ? ($areEquals && $result) : $areEquals; } - return true; + return $result ?? false; } } diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php new file mode 100644 index 0000000000..d6cdfc4cbf --- /dev/null +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zephir\Test; + +use PHPUnit\Framework\TestCase; +use Zephir\ClassDefinition; +use Zephir\ClassMethod; +use Zephir\Types; + +class TypesTest extends TestCase +{ + public function buildMethod(array $testData): ClassMethod + { + return new ClassMethod( + new ClassDefinition('Zephir', 'testMethod'), + ['public'], + 'exampleMethodName', + null, + null, + null, + [ + 'type' => 'return-type', + 'list' => array_map( + function ($type) { + return [ + 'type' => 'return-type-parameter', + 'data-type' => $type, + 'mandatory' => 0, + 'file' => 'stubs.zep', + 'line' => 1, + 'char' => 42, + ]; + }, + $testData + ), + ] + ); + } + + public function typesDataProvider(): array + { + return [ + [ + ['int'], 'int', + ], + [ + ['uint'], 'int', + ], + [ + ['char'], 'int', + ], + [ + ['uchar'], 'int', + ], + [ + ['long'], 'int', + ], + [ + ['ulong'], 'int', + ], + [ + ['double'], 'float', + ], + [ + ['float'], 'float', + ], + [ + ['number'], 'mixed', + ], + [ + ['null'], 'null', + ], + [ + ['bool'], 'bool', + ], + [ + ['string'], 'string', + ], + [ + ['istring'], 'string', + ], + [ + ['array'], 'array', + ], + [ + ['object'], 'object', + ], + [ + ['iterable'], 'iterable', + ], + [ + ['resource'], 'resource', + ], + [ + ['void'], 'void', + ], + [ + ['callable'], 'mixed', + ], + ]; + } + + /** + * @test + * @dataProvider typesDataProvider + */ + public function integerTypeCompatible(array $returnTypes, string $expected) + { + $testMethod = $this->buildMethod($returnTypes); + + $actual = Types::getCompatibleReturnType($testMethod); + + $this->assertSame($actual, $expected); + } +} From 10a6049c9194d93009ed0471288960cfcb9ad51e Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 6 Nov 2019 21:29:54 +0200 Subject: [PATCH 12/47] do not use static access for types --- Library/Stubs/MethodDocBlock.php | 8 ++++-- Library/Types.php | 42 +++++++++++++++++++--------- unit-tests/Zephir/Test/TypesTest.php | 3 +- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/Library/Stubs/MethodDocBlock.php b/Library/Stubs/MethodDocBlock.php index 3846fa7603..4364b212cc 100644 --- a/Library/Stubs/MethodDocBlock.php +++ b/Library/Stubs/MethodDocBlock.php @@ -40,7 +40,10 @@ class MethodDocBlock extends DocBlock /** @var ClassMethod */ private $classMethod; - public function __construct(ClassMethod $method, AliasManager $aliasManager, $indent = ' ') + /** @var Types */ + private $types; + + public function __construct(ClassMethod $method, AliasManager $aliasManager, $indent = ' ', Types $types = null) { parent::__construct($method->getDocBlock(), $indent); @@ -48,6 +51,7 @@ public function __construct(ClassMethod $method, AliasManager $aliasManager, $in $this->aliasManager = $aliasManager; $this->shortcutName = $method->isShortcut() ? $method->getShortcutName() : ''; $this->classMethod = $method; + $this->types = $types ?? new Types(); } /** @@ -114,7 +118,7 @@ protected function parseMethodReturnType(ClassMethod $method) } } - $returnType = \Zephir\Types::getCompatibleReturnType($this->classMethod); + $returnType = $this->types->getCompatibleReturnType($this->classMethod); if (!empty($returnType)) { // Empty line in array - it's an empty description. Don't remove it! diff --git a/Library/Types.php b/Library/Types.php index 3486745605..72f2375e3f 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -36,7 +36,7 @@ final class Types const T_ITERABLE = 'iterable'; const T_UNDEFINED = 'undefined'; - public const COMPATIBLETYPES = [ + const COMPATIBLETYPES = [ self::T_INT => [ self::T_INT, self::T_UINT, @@ -76,7 +76,14 @@ final class Types ], ]; - public static function getCompatibleReturnType(ClassMethod $method): string + /** + * Gets PHP compatible return type from class method. + * + * @param ClassMethod $method + * + * @return string + */ + public function getCompatibleReturnType(ClassMethod $method): string { if (!$method->hasReturnTypes() && !$method->isVoid()) { return ''; @@ -86,16 +93,16 @@ public static function getCompatibleReturnType(ClassMethod $method): string $typesCount = \count($returnTypes); $isNulable = $method->areReturnTypesNullCompatible() ? '|null' : ''; - $isInteger = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_INT]); - $isDouble = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_FLOAT]); - $isBool = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_BOOL]); - $isString = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_STRING]); - $isNull = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_NULL]); - $isArray = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ARRAY]); - $isObject = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_OBJECT]); - $isIterable = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ITERABLE]); - $isResource = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_RESOURCE]); - $isVoid = static::areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_VOID]); + $isInteger = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_INT]); + $isDouble = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_FLOAT]); + $isBool = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_BOOL]); + $isString = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_STRING]); + $isNull = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_NULL]); + $isArray = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ARRAY]); + $isObject = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_OBJECT]); + $isIterable = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ITERABLE]); + $isResource = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_RESOURCE]); + $isVoid = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_VOID]); if ($method->isVoid() || $isVoid) { return static::T_VOID; @@ -144,7 +151,16 @@ public static function getCompatibleReturnType(ClassMethod $method): string return static::T_MIXED; } - private static function areReturnTypesCompatible(array $types, array $allowedTypes): bool + /** + * Match if return types from Zephire are compatible + * with allowed return types from PHP. + * + * @param array $types - Return types from parser + * @param array $allowedTypes - Allowed return types + * + * @return bool + */ + private function areReturnTypesCompatible(array $types, array $allowedTypes): bool { $result = null; $areEquals = false; diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index d6cdfc4cbf..fd74bebe8d 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -116,8 +116,9 @@ public function typesDataProvider(): array public function integerTypeCompatible(array $returnTypes, string $expected) { $testMethod = $this->buildMethod($returnTypes); + $testTypes = new Types(); - $actual = Types::getCompatibleReturnType($testMethod); + $actual = $testTypes->getCompatibleReturnType($testMethod); $this->assertSame($actual, $expected); } From 31a9d3d87c9295f92dbf5f6080c88c5c87ed7d45 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 6 Nov 2019 21:56:02 +0200 Subject: [PATCH 13/47] amend missed include --- Library/Stubs/MethodDocBlock.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Library/Stubs/MethodDocBlock.php b/Library/Stubs/MethodDocBlock.php index 4364b212cc..03eca13bcd 100644 --- a/Library/Stubs/MethodDocBlock.php +++ b/Library/Stubs/MethodDocBlock.php @@ -13,6 +13,7 @@ use Zephir\AliasManager; use Zephir\ClassMethod; +use Zephir\Types; /** * Stubs Generator. From e5e82aa6cf73a587a816b76fcdc8a98875b2c482 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 00:43:16 +0200 Subject: [PATCH 14/47] improve tests flexibility --- Library/Types.php | 28 ++++++----- unit-tests/Zephir/Test/TypesTest.php | 73 ++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/Library/Types.php b/Library/Types.php index 72f2375e3f..176af3bd67 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -83,20 +83,20 @@ final class Types * * @return string */ - public function getCompatibleReturnType(ClassMethod $method): string + public function getCompatibleReturnType(ClassMethod $method, array $returnTypes = null): string { if (!$method->hasReturnTypes() && !$method->isVoid()) { return ''; } - $returnTypes = $method->getReturnTypes(); + $returnTypes = $returnTypes ?? $method->getReturnTypes(); $typesCount = \count($returnTypes); - $isNulable = $method->areReturnTypesNullCompatible() ? '|null' : ''; + $isNullable = $method->areReturnTypesNullCompatible() && 1 !== $typesCount; $isInteger = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_INT]); $isDouble = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_FLOAT]); $isBool = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_BOOL]); - $isString = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_STRING]); + $isString = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_STRING], $isNullable); $isNull = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_NULL]); $isArray = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ARRAY]); $isObject = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_OBJECT]); @@ -104,24 +104,26 @@ public function getCompatibleReturnType(ClassMethod $method): string $isResource = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_RESOURCE]); $isVoid = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_VOID]); + $nullable = $isNullable ? '|null' : ''; + if ($method->isVoid() || $isVoid) { return static::T_VOID; } if ($isInteger) { - return static::T_INT.$isNulable; + return static::T_INT.$nullable; } if ($isDouble) { - return static::T_FLOAT.$isNulable; + return static::T_FLOAT.$nullable; } if ($isBool) { - return static::T_BOOL.$isNulable; + return static::T_BOOL.$nullable; } if ($isString) { - return static::T_STRING.$isNulable; + return static::T_STRING.$nullable; } if ($isNull && 1 === $typesCount) { @@ -145,10 +147,10 @@ public function getCompatibleReturnType(ClassMethod $method): string } if ($method->areReturnTypesCompatible()) { - return static::T_MIXED; + return static::T_MIXED.$nullable; } - return static::T_MIXED; + return static::T_MIXED.$nullable; } /** @@ -160,11 +162,15 @@ public function getCompatibleReturnType(ClassMethod $method): string * * @return bool */ - private function areReturnTypesCompatible(array $types, array $allowedTypes): bool + private function areReturnTypesCompatible(array $types, array $allowedTypes, bool $isNullable = false): bool { $result = null; $areEquals = false; + if ($isNullable) { + array_push($allowedTypes, static::T_NULL); + } + foreach ($types as $type => $data) { $areEquals = \in_array($type, $allowedTypes); diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index fd74bebe8d..76cc8c23cf 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -18,7 +18,27 @@ class TypesTest extends TestCase { - public function buildMethod(array $testData): ClassMethod + private function baseClassDefinition(array $types): array + { + return [ + 'type' => 'return-type', + 'list' => array_map( + function ($type) { + return [ + 'type' => 'return-type-parameter', + 'data-type' => $type, + 'mandatory' => 0, + 'file' => 'stubs.zep', + 'line' => 1, + 'char' => 42, + ]; + }, + $types, + ), + ]; + } + + private function buildMethod(array $testData, string $definition): ClassMethod { return new ClassMethod( new ClassDefinition('Zephir', 'testMethod'), @@ -27,22 +47,7 @@ public function buildMethod(array $testData): ClassMethod null, null, null, - [ - 'type' => 'return-type', - 'list' => array_map( - function ($type) { - return [ - 'type' => 'return-type-parameter', - 'data-type' => $type, - 'mandatory' => 0, - 'file' => 'stubs.zep', - 'line' => 1, - 'char' => 42, - ]; - }, - $testData - ), - ] + $this->$definition($testData), ); } @@ -106,6 +111,12 @@ public function typesDataProvider(): array [ ['callable'], 'mixed', ], + [ + ['var', 'null'], 'mixed|null', + ], + [ + ['string', 'null'], 'string|null', + ], ]; } @@ -113,9 +124,33 @@ public function typesDataProvider(): array * @test * @dataProvider typesDataProvider */ - public function integerTypeCompatible(array $returnTypes, string $expected) + public function shouldResolveCompatibleTypeForBaseTypes(array $returnTypes, string $expected) + { + $testMethod = $this->buildMethod($returnTypes, 'baseClassDefinition'); + $testTypes = new Types(); + + $actual = $testTypes->getCompatibleReturnType($testMethod); + + $this->assertSame($actual, $expected); + } + + public function objectsDataProvider(): array + { + return [ + [ + [''], 'EventsManagerInterface', + ], + ]; + } + + /** + * test. + * + * @dataProvider objectsDataProvider + */ + public function shouldResolveCompatibleTypeForObjects(array $returnTypes, string $expected) { - $testMethod = $this->buildMethod($returnTypes); + $testMethod = $this->buildMethod($returnTypes, 'baseClassDefinition'); $testTypes = new Types(); $actual = $testTypes->getCompatibleReturnType($testMethod); From 0396025bbcbec5a2ac1fe1701a2abbd4da80ce2a Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 11:06:07 +0200 Subject: [PATCH 15/47] Improve code readability, add object definition for test --- Library/Types.php | 6 ++++-- unit-tests/Zephir/Test/TypesTest.php | 28 +++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Library/Types.php b/Library/Types.php index 176af3bd67..d14bbe2408 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -154,7 +154,7 @@ public function getCompatibleReturnType(ClassMethod $method, array $returnTypes } /** - * Match if return types from Zephire are compatible + * Match if return types from Zephir are compatible * with allowed return types from PHP. * * @param array $types - Return types from parser @@ -174,7 +174,9 @@ private function areReturnTypesCompatible(array $types, array $allowedTypes, boo foreach ($types as $type => $data) { $areEquals = \in_array($type, $allowedTypes); - $result = isset($result) ? ($areEquals && $result) : $areEquals; + $result = isset($result) + ? ($areEquals && $result) + : $areEquals; } return $result ?? false; diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index 76cc8c23cf..553c1e89a8 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -38,6 +38,32 @@ function ($type) { ]; } + private function objectClassDefinition(array $types): array + { + return [ + 'type' => 'return-type', + 'list' => array_map( + function ($type) { + return [ + 'type' => 'return-type-parameter', + 'cast' => [ + 'type' => 'variable', + 'value' => $type, + 'file' => 'stubs.zep', + 'line' => 8, + 'char' => 5, + ], + 'collection' => 0, + 'file' => 'stubs.zep', + 'line' => 8, + 'char' => 5, + ]; + }, + $types, + ), + ]; + } + private function buildMethod(array $testData, string $definition): ClassMethod { return new ClassMethod( @@ -150,7 +176,7 @@ public function objectsDataProvider(): array */ public function shouldResolveCompatibleTypeForObjects(array $returnTypes, string $expected) { - $testMethod = $this->buildMethod($returnTypes, 'baseClassDefinition'); + $testMethod = $this->buildMethod($returnTypes, 'objectClassDefinition'); $testTypes = new Types(); $actual = $testTypes->getCompatibleReturnType($testMethod); From a3bc3f20883a3906f2881e8e8cce22141359702d Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 14:59:44 +0200 Subject: [PATCH 16/47] support objects types resolving --- Library/Stubs/MethodDocBlock.php | 3 +- Library/Types.php | 10 ++++- unit-tests/Zephir/Test/TypesTest.php | 44 +++++++++++++++++-- .../stubs/issues/expected/Issue_1986.zep.php | 1 + 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/Library/Stubs/MethodDocBlock.php b/Library/Stubs/MethodDocBlock.php index 03eca13bcd..fde8855677 100644 --- a/Library/Stubs/MethodDocBlock.php +++ b/Library/Stubs/MethodDocBlock.php @@ -119,7 +119,8 @@ protected function parseMethodReturnType(ClassMethod $method) } } - $returnType = $this->types->getCompatibleReturnType($this->classMethod); + $processedTypes = !empty($method->getReturnClassTypes()) ? $return : null; + $returnType = $this->types->getCompatibleReturnType($this->classMethod, $processedTypes); if (!empty($returnType)) { // Empty line in array - it's an empty description. Don't remove it! diff --git a/Library/Types.php b/Library/Types.php index d14bbe2408..93563d6cef 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -103,7 +103,11 @@ public function getCompatibleReturnType(ClassMethod $method, array $returnTypes $isIterable = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ITERABLE]); $isResource = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_RESOURCE]); $isVoid = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_VOID]); + $isNumeric = $this->areReturnTypesCompatible($returnTypes, [static::T_NUMBER]); + $isDynamic = \in_array('var', array_keys($returnTypes)); + $isTypeHinted = $method->isReturnTypesHintDetermined(); + $isBasicTypes = $isArray || $isBool || $isDouble || $isInteger || $isResource || $isString || $isVoid || $isNumeric; $nullable = $isNullable ? '|null' : ''; if ($method->isVoid() || $isVoid) { @@ -146,10 +150,14 @@ public function getCompatibleReturnType(ClassMethod $method, array $returnTypes return static::T_RESOURCE; } - if ($method->areReturnTypesCompatible()) { + if ($method->areReturnTypesCompatible() && !$isTypeHinted) { return static::T_MIXED.$nullable; } + if ($isTypeHinted && !$isBasicTypes && !$isDynamic) { + return implode('|', array_keys($returnTypes)); + } + return static::T_MIXED.$nullable; } diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index 553c1e89a8..b3ffcd80be 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -39,6 +39,17 @@ function ($type) { } private function objectClassDefinition(array $types): array + { + $ret = []; + + foreach ($types as $alias) { + $ret[$alias] = 'Stubs\\'.$alias; + } + + return $ret; + } + + private function processVariableReturnTypes(array $types): array { return [ 'type' => 'return-type', @@ -143,6 +154,9 @@ public function typesDataProvider(): array [ ['string', 'null'], 'string|null', ], + [ + ['char', 'ulong', 'int'], 'int', + ], ]; } @@ -164,22 +178,44 @@ public function objectsDataProvider(): array { return [ [ - [''], 'EventsManagerInterface', + ['EventsManagerInterface'], 'EventsManagerInterface', + ], + [ + ['EventsManagerInterface', 'StdClass'], 'EventsManagerInterface|StdClass', + ], + [ + ['EventsManagerInterface', 'null'], 'EventsManagerInterface|null', + ], + [ + ['SomeNamespace\EventsManagerInterface', 'null'], 'SomeNamespace\EventsManagerInterface|null', + ], + [ + ['\SomeNamespace\EventsManagerInterface'], '\SomeNamespace\EventsManagerInterface', ], ]; } /** - * test. - * + * @test * @dataProvider objectsDataProvider */ public function shouldResolveCompatibleTypeForObjects(array $returnTypes, string $expected) { + // This processes into MethodDocBlock with AliasManager resolving + $processedReturnTypes = $this->objectClassDefinition($returnTypes); + $testMethod = $this->buildMethod($returnTypes, 'objectClassDefinition'); + + $testMethod->setReturnTypes( + $this->processVariableReturnTypes($returnTypes) + ); + $testTypes = new Types(); - $actual = $testTypes->getCompatibleReturnType($testMethod); + $actual = $testTypes->getCompatibleReturnType( + $testMethod, + $processedReturnTypes + ); $this->assertSame($actual, $expected); } diff --git a/unit-tests/fixtures/stubs/issues/expected/Issue_1986.zep.php b/unit-tests/fixtures/stubs/issues/expected/Issue_1986.zep.php index 9143ec7b32..052dd6bb9a 100644 --- a/unit-tests/fixtures/stubs/issues/expected/Issue_1986.zep.php +++ b/unit-tests/fixtures/stubs/issues/expected/Issue_1986.zep.php @@ -22,6 +22,7 @@ public function getEventsManager(): EventsManagerInterface * * @param object|callable $handler * @param string $eventType + * @return void */ public function attach(string $eventType, $handler) { From 35a9439dbabc19ed9c436858bbd1ac0f3c934c35 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 17:45:48 +0200 Subject: [PATCH 17/47] changed method name, fixed test --- Library/Types.php | 2 +- unit-tests/Zephir/Test/TypesTest.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Library/Types.php b/Library/Types.php index 93563d6cef..dd7df3d55b 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -83,7 +83,7 @@ final class Types * * @return string */ - public function getCompatibleReturnType(ClassMethod $method, array $returnTypes = null): string + public function getReturnTypeAnnotation(ClassMethod $method, array $returnTypes = null): string { if (!$method->hasReturnTypes() && !$method->isVoid()) { return ''; diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index b3ffcd80be..1a8972aa43 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -33,7 +33,7 @@ function ($type) { 'char' => 42, ]; }, - $types, + $types ), ]; } @@ -70,7 +70,7 @@ function ($type) { 'char' => 5, ]; }, - $types, + $types ), ]; } @@ -169,7 +169,7 @@ public function shouldResolveCompatibleTypeForBaseTypes(array $returnTypes, stri $testMethod = $this->buildMethod($returnTypes, 'baseClassDefinition'); $testTypes = new Types(); - $actual = $testTypes->getCompatibleReturnType($testMethod); + $actual = $testTypes->getReturnTypeAnnotation($testMethod); $this->assertSame($actual, $expected); } @@ -212,7 +212,7 @@ public function shouldResolveCompatibleTypeForObjects(array $returnTypes, string $testTypes = new Types(); - $actual = $testTypes->getCompatibleReturnType( + $actual = $testTypes->getReturnTypeAnnotation( $testMethod, $processedReturnTypes ); From c64af73fa82bcb549544abe514c8efa57cd73aa2 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 23:05:20 +0200 Subject: [PATCH 18/47] print phpcs tools version into travis report --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9c3061a62e..8c3606740c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -184,8 +184,8 @@ script: # Static Code Analysis - phpenv config-rm xdebug.ini || true - - if [ "$BUILD_TYPE" == "analysis" ]; then phpcs; fi - - if [ "$BUILD_TYPE" == "analysis" ]; then php-cs-fixer fix --diff --dry-run -v; fi + - if [ "$BUILD_TYPE" == "analysis" ]; then phpcs --version; phpcs; fi + - if [ "$BUILD_TYPE" == "analysis" ]; then php-cs-fixer --version; php-cs-fixer fix --diff --dry-run -v; fi - if [ "$BUILD_TYPE" == "analysis" ]; then shellcheck .ci/*.sh; fi after_script: From dd15df0544696aab7cb740696ed2a32d9b040962 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 23:32:23 +0200 Subject: [PATCH 19/47] fix method name --- Library/Stubs/MethodDocBlock.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Library/Stubs/MethodDocBlock.php b/Library/Stubs/MethodDocBlock.php index fde8855677..c718daaf67 100644 --- a/Library/Stubs/MethodDocBlock.php +++ b/Library/Stubs/MethodDocBlock.php @@ -120,7 +120,7 @@ protected function parseMethodReturnType(ClassMethod $method) } $processedTypes = !empty($method->getReturnClassTypes()) ? $return : null; - $returnType = $this->types->getCompatibleReturnType($this->classMethod, $processedTypes); + $returnType = $this->types->getReturnTypeAnnotation($this->classMethod, $processedTypes); if (!empty($returnType)) { // Empty line in array - it's an empty description. Don't remove it! From 84186918e5e4c07f6bda4dcea3bdd3637063ed87 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 7 Nov 2019 23:38:44 +0200 Subject: [PATCH 20/47] move ignore rule for `prove` to proper gitignore --- .gitignore | 2 -- unit-tests/sharness/.gitignore | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index cd972c64c6..672a8bcbc6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,6 @@ ide/ doc/ vendor/ -.prove - *.gch *.lo *.la diff --git a/unit-tests/sharness/.gitignore b/unit-tests/sharness/.gitignore index 5e59048ac5..1fb80c33f9 100644 --- a/unit-tests/sharness/.gitignore +++ b/unit-tests/sharness/.gitignore @@ -1,3 +1,6 @@ lib/sharness/ test-results/ trash directory.*.sh/ + +# prove saved state +.prove From 719ce6a1f7622f53b59236b3ffb5244b992366f6 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Fri, 8 Nov 2019 00:01:30 +0200 Subject: [PATCH 21/47] remove useless and painful comma --- unit-tests/Zephir/Test/TypesTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index 1a8972aa43..736305b36e 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -84,7 +84,7 @@ private function buildMethod(array $testData, string $definition): ClassMethod null, null, null, - $this->$definition($testData), + $this->$definition($testData) ); } From 5b2b8ca7f32665f53a339019e27e1a64f58f6847 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Fri, 8 Nov 2019 15:48:07 +0200 Subject: [PATCH 22/47] improve readability, add test case --- Library/Types.php | 59 +++++++++++++++------------- unit-tests/Zephir/Test/TypesTest.php | 3 ++ 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/Library/Types.php b/Library/Types.php index dd7df3d55b..c8f954e39a 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -37,7 +37,7 @@ final class Types const T_UNDEFINED = 'undefined'; const COMPATIBLETYPES = [ - self::T_INT => [ + 'int' => [ self::T_INT, self::T_UINT, self::T_CHAR, @@ -45,33 +45,33 @@ final class Types self::T_LONG, self::T_ULONG, ], - self::T_FLOAT => [ + 'float' => [ self::T_FLOAT, self::T_DOUBLE, ], - self::T_BOOL => [ + 'bool' => [ self::T_BOOL, ], - self::T_STRING => [ + 'string' => [ self::T_STRING, self::T_ISTRING, ], - self::T_NULL => [ + 'null' => [ self::T_NULL, ], - self::T_ARRAY => [ + 'array' => [ self::T_ARRAY, ], - self::T_OBJECT => [ + 'object' => [ self::T_OBJECT, ], - self::T_ITERABLE => [ + 'iterable' => [ self::T_ITERABLE, ], - self::T_RESOURCE => [ + 'resource' => [ self::T_RESOURCE, ], - self::T_VOID => [ + 'void' => [ self::T_VOID, ], ]; @@ -92,42 +92,45 @@ public function getReturnTypeAnnotation(ClassMethod $method, array $returnTypes $returnTypes = $returnTypes ?? $method->getReturnTypes(); $typesCount = \count($returnTypes); - $isNullable = $method->areReturnTypesNullCompatible() && 1 !== $typesCount; - $isInteger = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_INT]); - $isDouble = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_FLOAT]); - $isBool = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_BOOL]); - $isString = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_STRING], $isNullable); - $isNull = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_NULL]); - $isArray = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ARRAY]); - $isObject = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_OBJECT]); - $isIterable = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_ITERABLE]); - $isResource = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_RESOURCE]); - $isVoid = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES[static::T_VOID]); + $isNullable = $method->areReturnTypesNullCompatible() + && 1 !== $typesCount; + + $isInteger = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['int']); + $isDouble = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['float']); + $isBool = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['bool']); + $isString = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['string'], $isNullable); + $isNull = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['null']); + $isArray = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['array']); + $isObject = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['object']); + $isIterable = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['iterable']); + $isResource = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['resource']); + $isVoid = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['void']); $isNumeric = $this->areReturnTypesCompatible($returnTypes, [static::T_NUMBER]); $isDynamic = \in_array('var', array_keys($returnTypes)); $isTypeHinted = $method->isReturnTypesHintDetermined(); $isBasicTypes = $isArray || $isBool || $isDouble || $isInteger || $isResource || $isString || $isVoid || $isNumeric; - $nullable = $isNullable ? '|null' : ''; + + $nullableType = $isNullable ? '|null' : ''; if ($method->isVoid() || $isVoid) { return static::T_VOID; } if ($isInteger) { - return static::T_INT.$nullable; + return static::T_INT.$nullableType; } if ($isDouble) { - return static::T_FLOAT.$nullable; + return static::T_FLOAT.$nullableType; } if ($isBool) { - return static::T_BOOL.$nullable; + return static::T_BOOL.$nullableType; } if ($isString) { - return static::T_STRING.$nullable; + return static::T_STRING.$nullableType; } if ($isNull && 1 === $typesCount) { @@ -151,14 +154,14 @@ public function getReturnTypeAnnotation(ClassMethod $method, array $returnTypes } if ($method->areReturnTypesCompatible() && !$isTypeHinted) { - return static::T_MIXED.$nullable; + return static::T_MIXED.$nullableType; } if ($isTypeHinted && !$isBasicTypes && !$isDynamic) { return implode('|', array_keys($returnTypes)); } - return static::T_MIXED.$nullable; + return static::T_MIXED.$nullableType; } /** diff --git a/unit-tests/Zephir/Test/TypesTest.php b/unit-tests/Zephir/Test/TypesTest.php index 736305b36e..c00bd94536 100644 --- a/unit-tests/Zephir/Test/TypesTest.php +++ b/unit-tests/Zephir/Test/TypesTest.php @@ -157,6 +157,9 @@ public function typesDataProvider(): array [ ['char', 'ulong', 'int'], 'int', ], + [ + ['mixed', 'string'], 'mixed|string', + ], ]; } From 4df481b12984314a9adb9b7400eedfc77c723a9b Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Sat, 9 Nov 2019 00:47:00 +0200 Subject: [PATCH 23/47] add sharness tests for stubs --- .../stubs/issues/expected/Issue_1900.zep.php | 144 ++++++++++++++++++ .../stubs/issues/stubs/issue_1900.zep | 122 +++++++++++++++ unit-tests/sharness/t0005-stubs.sh | 8 + 3 files changed, 274 insertions(+) create mode 100644 unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php create mode 100644 unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep diff --git a/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php b/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php new file mode 100644 index 0000000000..80ac22a25a --- /dev/null +++ b/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php @@ -0,0 +1,144 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +class Issue_1900 implements AliasedManagerInterface +{ + + public $eventsManager; + + /** + * @var array $collect - descr for collect var. + */ + private $collect; + + + static protected $uniqueId = 0; + + + /** + * @return AliasedManagerInterface + */ + public function getEventsManager(): AliasedManagerInterface + { + } + + /** + * @return \StdClass + */ + public function test01(): \StdClass + { + } + + /** + * Attach a listener to the events manager + * + * @param object|callable $handler + * @param string $eventType + * @param int $priority + * @return void + */ + public function attach(string $eventType, $handler, int $priority = 1) + { + } + + /** + * Returns if priorities are enabled + * + * @return bool + */ + public function arePrioritiesEnabled(): bool + { + } + + /** + * Tells the event manager if it needs to collect all the responses returned + * by every registered listener in a single fire + * + * @param bool $collect + * @param string $eventType + * @param mixed $handler + * @return void + */ + public function collectResponses(bool $collect, string $eventType, $handler) + { + } + + /** + * Fires an event in the events manager causing the active listeners to be + * notified about it + * + * ```php + * $eventsManager->fire("db", $connection); + * ``` + * + * @param object $source + * @param mixed $data + * @return mixed + * @param string $eventType + * @param bool $cancelable + */ + public function fire(string $eventType, $source, $data = null, bool $cancelable = true) + { + } + + /** + * Internal handler to call a queue of events + * + * @return mixed + * @param SplPriorityQueue $queue + * @param \Psr\Http\Message\RequestInterface $event + */ + final public function fireQueue(SplPriorityQueue $queue, \Psr\Http\Message\RequestInterface $event): AliasedManagerInterface + { + } + + /** + * Returns all the attached listeners of a certain type + * + * @param string $type + * @return array + */ + public function getListeners(string $type): array + { + } + + /** + * Returns all the responses returned by every handler executed by the last + * 'fire' executed + * + * @return array|null + */ + public function getResponses(): ?array + { + } + + /** + * @return resource + */ + public function getResources() + { + } + + /** + * Check whether certain type of event has listeners + * + * @param string $type + * @return AliasedManagerInterface + */ + public function hasListeners(string $type): AliasedManagerInterface + { + } + +} diff --git a/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep b/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep new file mode 100644 index 0000000000..7bc08dc9ac --- /dev/null +++ b/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep @@ -0,0 +1,122 @@ +/** + * This file is part of the Zephir. + * + * (c) Zephir Team + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Stubs; + +use Psr\Http\Message\RequestInterface; +use Stubs\Events\ManagerInterface as AliasedManagerInterface; + +class Issue_1900 implements AliasedManagerInterface +{ + public $eventsManager; + + /** @var array $collect - descr for collect var. */ + private $collect; + + protected static $uniqueId = 0; + + /** + * @return AliasedManagerInterface + */ + public function getEventsManager() -> + { + } + + public function test01() -> <\StdClass> + { + return new \StdClass(); + } + + /** + * Attach a listener to the events manager + * + * @param object|callable handler + */ + public function attach(string! eventType, var handler, int! priority = 1) -> void + { + var priorityQueue; + + // Insert the handler in the queue + priorityQueue->insert(handler, priority); + } + + /** + * Returns if priorities are enabled + */ + public function arePrioritiesEnabled() -> bool + { + return true; + } + + /** + * Tells the event manager if it needs to collect all the responses returned + * by every registered listener in a single fire + */ + public function collectResponses(bool collect, string! eventType, var handler) -> void + { + let this->collect = collect; + } + + /** + * Fires an event in the events manager causing the active listeners to be + * notified about it + * + *```php + * $eventsManager->fire("db", $connection); + *``` + * + * @param object source + * @param mixed data + * @return mixed + */ + public function fire(string! eventType, object source, var data = null, bool cancelable = true) + { + return 1; + } + + /** + * Internal handler to call a queue of events + * + * @return mixed + */ + final public function fireQueue( queue, event) -> + { + return event; + } + + /** + * Returns all the attached listeners of a certain type + */ + public function getListeners(string! type) -> array + { + return []; + } + + /** + * Returns all the responses returned by every handler executed by the last + * 'fire' executed + */ + public function getResponses() -> array | null + { + return []; + } + + public function getResources() -> Resource + { + return []; + } + + /** + * Check whether certain type of event has listeners + */ + public function hasListeners(string! type) -> + { + return []; + } +} diff --git a/unit-tests/sharness/t0005-stubs.sh b/unit-tests/sharness/t0005-stubs.sh index 1b671d5b8a..34cc72c2bd 100755 --- a/unit-tests/sharness/t0005-stubs.sh +++ b/unit-tests/sharness/t0005-stubs.sh @@ -22,6 +22,14 @@ test_expect_success "Should properly namespace imports (use block)" " test_cmp expected/Issue_1778.zep.php ide/0.0.1/Stubs/Issue_1778.zep.php " +# See: https://github.com/phalcon/zephir/issues/1900 +test_expect_success "Should properly generate return types for stubs" " + cd $FIXTURESDIR/stubs/issues && + zephirc generate --no-ansi 2>&1 >/dev/null && + zephirc stubs --no-ansi 2>&1 >/dev/null && + test_cmp expected/Issue_1900.zep.php ide/0.0.1/Stubs/Issue_1900.zep.php +" + # See: https://github.com/phalcon/zephir/issues/1907 test_expect_success "Should properly generate Namespace for extends" " cd $FIXTURESDIR/stubs/issues && From 2b0d884dd19223109d656ce017222621255cb332 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Sat, 9 Nov 2019 01:09:05 +0200 Subject: [PATCH 24/47] fix variables declaration for zep test --- unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep b/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep index 7bc08dc9ac..79c458a3cc 100644 --- a/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep +++ b/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep @@ -14,12 +14,12 @@ use Stubs\Events\ManagerInterface as AliasedManagerInterface; class Issue_1900 implements AliasedManagerInterface { - public $eventsManager; + public eventsManager; /** @var array $collect - descr for collect var. */ - private $collect; + private collect; - protected static $uniqueId = 0; + protected static uniqueId = 0; /** * @return AliasedManagerInterface From 04d6bccf315d99995b53b1317b71f800018ae032 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Sat, 9 Nov 2019 12:07:11 +0200 Subject: [PATCH 25/47] remove nullable return type from test --- .../stubs/issues/expected/Issue_1900.zep.php | 10 ---- .../stubs/issues/stubs/issue_1900.zep | 57 ++++++++----------- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php b/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php index 80ac22a25a..e3e717d086 100644 --- a/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php +++ b/unit-tests/fixtures/stubs/issues/expected/Issue_1900.zep.php @@ -114,16 +114,6 @@ public function getListeners(string $type): array { } - /** - * Returns all the responses returned by every handler executed by the last - * 'fire' executed - * - * @return array|null - */ - public function getResponses(): ?array - { - } - /** * @return resource */ diff --git a/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep b/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep index 79c458a3cc..f3c6ee2477 100644 --- a/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep +++ b/unit-tests/fixtures/stubs/issues/stubs/issue_1900.zep @@ -47,74 +47,65 @@ class Issue_1900 implements AliasedManagerInterface } /** - * Returns if priorities are enabled - */ + * Returns if priorities are enabled + */ public function arePrioritiesEnabled() -> bool { return true; } /** - * Tells the event manager if it needs to collect all the responses returned - * by every registered listener in a single fire - */ + * Tells the event manager if it needs to collect all the responses returned + * by every registered listener in a single fire + */ public function collectResponses(bool collect, string! eventType, var handler) -> void { let this->collect = collect; } /** - * Fires an event in the events manager causing the active listeners to be - * notified about it - * - *```php - * $eventsManager->fire("db", $connection); - *``` - * - * @param object source - * @param mixed data - * @return mixed - */ + * Fires an event in the events manager causing the active listeners to be + * notified about it + * + *```php + * $eventsManager->fire("db", $connection); + *``` + * + * @param object source + * @param mixed data + * @return mixed + */ public function fire(string! eventType, object source, var data = null, bool cancelable = true) { return 1; } /** - * Internal handler to call a queue of events - * - * @return mixed - */ + * Internal handler to call a queue of events + * + * @return mixed + */ final public function fireQueue( queue, event) -> { return event; } /** - * Returns all the attached listeners of a certain type - */ + * Returns all the attached listeners of a certain type + */ public function getListeners(string! type) -> array { return []; } - /** - * Returns all the responses returned by every handler executed by the last - * 'fire' executed - */ - public function getResponses() -> array | null - { - return []; - } - public function getResources() -> Resource { return []; } /** - * Check whether certain type of event has listeners - */ + * Check whether certain type of event has listeners + */ public function hasListeners(string! type) -> { return []; From 983f9941b6aa2ec132b890a8295ea2a67d28dac7 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Sat, 9 Nov 2019 13:08:51 +0200 Subject: [PATCH 26/47] Apply SRP to Types Class --- Library/Types.php | 235 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 181 insertions(+), 54 deletions(-) diff --git a/Library/Types.php b/Library/Types.php index c8f954e39a..9c63f8d0ca 100644 --- a/Library/Types.php +++ b/Library/Types.php @@ -36,46 +36,6 @@ final class Types const T_ITERABLE = 'iterable'; const T_UNDEFINED = 'undefined'; - const COMPATIBLETYPES = [ - 'int' => [ - self::T_INT, - self::T_UINT, - self::T_CHAR, - self::T_UCHAR, - self::T_LONG, - self::T_ULONG, - ], - 'float' => [ - self::T_FLOAT, - self::T_DOUBLE, - ], - 'bool' => [ - self::T_BOOL, - ], - 'string' => [ - self::T_STRING, - self::T_ISTRING, - ], - 'null' => [ - self::T_NULL, - ], - 'array' => [ - self::T_ARRAY, - ], - 'object' => [ - self::T_OBJECT, - ], - 'iterable' => [ - self::T_ITERABLE, - ], - 'resource' => [ - self::T_RESOURCE, - ], - 'void' => [ - self::T_VOID, - ], - ]; - /** * Gets PHP compatible return type from class method. * @@ -92,21 +52,20 @@ public function getReturnTypeAnnotation(ClassMethod $method, array $returnTypes $returnTypes = $returnTypes ?? $method->getReturnTypes(); $typesCount = \count($returnTypes); - $isNullable = $method->areReturnTypesNullCompatible() - && 1 !== $typesCount; - - $isInteger = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['int']); - $isDouble = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['float']); - $isBool = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['bool']); - $isString = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['string'], $isNullable); - $isNull = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['null']); - $isArray = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['array']); - $isObject = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['object']); - $isIterable = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['iterable']); - $isResource = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['resource']); - $isVoid = $this->areReturnTypesCompatible($returnTypes, static::COMPATIBLETYPES['void']); - $isNumeric = $this->areReturnTypesCompatible($returnTypes, [static::T_NUMBER]); $isDynamic = \in_array('var', array_keys($returnTypes)); + $isNullable = $this->isNullable($returnTypes); + + $isBool = $this->areReturnTypesBoolCompatible($returnTypes); + $isNull = $this->areReturnTypesNullCompatible($returnTypes); + $isVoid = $this->areReturnTypesVoidCompatible($returnTypes); + $isArray = $this->areReturnTypesArrayCompatible($returnTypes); + $isDouble = $this->areReturnTypesFloatCompatible($returnTypes); + $isString = $this->areReturnTypesStringCompatible($returnTypes); + $isObject = $this->areReturnTypesObjectCompatible($returnTypes); + $isInteger = $this->areReturnTypesIntegerCompatible($returnTypes); + $isNumeric = $this->isNumeric($returnTypes); + $isIterable = $this->areReturnTypesIterableCompatible($returnTypes); + $isResource = $this->areReturnTypesResourceCompatible($returnTypes); $isTypeHinted = $method->isReturnTypesHintDetermined(); $isBasicTypes = $isArray || $isBool || $isDouble || $isInteger || $isResource || $isString || $isVoid || $isNumeric; @@ -164,6 +123,174 @@ public function getReturnTypeAnnotation(ClassMethod $method, array $returnTypes return static::T_MIXED.$nullableType; } + /** + * Match Zephir types with Integer type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesIntegerCompatible(array $types): bool + { + return $this->areReturnTypesCompatible( + $types, + [ + self::T_INT, + self::T_UINT, + self::T_CHAR, + self::T_UCHAR, + self::T_LONG, + self::T_ULONG, + ] + ); + } + + /** + * Match Zephir types with Float type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesFloatCompatible(array $types): bool + { + return $this->areReturnTypesCompatible( + $types, + [ + self::T_FLOAT, + self::T_DOUBLE, + ] + ); + } + + /** + * Match Zephir types with Boolean type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesBoolCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_BOOL]); + } + + /** + * Match Zephir types with String type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesStringCompatible(array $types): bool + { + return $this->areReturnTypesCompatible( + $types, + [ + static::T_STRING, + static::T_ISTRING, + ], + $this->isNullable($types) + ); + } + + /** + * Match Zephir types with Null type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesNullCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_NULL]); + } + + /** + * Match Zephir types with Array type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesArrayCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_ARRAY]); + } + + /** + * Match Zephir types with Object type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesObjectCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_OBJECT]); + } + + /** + * Match Zephir types with Iterable type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesIterableCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_ITERABLE]); + } + + /** + * Match Zephir types with Resource type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesResourceCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_RESOURCE]); + } + + /** + * Match Zephir types with Void type. + * + * @param array $types + * + * @return bool + */ + private function areReturnTypesVoidCompatible(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_VOID]); + } + + /** + * Check if Zephir types is a Numeric type compatible. + * + * @param array $types + * + * @return bool + */ + private function isNumeric(array $types): bool + { + return $this->areReturnTypesCompatible($types, [static::T_NUMBER]); + } + + /** + * Check if Zephir types can be Nullable. + * + * @param array $types + * + * @return bool + */ + private function isNullable(array $types): bool + { + return \array_key_exists(static::T_NULL, $types) + && 1 !== \count($types); + } + /** * Match if return types from Zephir are compatible * with allowed return types from PHP. From 121a17ede93eb4d1e523afaecf7e5d7d664e49b3 Mon Sep 17 00:00:00 2001 From: Anton Vasiliev Date: Sun, 10 Nov 2019 09:58:18 +0000 Subject: [PATCH 27/47] Fix typo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8c3606740c..2d17f2987e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -148,7 +148,7 @@ install: # Prepare Zephir executable if [ "$BUILD_PHAR" -eq 1 ] then - echo "Build Zephit PHAR" + echo "Build Zephir PHAR" .ci/build-phar.sh sudo ln -s "$(pwd)/zephir.phar" /usr/local/bin/zephir else From 5462ecd20c5c409639a6f9ad76341a5060f86f0f Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 12 Nov 2019 23:41:36 +0200 Subject: [PATCH 28/47] Fix lowercase folders for stubs --- Library/Stubs/Generator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Library/Stubs/Generator.php b/Library/Stubs/Generator.php index 6cb0113c1f..8eb7cce982 100644 --- a/Library/Stubs/Generator.php +++ b/Library/Stubs/Generator.php @@ -72,10 +72,10 @@ public function generate(string $namespace, string $path, string $indent) $source = $this->buildClass($class, $indent); $filename = ucfirst($class->getName()).'.zep.php'; - $filePath = $path.str_replace( + $filePath = $path.str_ireplace( $namespace, '', - str_replace($namespace.'\\\\', \DIRECTORY_SEPARATOR, strtolower($class->getNamespace())) + str_replace($namespace.'\\\\', \DIRECTORY_SEPARATOR, $class->getNamespace()) ); $filePath = str_replace('\\', \DIRECTORY_SEPARATOR, $filePath); $filePath = str_replace(\DIRECTORY_SEPARATOR.\DIRECTORY_SEPARATOR, \DIRECTORY_SEPARATOR, $filePath); From fe8eb0dae82baa964a884cc7fadc13b227b4b1a4 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 13 Nov 2019 00:52:40 +0200 Subject: [PATCH 29/47] Refactor sharness tests --- unit-tests/sharness/t0005-stubs.sh | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/unit-tests/sharness/t0005-stubs.sh b/unit-tests/sharness/t0005-stubs.sh index 34cc72c2bd..33665fe575 100755 --- a/unit-tests/sharness/t0005-stubs.sh +++ b/unit-tests/sharness/t0005-stubs.sh @@ -6,27 +6,35 @@ test_description="Test generate IDE stubs" # shellcheck disable=SC1091 source ./setup.sh +# shellcheck disable=SC2164,SC2069 +# Generate Stubs for test +zephir_stubs() { + cd "$FIXTURESDIR"/stubs/issues + zephirc generate --no-ansi 2>&1 >/dev/null + zephirc stubs --no-ansi 2>&1 >/dev/null +} + +# This test generates Stubs once and all other tests uses this build artifacts +test_expect_success "Should generate Stubs" " + zephir_stubs && + test -d ./ide/0.0.1/Stubs +" + # See: https://github.com/phalcon/zephir/issues/1922 test_expect_success "Should properly generate type hint" " cd $FIXTURESDIR/stubs/issues && - zephirc generate --no-ansi 2>&1 >/dev/null && - zephirc stubs --no-ansi 2>&1 >/dev/null && test_cmp expected/Issue_1922.zep.php ide/0.0.1/Stubs/Issue_1922.zep.php " # See: https://github.com/phalcon/zephir/issues/1778 test_expect_success "Should properly namespace imports (use block)" " cd $FIXTURESDIR/stubs/issues && - zephirc generate --no-ansi 2>&1 >/dev/null && - zephirc stubs --no-ansi 2>&1 >/dev/null && test_cmp expected/Issue_1778.zep.php ide/0.0.1/Stubs/Issue_1778.zep.php " # See: https://github.com/phalcon/zephir/issues/1900 test_expect_success "Should properly generate return types for stubs" " cd $FIXTURESDIR/stubs/issues && - zephirc generate --no-ansi 2>&1 >/dev/null && - zephirc stubs --no-ansi 2>&1 >/dev/null && test_cmp expected/Issue_1900.zep.php ide/0.0.1/Stubs/Issue_1900.zep.php " @@ -41,17 +49,19 @@ test_expect_success "Should properly generate Namespace for extends" " # See: https://github.com/phalcon/zephir/issues/1907 test_expect_success "Should properly generate Namespace for extends (slash)" " cd $FIXTURESDIR/stubs/issues && - zephirc generate --no-ansi 2>&1 >/dev/null && - zephirc stubs --no-ansi 2>&1 >/dev/null && test_cmp expected/Issue_1907.zep.php ide/0.0.1/Stubs/Issue_1907.zep.php " # See: https://github.com/phalcon/zephir/issues/1986 test_expect_success "Should properly generate Aliases for use statements" " cd $FIXTURESDIR/stubs/issues && - zephirc generate --no-ansi 2>&1 >/dev/null && - zephirc stubs --no-ansi 2>&1 >/dev/null && test_cmp expected/Issue_1986.zep.php ide/0.0.1/Stubs/Issue_1986.zep.php " +# See: https://github.com/phalcon/zephir/issues/1896 +test_expect_success "Should generage CamelCase folders for stubs" " + cd $FIXTURESDIR/stubs/issues && + test $(ls ./ide/0.0.1/Stubs/Events/ManagerInterface.zep.php | sed -e 's~\/~\\~g') = .\ide\0.0.1\Stubs\Events\ManagerInterface.zep.php +" + test_done From 5c805cdd3def6a69b9c954d1d0777a67f079f60b Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 13 Nov 2019 12:28:22 +0200 Subject: [PATCH 30/47] Beautify Sharness test for Stubs --- unit-tests/sharness/t0005-stubs.sh | 52 ++++++++++-------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/unit-tests/sharness/t0005-stubs.sh b/unit-tests/sharness/t0005-stubs.sh index 33665fe575..9c5b4d36a2 100755 --- a/unit-tests/sharness/t0005-stubs.sh +++ b/unit-tests/sharness/t0005-stubs.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # shellcheck disable=SC2034 -test_description="Test generate IDE stubs" +test_description="Test generates IDE stubs" # shellcheck disable=SC1091 source ./setup.sh @@ -15,53 +15,35 @@ zephir_stubs() { } # This test generates Stubs once and all other tests uses this build artifacts -test_expect_success "Should generate Stubs" " - zephir_stubs && - test -d ./ide/0.0.1/Stubs -" +test_expect_success "Should generate Stubs" \ + "zephir_stubs && test -d ./ide/0.0.1/Stubs" # See: https://github.com/phalcon/zephir/issues/1922 -test_expect_success "Should properly generate type hint" " - cd $FIXTURESDIR/stubs/issues && - test_cmp expected/Issue_1922.zep.php ide/0.0.1/Stubs/Issue_1922.zep.php -" +test_expect_success "Should properly generate type hint" \ + "test_cmp expected/Issue_1922.zep.php ide/0.0.1/Stubs/Issue_1922.zep.php" # See: https://github.com/phalcon/zephir/issues/1778 -test_expect_success "Should properly namespace imports (use block)" " - cd $FIXTURESDIR/stubs/issues && - test_cmp expected/Issue_1778.zep.php ide/0.0.1/Stubs/Issue_1778.zep.php -" +test_expect_success "Should properly namespace imports (use block)" \ + "test_cmp expected/Issue_1778.zep.php ide/0.0.1/Stubs/Issue_1778.zep.php" # See: https://github.com/phalcon/zephir/issues/1900 -test_expect_success "Should properly generate return types for stubs" " - cd $FIXTURESDIR/stubs/issues && - test_cmp expected/Issue_1900.zep.php ide/0.0.1/Stubs/Issue_1900.zep.php -" +test_expect_success "Should properly generate return types for stubs" \ + "test_cmp expected/Issue_1900.zep.php ide/0.0.1/Stubs/Issue_1900.zep.php" # See: https://github.com/phalcon/zephir/issues/1907 -test_expect_success "Should properly generate Namespace for extends" " - cd $FIXTURESDIR/stubs/issues && - zephirc generate --no-ansi 2>&1 >/dev/null && - zephirc stubs --no-ansi 2>&1 >/dev/null && - test_cmp expected/Exception.zep.php ide/0.0.1/Stubs/Exception.zep.php -" +test_expect_success "Should properly generate Namespace for extends" \ + "test_cmp expected/Exception.zep.php ide/0.0.1/Stubs/Exception.zep.php" # See: https://github.com/phalcon/zephir/issues/1907 -test_expect_success "Should properly generate Namespace for extends (slash)" " - cd $FIXTURESDIR/stubs/issues && - test_cmp expected/Issue_1907.zep.php ide/0.0.1/Stubs/Issue_1907.zep.php -" +test_expect_success "Should properly generate Namespace for extends (slash)" \ + "test_cmp expected/Issue_1907.zep.php ide/0.0.1/Stubs/Issue_1907.zep.php" # See: https://github.com/phalcon/zephir/issues/1986 -test_expect_success "Should properly generate Aliases for use statements" " - cd $FIXTURESDIR/stubs/issues && - test_cmp expected/Issue_1986.zep.php ide/0.0.1/Stubs/Issue_1986.zep.php -" +test_expect_success "Should properly generate Aliases for use statements" \ + "test_cmp expected/Issue_1986.zep.php ide/0.0.1/Stubs/Issue_1986.zep.php" # See: https://github.com/phalcon/zephir/issues/1896 -test_expect_success "Should generage CamelCase folders for stubs" " - cd $FIXTURESDIR/stubs/issues && - test $(ls ./ide/0.0.1/Stubs/Events/ManagerInterface.zep.php | sed -e 's~\/~\\~g') = .\ide\0.0.1\Stubs\Events\ManagerInterface.zep.php -" +test_expect_success "Should generage CamelCase folders for stubs" \ + "test $(ls ./ide/0.0.1/Stubs/Events/ManagerInterface.zep.php | sed -e 's~\/~\\~g') = .\ide\0.0.1\Stubs\Events\ManagerInterface.zep.php" test_done From 8c54a40f9a3334dfc48c77c11d2bef704ac19700 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Wed, 13 Nov 2019 23:43:07 +0200 Subject: [PATCH 31/47] Add sample banner to config --- config.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config.json b/config.json index 136fabc730..ee90759135 100644 --- a/config.json +++ b/config.json @@ -18,7 +18,15 @@ "stubs": { "path": "ide\/%version%\/%namespace%\/", - "stubs-run-after-generate": false + "stubs-run-after-generate": false, + "banner": [ + "This file is part of the Zephir.", + "", + "(c) Zephir Team ", + "", + "For the full copyright and license information, please view", + "the LICENSE file that was distributed with this source code." + ] }, "api": { From ec3c80baa73c575facbb8dbcb856d5a82b1b7a69 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 00:40:41 +0200 Subject: [PATCH 32/47] Refactor test for Config --- unit-tests/Zephir/Test/ConfigTest.php | 113 +++++++++++++++----------- 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/unit-tests/Zephir/Test/ConfigTest.php b/unit-tests/Zephir/Test/ConfigTest.php index 0651463850..849bd7c504 100644 --- a/unit-tests/Zephir/Test/ConfigTest.php +++ b/unit-tests/Zephir/Test/ConfigTest.php @@ -23,10 +23,14 @@ class ConfigTest extends TestCase */ private $pwd; + /** @var Config $config */ + private $config; + public function setUp() { /* Store the current directory before to be change */ $this->pwd = getcwd(); + $this->config = new Config(); } /** @@ -41,51 +45,91 @@ public function tearDown() $this->cleanTmpConfigFile(); } + /** + * Clean config.json file into tmp dir. + */ + private function cleanTmpConfigFile() + { + /* clean config.json into tmp dir */ + $tmpConfigFile = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'config.json'; + + if (file_exists($tmpConfigFile)) { + unlink($tmpConfigFile); + } + } + /** * Test when we have a bad config.json file. * + * @test * @expectedException \Zephir\Exception * @expectedExceptionMessage The config.json file is not valid or there is * no Zephir extension initialized in this directory. */ - public function testConstructWithBadConfigFile() + public function constructWithBadConfigFile() { chdir(\constant('ZEPHIRPATH').'/unit-tests/fixtures/badconfig'); new Config(); } - public function testGetWithoutNamespace() + /** + * Test data provider. + * + * [ + * 'test name' => [ + * [$key, $value, $namespace], $expected, + * ], + * ... + * ] + * + * @return array + */ + public function setConfigDataProvider(): array { - $config = new Config(); - $config->set('verbose', false); - $this->assertFalse($config->get('verbose')); + return [ + 'set with namespace' => [ + ['unused-variable', false, 'warnings'], false, + ], + 'set without namespace' => [ + ['config', true, null], true, + ], + 'get with namespace' => [ + ['unused-variable', true, 'warnings'], true, + ], + 'get without namespace' => [ + ['verbose', false, null], false, + ], + 'directive don`t have duplicates with namespace' => [ + ['ext.some_key', 'some_value', 'test'], 'some_value', + ], + 'directive don`t have duplicates without namespace' => [ + ['test.my_setting_1', 'test', null], 'test', + ], + ]; } - public function testGetWithNamespace() + /** + * @test + * @dataProvider setConfigDataProvider + */ + public function shouldSetConfigParams(array $test, $expected) { - $config = new Config(); - $config->get('unused-variable', true, 'warnings'); - $this->assertTrue($config->get('unused-variable', 'warnings')); - } + list($key, $value, $namespace) = $test; + $this->config->set($key, $value, $namespace); - public function testSetWithoutNamespace() - { - $config = new Config(); - $config->set('config', true); - $this->assertTrue($config->get('config')); + $actual = $this->config->get($key, $namespace); + + $this->assertSame($expected, $actual); } - public function testSetWithNamespace() + /** @test */ + public function shouldGetDefaultConfigParams() { - $config = new Config(); - $config->set('unused-variable', false, 'warnings'); - $this->assertFalse($config->get('unused-variable', 'warnings')); + $this->assertSame($this->config->get('test.my_setting_1', 'test'), null); } - /** - * Test saveOnExit method. - */ - public function testSaveOnExit() + /** @test */ + public function shouldSaveConfigOnExit() { chdir(sys_get_temp_dir()); $config = new Config(); @@ -96,27 +140,4 @@ public function testSaveOnExit() $this->assertSame($configJson['name'], 'foo'); $this->cleanTmpConfigFile(); } - - /** - * Clean config.json file into tmp dir. - */ - private function cleanTmpConfigFile() - { - /* clean config.json into tmp dir */ - $tmpConfigFile = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'config.json'; - - if (file_exists($tmpConfigFile)) { - unlink($tmpConfigFile); - } - } - - /** @test */ - public function directiveShowsWithoutDuplicatedNamespace() - { - $config = new Config(); - $config->set('ext.some_key', 'some_value', 'test'); - - $this->assertSame($config->get('ext.some_key', 'test'), 'some_value'); - $this->assertSame($config->get('test.my_setting_1', 'test'), null); - } } From a8d238516ab033e4c7c885c2df52f823122dd66e Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 01:28:00 +0200 Subject: [PATCH 33/47] Add data provider to config test --- Library/Config.php | 1 + unit-tests/Zephir/Test/ConfigTest.php | 54 +++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/Library/Config.php b/Library/Config.php index 1d5fe9a99d..6551bea4cc 100644 --- a/Library/Config.php +++ b/Library/Config.php @@ -34,6 +34,7 @@ class Config implements \ArrayAccess, \JsonSerializable 'stubs' => [ 'path' => 'ide/%version%/%namespace%/', 'stubs-run-after-generate' => false, + 'banner' => [], ], 'api' => [ 'path' => 'doc/%version%', diff --git a/unit-tests/Zephir/Test/ConfigTest.php b/unit-tests/Zephir/Test/ConfigTest.php index 849bd7c504..17a017474c 100644 --- a/unit-tests/Zephir/Test/ConfigTest.php +++ b/unit-tests/Zephir/Test/ConfigTest.php @@ -84,7 +84,7 @@ public function constructWithBadConfigFile() * * @return array */ - public function setConfigDataProvider(): array + public function setConfigProvider(): array { return [ 'set with namespace' => [ @@ -110,7 +110,7 @@ public function setConfigDataProvider(): array /** * @test - * @dataProvider setConfigDataProvider + * @dataProvider setConfigProvider */ public function shouldSetConfigParams(array $test, $expected) { @@ -122,22 +122,60 @@ public function shouldSetConfigParams(array $test, $expected) $this->assertSame($expected, $actual); } - /** @test */ - public function shouldGetDefaultConfigParams() + public function defaultConfigProvider(): array + { + $banner = [ + 'This file is part of the Zephir.', + '', + '(c) Zephir Team ', + '', + 'For the full copyright and license information, please view', + 'the LICENSE file that was distributed with this source code.', + ]; + + return [ + // 'test suite name' => [$namespace, $key, $expected,] + 'not existing param' => ['test.my_setting_1', 'test', null], + 'stubs path' => ['stubs', 'path', 'ide/%version%/%namespace%/'], + 'stubs run' => ['stubs', 'stubs-run-after-generate', false], + 'stubs banner' => ['stubs', 'banner', $banner], + 'api path' => ['api', 'path', 'doc/%version%'], + 'api path' => ['api', 'path', 'doc/%version%'], + 'warnings unused-variable' => ['warnings', 'unused-variable', true], + 'optimizations static-type-inference' => ['optimizations', 'static-type-inference', true], + 'extra indent' => ['extra', 'indent', 'spaces'], + 'namespace' => [null, 'namespace', 'test'], + 'name' => [null, 'name', 'Test Extension'], + 'author' => [null, 'author', 'Zephir Team and contributors'], + 'globals test_setting_1' => ['globals', 'test_setting_1', ['type' => 'bool', 'default' => true]], + 'globals db.my_setting_1' => ['globals', 'db.my_setting_1', ['type' => 'bool', 'default' => false]], + ]; + } + + /** + * @test + * @dataProvider defaultConfigProvider + */ + public function shouldGetDefaultConfigParams($namespace, string $key, $expected) { - $this->assertSame($this->config->get('test.my_setting_1', 'test'), null); + $actual = $this->config->get($key, $namespace); + + $this->assertSame($expected, $actual); } /** @test */ public function shouldSaveConfigOnExit() { chdir(sys_get_temp_dir()); - $config = new Config(); - $config->set('name', 'foo'); - $config->dumpToFile(); + + $this->config->set('name', 'foo'); + $this->config->dumpToFile(); + $configJson = json_decode(file_get_contents('config.json'), true); + $this->assertInternalType('array', $configJson); $this->assertSame($configJson['name'], 'foo'); + $this->cleanTmpConfigFile(); } } From 90a25aabfec8147732cc45b14439114e2a4e212d Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 11:57:14 +0200 Subject: [PATCH 34/47] Optimize compile logic internals --- Library/CompilerFile.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Library/CompilerFile.php b/Library/CompilerFile.php index 2a947bfdb7..3dbc5ebd4f 100644 --- a/Library/CompilerFile.php +++ b/Library/CompilerFile.php @@ -849,13 +849,6 @@ public function compile(Compiler $compiler, StringsManager $stringsManager) foreach ($this->ir as $topStatement) { switch ($topStatement['type']) { case 'class': - if ($interface || $class) { - throw new CompilerException('More than one class defined in the same file', $topStatement); - } - $class = true; - $this->compileClass($compilationContext, $this->namespace, $topStatement); - break; - case 'interface': if ($interface || $class) { throw new CompilerException('More than one class defined in the same file', $topStatement); @@ -867,6 +860,9 @@ public function compile(Compiler $compiler, StringsManager $stringsManager) case 'comment': $this->compileComment($compilationContext, $topStatement); break; + + default: + break; } } From c7f52eaf0a302d73fdbb564611e602b93abd8fbe Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 13:19:27 +0200 Subject: [PATCH 35/47] Add test for offsetGet --- unit-tests/Zephir/Test/ConfigTest.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/unit-tests/Zephir/Test/ConfigTest.php b/unit-tests/Zephir/Test/ConfigTest.php index 17a017474c..c587d72456 100644 --- a/unit-tests/Zephir/Test/ConfigTest.php +++ b/unit-tests/Zephir/Test/ConfigTest.php @@ -163,6 +163,33 @@ public function shouldGetDefaultConfigParams($namespace, string $key, $expected) $this->assertSame($expected, $actual); } + public function offsetConfigProvider(): array + { + return [ + // 'test suite name' => [$key, $expected,] + 'globals test_setting_1 with namespace' => [ + ['globals' => 'test_setting_1'], ['type' => 'bool', 'default' => true], + ], + 'globals test_setting_1 without namespace' => [ + ['globals' => 'test_setting_1'], ['type' => 'bool', 'default' => true], + ], + 'info header without namespace' => [ + ['requires' => 'extensions'], ['PDO', 'SPL', 'standard', 'hash', 'json'], + ], + ]; + } + + /** + * @test + * @dataProvider offsetConfigProvider + */ + public function shouldGetWithOffsetConfigParams(array $key, array $expected) + { + $actual = $this->config->offsetGet($key); + + $this->assertSame($expected, $actual); + } + /** @test */ public function shouldSaveConfigOnExit() { From f64d97bd5b22417e2bd482445220e7a9f42ba2cb Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 13:57:07 +0200 Subject: [PATCH 36/47] Add more tests --- unit-tests/Zephir/Test/ConfigTest.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/unit-tests/Zephir/Test/ConfigTest.php b/unit-tests/Zephir/Test/ConfigTest.php index c587d72456..2667658331 100644 --- a/unit-tests/Zephir/Test/ConfigTest.php +++ b/unit-tests/Zephir/Test/ConfigTest.php @@ -190,6 +190,27 @@ public function shouldGetWithOffsetConfigParams(array $key, array $expected) $this->assertSame($expected, $actual); } + /** @test */ + public function shouldUnsetConfigParams() + { + $initials = $this->config->get('test_setting_1', 'globals'); + + $this->assertTrue($this->config->offsetExists('globals')); + $this->assertSame( + [ + 'type' => 'bool', + 'default' => true, + ], + $initials + ); + + $this->config->offsetUnset('globals'); + $actual = $this->config->get('test_setting_1', 'globals'); + + $this->assertFalse($this->config->offsetExists('globals')); + $this->assertNull($actual); + } + /** @test */ public function shouldSaveConfigOnExit() { From c4f9549bc50a90678eb634e61cff723bad73c253 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 17:25:18 +0200 Subject: [PATCH 37/47] Change banner type, add tests --- Library/Compiler.php | 3 ++- Library/Config.php | 12 ++++++++- Library/Stubs/Generator.php | 9 ++++--- config.json | 9 +------ unit-tests/Zephir/Test/ConfigTest.php | 39 ++++++++++++++++++++------- 5 files changed, 49 insertions(+), 23 deletions(-) diff --git a/Library/Compiler.php b/Library/Compiler.php index bec5d0a080..6940e1deb8 100644 --- a/Library/Compiler.php +++ b/Library/Compiler.php @@ -973,7 +973,8 @@ public function stubs(bool $fromGenerate = false) $stubsGenerator->generate( $this->config->get('namespace'), $path, - $this->config->get('indent', 'extra') + $this->config->get('indent', 'extra'), + $this->config->getBanner() ); } diff --git a/Library/Config.php b/Library/Config.php index 6551bea4cc..825d2f7cbc 100644 --- a/Library/Config.php +++ b/Library/Config.php @@ -34,7 +34,7 @@ class Config implements \ArrayAccess, \JsonSerializable 'stubs' => [ 'path' => 'ide/%version%/%namespace%/', 'stubs-run-after-generate' => false, - 'banner' => [], + 'banner' => '', ], 'api' => [ 'path' => 'doc/%version%', @@ -329,6 +329,16 @@ public function jsonSerialize() return $this->container; } + /** + * Returns banner from configuration file. + * + * @return string + */ + public function getBanner(): string + { + return $this->get('banner', 'stubs') ?? ''; + } + /** * Populate project configuration. * diff --git a/Library/Stubs/Generator.php b/Library/Stubs/Generator.php index 8eb7cce982..144ce2d283 100644 --- a/Library/Stubs/Generator.php +++ b/Library/Stubs/Generator.php @@ -54,10 +54,11 @@ public function __construct(array $files) * @param string $namespace * @param string $path * @param string $indent + * @param string $banner * * @throws Exception\LogicException */ - public function generate(string $namespace, string $path, string $indent) + public function generate(string $namespace, string $path, string $indent, string $banner) { if (empty($path)) { throw new Exception\LogicException( @@ -69,7 +70,7 @@ public function generate(string $namespace, string $path, string $indent) foreach ($this->files as $file) { $class = $file->getClassDefinition(); - $source = $this->buildClass($class, $indent); + $source = $this->buildClass($class, $indent, $banner); $filename = ucfirst($class->getName()).'.zep.php'; $filePath = $path.str_ireplace( @@ -94,14 +95,16 @@ public function generate(string $namespace, string $path, string $indent) * * @param ClassDefinition $class * @param string $indent + * @param string $banner * * @throws Exception\RuntimeException * * @return string */ - protected function buildClass(ClassDefinition $class, string $indent): string + protected function buildClass(ClassDefinition $class, string $indent, string $banner): string { $source = 'getNamespace()};".PHP_EOL; /** @var Zephir\AliasManager $aliasManager */ diff --git a/config.json b/config.json index ee90759135..fbdf745117 100644 --- a/config.json +++ b/config.json @@ -19,14 +19,7 @@ "stubs": { "path": "ide\/%version%\/%namespace%\/", "stubs-run-after-generate": false, - "banner": [ - "This file is part of the Zephir.", - "", - "(c) Zephir Team ", - "", - "For the full copyright and license information, please view", - "the LICENSE file that was distributed with this source code." - ] + "banner": "/**\n * This file is part of the Zephir.\n *\n * (c) Zephir Team \n *\n * For the full copyright and license information, please view\n * the LICENSE file that was distributed with this source code.\n */" }, "api": { diff --git a/unit-tests/Zephir/Test/ConfigTest.php b/unit-tests/Zephir/Test/ConfigTest.php index 2667658331..76b87dbb12 100644 --- a/unit-tests/Zephir/Test/ConfigTest.php +++ b/unit-tests/Zephir/Test/ConfigTest.php @@ -58,6 +58,25 @@ private function cleanTmpConfigFile() } } + /** + * Returns Stubs banner for test suite. + * + * @return string + */ + private function stubsBanner(): string + { + return << + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ +DOC; + } + /** * Test when we have a bad config.json file. * @@ -124,21 +143,12 @@ public function shouldSetConfigParams(array $test, $expected) public function defaultConfigProvider(): array { - $banner = [ - 'This file is part of the Zephir.', - '', - '(c) Zephir Team ', - '', - 'For the full copyright and license information, please view', - 'the LICENSE file that was distributed with this source code.', - ]; - return [ // 'test suite name' => [$namespace, $key, $expected,] 'not existing param' => ['test.my_setting_1', 'test', null], 'stubs path' => ['stubs', 'path', 'ide/%version%/%namespace%/'], 'stubs run' => ['stubs', 'stubs-run-after-generate', false], - 'stubs banner' => ['stubs', 'banner', $banner], + 'stubs banner' => ['stubs', 'banner', $this->stubsBanner()], 'api path' => ['api', 'path', 'doc/%version%'], 'api path' => ['api', 'path', 'doc/%version%'], 'warnings unused-variable' => ['warnings', 'unused-variable', true], @@ -211,6 +221,15 @@ public function shouldUnsetConfigParams() $this->assertNull($actual); } + /** @test */ + public function shouldGetBannerFromConfig() + { + $this->assertSame($this->stubsBanner(), $this->config->getBanner()); + + $this->config->offsetUnset('stubs'); + $this->assertSame('', $this->config->getBanner()); + } + /** @test */ public function shouldSaveConfigOnExit() { From 74fdd4cdfe12354c9714b6185cfb60f09dd53d7d Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Thu, 14 Nov 2019 17:39:35 +0200 Subject: [PATCH 38/47] Update Changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bdc623ba0..8c68e606fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ## [0.12.11] - 2019-11-02 +### Added +- Option to set banner for stubs [#1987](https://github.com/phalcon/zephir/1987) + ### Fixed - Fixed arithmetical operations with `zvals` which stores `double` numbers - Fixed updating static variables in the loop which represents `double` and From 900bd5f76696805c0a161bed5b0a4626b16258d6 Mon Sep 17 00:00:00 2001 From: Dreamszhu Date: Fri, 15 Nov 2019 07:21:50 +0800 Subject: [PATCH 39/47] Fix bug #2005 --- Library/Backends/ZendEngine3/FcallManager.php | 2 - ext/kernel/fcall.h | 67 ++++++++++--------- kernels/ZendEngine3/fcall.h | 67 ++++++++++--------- 3 files changed, 74 insertions(+), 62 deletions(-) diff --git a/Library/Backends/ZendEngine3/FcallManager.php b/Library/Backends/ZendEngine3/FcallManager.php index b3e106707c..b82974f256 100644 --- a/Library/Backends/ZendEngine3/FcallManager.php +++ b/Library/Backends/ZendEngine3/FcallManager.php @@ -114,7 +114,6 @@ public function genFcallCode() } $codePrinter->output('ZEPHIR_BACKUP_SCOPE(); \\'); - $codePrinter->output('ZEPHIR_BACKUP_THIS_PTR(); \\'); if (!$scope) { $codePrinter->output('ZEPHIR_SET_THIS(object); \\'); $codePrinter->output('ZEPHIR_SET_SCOPE((Z_OBJ_P(object) ? Z_OBJCE_P(object) : NULL), (Z_OBJ_P(object) ? Z_OBJCE_P(object) : NULL)); \\'); @@ -156,7 +155,6 @@ public function genFcallCode() } $codePrinter->output('ZEPHIR_LAST_CALL_STATUS = EG(exception) ? FAILURE : SUCCESS; \\'); - $codePrinter->output('ZEPHIR_RESTORE_THIS_PTR(); \\'); $codePrinter->output('ZEPHIR_RESTORE_SCOPE(); \\'); $codePrinter->decreaseLevel(); diff --git a/ext/kernel/fcall.h b/ext/kernel/fcall.h index ad466638f3..cfb506711e 100644 --- a/ext/kernel/fcall.h +++ b/ext/kernel/fcall.h @@ -58,22 +58,6 @@ typedef enum _zephir_call_type { ZEPHIR_LAST_CALL_STATUS = zephir_call_zval_func_aparams(return_value_ptr, func_name, cache, cache_slot, ZEPHIR_CALL_NUM_PARAMS(params_), ZEPHIR_PASS_CALL_PARAMS(params_)); \ } while (0) -/* Saves the if pointer, and called/calling scope */ -#define ZEPHIR_BACKUP_THIS_PTR() \ - zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; \ - zend_class_entry *old_ce = Z_TYPE(EG(current_execute_data)->This) != IS_OBJECT ? Z_CE(EG(current_execute_data)->This) : NULL; - -#define ZEPHIR_RESTORE_THIS_PTR() do { \ - if (old_this_ptr) { \ - ZEPHIR_SET_THIS_OBJ(old_this_ptr); \ - } else if (old_ce) { \ - ZVAL_UNDEF(&EG(current_execute_data)->This); \ - Z_CE(EG(current_execute_data)->This) = old_ce; \ - } else { \ - ZEPHIR_SET_THIS_EXPLICIT_NULL(); \ - } \ -} while (0) - #define ZEPHIR_SET_THIS(zv) ZEPHIR_SET_THIS_OBJ((zv ? Z_OBJ_P(zv) : NULL)) #define ZEPHIR_SET_THIS_EXPLICIT_NULL() \ ZVAL_NULL(&EG(current_execute_data)->This); \ @@ -81,23 +65,33 @@ typedef enum _zephir_call_type { #define ZEPHIR_SET_THIS_OBJ(obj) \ if (obj) { \ - if (old_this_ptr) { \ - Z_OBJ_P(&EG(current_execute_data)->This) = obj; \ - } else { \ - ZVAL_OBJ(&EG(current_execute_data)->This, obj); \ - } \ + ZVAL_OBJ(&EG(current_execute_data)->This, obj); \ } \ else { ZEPHIR_SET_THIS_EXPLICIT_NULL(); } \ + #if PHP_VERSION_ID >= 70100 #define ZEPHIR_BACKUP_SCOPE() \ zend_class_entry *old_scope = EG(fake_scope); \ - zend_class_entry *old_called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_execute_data *old_call = execute_data; \ + zend_execute_data *old_execute_data = EG(current_execute_data), new_execute_data; \ + if (!EG(current_execute_data)) { \ + memset(&new_execute_data, 0, sizeof(zend_execute_data)); \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } else { \ + new_execute_data = *EG(current_execute_data); \ + new_execute_data.prev_execute_data = EG(current_execute_data); \ + new_execute_data.call = NULL; \ + new_execute_data.opline = NULL; \ + new_execute_data.func = NULL; \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } #define ZEPHIR_RESTORE_SCOPE() \ - zephir_set_called_scope(EG(current_execute_data), old_called_scope); \ EG(fake_scope) = old_scope; \ + execute_data = old_call; \ + EG(current_execute_data) = old_execute_data; #define ZEPHIR_SET_SCOPE(_scope, _scope_called) \ EG(fake_scope) = _scope; \ @@ -107,15 +101,28 @@ typedef enum _zephir_call_type { #define ZEPHIR_BACKUP_SCOPE() \ zend_class_entry *old_scope = EG(scope); \ - zend_class_entry *old_called_scope = EG(current_execute_data)->called_scope; + zend_execute_data *old_call = execute_data; \ + zend_execute_data *old_execute_data = EG(current_execute_data), new_execute_data; \ + if (!EG(current_execute_data)) { \ + memset(&new_execute_data, 0, sizeof(zend_execute_data)); \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } else { \ + new_execute_data = *EG(current_execute_data); \ + new_execute_data.prev_execute_data = EG(current_execute_data); \ + new_execute_data.call = NULL; \ + new_execute_data.opline = NULL; \ + new_execute_data.func = NULL; \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } #define ZEPHIR_RESTORE_SCOPE() \ - EG(current_execute_data)->called_scope = old_called_scope; \ EG(scope) = old_scope; \ + execute_data = old_call; \ + EG(current_execute_data) = old_execute_data; #define ZEPHIR_SET_SCOPE(_scope, _scope_called) \ EG(scope) = _scope; \ - EG(current_execute_data)->called_scope = _scope_called; \ + EG(current_execute_data)->called_scope = _scope_called; #endif @@ -445,13 +452,13 @@ static inline void zephir_set_called_scope(zend_execute_data *ex, zend_class_ent if (Z_TYPE(ex->This) == IS_OBJECT) { Z_OBJCE(ex->This) = called_scope; return; - } else if (Z_CE(ex->This)) { - Z_CE(ex->This) = called_scope; - return; - } else if (ex->func) { + }else if (ex->func) { if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) { return; } + } else { + Z_CE(ex->This) = called_scope; + return; } ex = ex->prev_execute_data; } diff --git a/kernels/ZendEngine3/fcall.h b/kernels/ZendEngine3/fcall.h index ad466638f3..cfb506711e 100644 --- a/kernels/ZendEngine3/fcall.h +++ b/kernels/ZendEngine3/fcall.h @@ -58,22 +58,6 @@ typedef enum _zephir_call_type { ZEPHIR_LAST_CALL_STATUS = zephir_call_zval_func_aparams(return_value_ptr, func_name, cache, cache_slot, ZEPHIR_CALL_NUM_PARAMS(params_), ZEPHIR_PASS_CALL_PARAMS(params_)); \ } while (0) -/* Saves the if pointer, and called/calling scope */ -#define ZEPHIR_BACKUP_THIS_PTR() \ - zend_object *old_this_ptr = Z_TYPE(EG(current_execute_data)->This) == IS_OBJECT ? Z_OBJ(EG(current_execute_data)->This) : NULL; \ - zend_class_entry *old_ce = Z_TYPE(EG(current_execute_data)->This) != IS_OBJECT ? Z_CE(EG(current_execute_data)->This) : NULL; - -#define ZEPHIR_RESTORE_THIS_PTR() do { \ - if (old_this_ptr) { \ - ZEPHIR_SET_THIS_OBJ(old_this_ptr); \ - } else if (old_ce) { \ - ZVAL_UNDEF(&EG(current_execute_data)->This); \ - Z_CE(EG(current_execute_data)->This) = old_ce; \ - } else { \ - ZEPHIR_SET_THIS_EXPLICIT_NULL(); \ - } \ -} while (0) - #define ZEPHIR_SET_THIS(zv) ZEPHIR_SET_THIS_OBJ((zv ? Z_OBJ_P(zv) : NULL)) #define ZEPHIR_SET_THIS_EXPLICIT_NULL() \ ZVAL_NULL(&EG(current_execute_data)->This); \ @@ -81,23 +65,33 @@ typedef enum _zephir_call_type { #define ZEPHIR_SET_THIS_OBJ(obj) \ if (obj) { \ - if (old_this_ptr) { \ - Z_OBJ_P(&EG(current_execute_data)->This) = obj; \ - } else { \ - ZVAL_OBJ(&EG(current_execute_data)->This, obj); \ - } \ + ZVAL_OBJ(&EG(current_execute_data)->This, obj); \ } \ else { ZEPHIR_SET_THIS_EXPLICIT_NULL(); } \ + #if PHP_VERSION_ID >= 70100 #define ZEPHIR_BACKUP_SCOPE() \ zend_class_entry *old_scope = EG(fake_scope); \ - zend_class_entry *old_called_scope = zend_get_called_scope(EG(current_execute_data)); + zend_execute_data *old_call = execute_data; \ + zend_execute_data *old_execute_data = EG(current_execute_data), new_execute_data; \ + if (!EG(current_execute_data)) { \ + memset(&new_execute_data, 0, sizeof(zend_execute_data)); \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } else { \ + new_execute_data = *EG(current_execute_data); \ + new_execute_data.prev_execute_data = EG(current_execute_data); \ + new_execute_data.call = NULL; \ + new_execute_data.opline = NULL; \ + new_execute_data.func = NULL; \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } #define ZEPHIR_RESTORE_SCOPE() \ - zephir_set_called_scope(EG(current_execute_data), old_called_scope); \ EG(fake_scope) = old_scope; \ + execute_data = old_call; \ + EG(current_execute_data) = old_execute_data; #define ZEPHIR_SET_SCOPE(_scope, _scope_called) \ EG(fake_scope) = _scope; \ @@ -107,15 +101,28 @@ typedef enum _zephir_call_type { #define ZEPHIR_BACKUP_SCOPE() \ zend_class_entry *old_scope = EG(scope); \ - zend_class_entry *old_called_scope = EG(current_execute_data)->called_scope; + zend_execute_data *old_call = execute_data; \ + zend_execute_data *old_execute_data = EG(current_execute_data), new_execute_data; \ + if (!EG(current_execute_data)) { \ + memset(&new_execute_data, 0, sizeof(zend_execute_data)); \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } else { \ + new_execute_data = *EG(current_execute_data); \ + new_execute_data.prev_execute_data = EG(current_execute_data); \ + new_execute_data.call = NULL; \ + new_execute_data.opline = NULL; \ + new_execute_data.func = NULL; \ + execute_data = EG(current_execute_data) = &new_execute_data; \ + } #define ZEPHIR_RESTORE_SCOPE() \ - EG(current_execute_data)->called_scope = old_called_scope; \ EG(scope) = old_scope; \ + execute_data = old_call; \ + EG(current_execute_data) = old_execute_data; #define ZEPHIR_SET_SCOPE(_scope, _scope_called) \ EG(scope) = _scope; \ - EG(current_execute_data)->called_scope = _scope_called; \ + EG(current_execute_data)->called_scope = _scope_called; #endif @@ -445,13 +452,13 @@ static inline void zephir_set_called_scope(zend_execute_data *ex, zend_class_ent if (Z_TYPE(ex->This) == IS_OBJECT) { Z_OBJCE(ex->This) = called_scope; return; - } else if (Z_CE(ex->This)) { - Z_CE(ex->This) = called_scope; - return; - } else if (ex->func) { + }else if (ex->func) { if (ex->func->type != ZEND_INTERNAL_FUNCTION || ex->func->common.scope) { return; } + } else { + Z_CE(ex->This) = called_scope; + return; } ex = ex->prev_execute_data; } From fc4e2d7a21f7e794e69440ed655db16bf9d1f3a0 Mon Sep 17 00:00:00 2001 From: Ruud Boon Date: Mon, 18 Nov 2019 12:05:39 +0100 Subject: [PATCH 40/47] Update CHANGELOG.md Banner isn't released yet. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c68e606fd..62a50621be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] -## [0.12.11] - 2019-11-02 ### Added - Option to set banner for stubs [#1987](https://github.com/phalcon/zephir/1987) +## [0.12.11] - 2019-11-02 ### Fixed - Fixed arithmetical operations with `zvals` which stores `double` numbers - Fixed updating static variables in the loop which represents `double` and From 1ff76dd432df67977ff370a94686f211160ebb22 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 19 Nov 2019 20:18:01 +0200 Subject: [PATCH 41/47] Fix Interface Aliases for stubs generation --- Library/AliasManager.php | 5 +- unit-tests/Zephir/Test/AliasManagerTest.php | 73 ++++++++++++++++----- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/Library/AliasManager.php b/Library/AliasManager.php index bef31e3aa8..03b37926f5 100644 --- a/Library/AliasManager.php +++ b/Library/AliasManager.php @@ -113,7 +113,10 @@ public function isAliasPresentFor(string $className): bool { $extractAlias = $this->implicitAlias($className); - return !isset($this->aliases[$extractAlias]); + $isClassDeclarated = \in_array($className, $this->aliases); + $classAlias = array_flip($this->aliases)[$className] ?? null; + + return $isClassDeclarated && $classAlias !== $extractAlias; } /** diff --git a/unit-tests/Zephir/Test/AliasManagerTest.php b/unit-tests/Zephir/Test/AliasManagerTest.php index c72b0aa7be..28007d8244 100644 --- a/unit-tests/Zephir/Test/AliasManagerTest.php +++ b/unit-tests/Zephir/Test/AliasManagerTest.php @@ -54,16 +54,21 @@ public function baseTestSuiteProvider(): array ], 'without explicit alias' => [ [ - 'name' => 'Throwable', - 'alias' => 'Throwable', + 'name' => '\\Throwable', + ], + ], + 'without explicit alias and FQN' => [ + [ + 'name' => 'Zephir\\Compiler\\CompilerInterface', ], ], ]; } - public function aliasDataProvider(): array + public function aliasProvider(): array { $expected = [ + // [ alias => name ] [ 'EventsManagerInterface' => 'Bug\\Events\\ManagerInterface', ], @@ -71,7 +76,10 @@ public function aliasDataProvider(): array 'EventsManagerInterface' => '\\Bug\\Events\\ManagerInterface', ], [ - 'Throwable' => 'Throwable', + 'Throwable' => '\Throwable', + ], + [ + 'CompilerInterface' => 'Zephir\\Compiler\\CompilerInterface', ], ]; @@ -80,7 +88,7 @@ public function aliasDataProvider(): array /** * @test - * @dataProvider aliasDataProvider + * @dataProvider aliasProvider */ public function shouldProperAddStatements(array $useStatements, array $expected) { @@ -88,18 +96,20 @@ public function shouldProperAddStatements(array $useStatements, array $expected) 'aliases' => [$useStatements], ]); - $alias = $useStatements['alias']; $className = $useStatements['name']; + $parts = explode('\\', $className); + $alias = $useStatements['alias'] ?? $parts[\count($parts) - 1]; + $this->assertTrue($this->testAliasMgr->isAlias($alias)); - $this->assertSame($this->testAliasMgr->getAliases(), $expected); - $this->assertSame($this->testAliasMgr->getAlias($alias), $className); + $this->assertSame($expected, $this->testAliasMgr->getAliases()); + $this->assertSame($className, $this->testAliasMgr->getAlias($alias)); } - public function statementDataProvider(): array + public function statementProvider(): array { $expected = [ - true, true, false, + true, true, false, false, ]; return $this->injectExpectedResult($expected); @@ -107,7 +117,7 @@ public function statementDataProvider(): array /** * @test - * @dataProvider statementDataProvider + * @dataProvider statementProvider */ public function shouldCheckAliasedStatement(array $useStatements, bool $expected) { @@ -115,11 +125,11 @@ public function shouldCheckAliasedStatement(array $useStatements, bool $expected 'aliases' => [$useStatements], ]); - $alias = $useStatements['alias']; $className = $useStatements['name']; + $alias = $useStatements['alias'] ?? trim($className, '\\'); - $this->assertSame($this->testAliasMgr->isUseStatementAliased($alias), $expected); - $this->assertSame($this->testAliasMgr->isAliasPresentFor($className), $expected); + $this->assertSame($expected, $this->testAliasMgr->isUseStatementAliased($alias)); + $this->assertSame($expected, $this->testAliasMgr->isAliasPresentFor($className)); } public function classNameDataProvider(): array @@ -127,7 +137,8 @@ public function classNameDataProvider(): array $expected = [ 'EventsManagerInterface', '\Bug\Events\ManagerInterface', - 'Throwable', + '\Throwable', + 'CompilerInterface', ]; return $this->injectExpectedResult($expected); @@ -145,6 +156,36 @@ public function shouldGetAliasForClassName(array $useStatements, string $expecte $className = $useStatements['name']; - $this->assertSame($this->testAliasMgr->getAliasForClassName($className), $expected); + $this->assertSame($expected, $this->testAliasMgr->getAliasForClassName($className)); + } + + /** @test */ + public function shouldCheckIfAliasPresentForClass() + { + $this->testAliasMgr->add([ + 'aliases' => [ + [ + 'name' => 'One', + 'alias' => 'One', + ], + [ + 'name' => 'Bug\\Events\\ManagerInterface', + 'alias' => 'EventsManagerInterface', + ], + [ + 'name' => '\\Root\SomeNamespace\\SomeClassName', + 'alias' => 'SomeClassName', + ], + [ + 'name' => 'AnotherClass', + 'alias' => 'AnotherClass', + ], + ], + ]); + + $this->assertTrue($this->testAliasMgr->isAliasPresentFor('Bug\\Events\\ManagerInterface')); + $this->assertFalse($this->testAliasMgr->isAliasPresentFor('\\Root\SomeNamespace\\SomeClassName')); + $this->assertFalse($this->testAliasMgr->isAliasPresentFor('AnotherClass')); + $this->assertFalse($this->testAliasMgr->isAliasPresentFor('NonExistingClass')); } } From 744793fb8e43b9e3de6e9f141361f66b47882548 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 19 Nov 2019 23:09:05 +0200 Subject: [PATCH 42/47] Change test internals --- unit-tests/Zephir/Test/AliasManagerTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unit-tests/Zephir/Test/AliasManagerTest.php b/unit-tests/Zephir/Test/AliasManagerTest.php index 28007d8244..c7fd256614 100644 --- a/unit-tests/Zephir/Test/AliasManagerTest.php +++ b/unit-tests/Zephir/Test/AliasManagerTest.php @@ -126,7 +126,9 @@ public function shouldCheckAliasedStatement(array $useStatements, bool $expected ]); $className = $useStatements['name']; - $alias = $useStatements['alias'] ?? trim($className, '\\'); + + $parts = explode('\\', $className); + $alias = $useStatements['alias'] ?? $parts[\count($parts) - 1]; $this->assertSame($expected, $this->testAliasMgr->isUseStatementAliased($alias)); $this->assertSame($expected, $this->testAliasMgr->isAliasPresentFor($className)); From 6ae6ff466abb6b597807d0701a9c240d9e260298 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 19 Nov 2019 23:12:22 +0200 Subject: [PATCH 43/47] Add test case for class without alias --- unit-tests/Zephir/Test/AliasManagerTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unit-tests/Zephir/Test/AliasManagerTest.php b/unit-tests/Zephir/Test/AliasManagerTest.php index c7fd256614..1bb35f025a 100644 --- a/unit-tests/Zephir/Test/AliasManagerTest.php +++ b/unit-tests/Zephir/Test/AliasManagerTest.php @@ -182,6 +182,9 @@ public function shouldCheckIfAliasPresentForClass() 'name' => 'AnotherClass', 'alias' => 'AnotherClass', ], + [ + 'name' => 'Bug\\Storage\\FileSystem', + ], ], ]); @@ -189,5 +192,6 @@ public function shouldCheckIfAliasPresentForClass() $this->assertFalse($this->testAliasMgr->isAliasPresentFor('\\Root\SomeNamespace\\SomeClassName')); $this->assertFalse($this->testAliasMgr->isAliasPresentFor('AnotherClass')); $this->assertFalse($this->testAliasMgr->isAliasPresentFor('NonExistingClass')); + $this->assertFalse($this->testAliasMgr->isAliasPresentFor('Bug\\Storage\\FileSystem')); } } From 34c96533e85bc4c3e5254261146b5b2645e3440c Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Tue, 19 Nov 2019 23:23:56 +0200 Subject: [PATCH 44/47] Update cahngelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62a50621be..b047a62d2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added - Option to set banner for stubs [#1987](https://github.com/phalcon/zephir/1987) +- Interface stubs have relative namespace [#2016](https://github.com/phalcon/zephir/2016) ## [0.12.11] - 2019-11-02 ### Fixed From 4117e8bffc383c619537e26cc7dc87ed2965c8e4 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Wed, 20 Nov 2019 02:17:17 +0200 Subject: [PATCH 45/47] Correct chage log [ci skip] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b047a62d2c..53a3b3fe75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added - Option to set banner for stubs [#1987](https://github.com/phalcon/zephir/1987) -- Interface stubs have relative namespace [#2016](https://github.com/phalcon/zephir/2016) ## [0.12.11] - 2019-11-02 ### Fixed @@ -22,6 +21,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#1986](https://github.com/phalcon/zephir/issues/1986) - Fixed incorrect namespace on type hinted return when generating API docs [#1229](https://github.com/phalcon/zephir/issues/1229) +- Fixed incorrect stubs generation for classes in the same namespace + [#2016](https://github.com/phalcon/zephir/issues/2016) ## [0.12.10] - 2019-10-19 ### Fixed From 420e5e55f1da251b56bc5291b17757a21ad7f615 Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 24 Nov 2019 22:42:36 +0200 Subject: [PATCH 46/47] Update change log --- CHANGELOG.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a3b3fe75..0c9166ebad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,23 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ### Added -- Option to set banner for stubs [#1987](https://github.com/phalcon/zephir/1987) +- Option to set banner for stubs generator + [#1987](https://github.com/phalcon/zephir/1987) + +### Fixed +- Calling object methods from static context yields segmentation fault when + `internal-call-transformation` is set to `TRUE` + [#2000](https://github.com/phalcon/zephir/issues/2000) +- Certain method calls fail when called from static context when + `internal-call-transformation` is set to `TRUE` + [#2005](https://github.com/phalcon/zephir/issues/2005) +- Method context loses track of `this` after calling static method when + `internal-call-transformation` is set to `TRUE` + [#2007](https://github.com/phalcon/zephir/issues/2007) +- Fixed incorrect stubs generation for return type hint + [#1990](https://github.com/phalcon/zephir/issues/1990) +- Fixed incorrect stubs generation for classes in the same namespace + [#2016](https://github.com/phalcon/zephir/issues/2016) ## [0.12.11] - 2019-11-02 ### Fixed @@ -21,8 +37,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#1986](https://github.com/phalcon/zephir/issues/1986) - Fixed incorrect namespace on type hinted return when generating API docs [#1229](https://github.com/phalcon/zephir/issues/1229) -- Fixed incorrect stubs generation for classes in the same namespace - [#2016](https://github.com/phalcon/zephir/issues/2016) ## [0.12.10] - 2019-10-19 ### Fixed From c8bf7e18dd4c376ff26474ade958f6f2deedf7bb Mon Sep 17 00:00:00 2001 From: Serghei Iakovlev Date: Sun, 24 Nov 2019 23:42:52 +0200 Subject: [PATCH 47/47] Update change log --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c9166ebad..092cfbde77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [0.12.12] - 2019-11-25 ### Added - Option to set banner for stubs generator [#1987](https://github.com/phalcon/zephir/1987) @@ -338,7 +339,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed casting resource to int (only ZendEngine 3) [#1524](https://github.com/phalcon/zephir/issues/1524) -[Unreleased]: https://github.com/phalcon/zephir/compare/0.12.11...HEAD +[Unreleased]: https://github.com/phalcon/zephir/compare/0.12.12...HEAD +[0.12.12]: https://github.com/phalcon/zephir/compare/0.12.11...0.12.12 [0.12.11]: https://github.com/phalcon/zephir/compare/0.12.10...0.12.11 [0.12.10]: https://github.com/phalcon/zephir/compare/0.12.9...0.12.10 [0.12.9]: https://github.com/phalcon/zephir/compare/0.12.8...0.12.9