diff --git a/CHANGELOG.md b/CHANGELOG.md index d43b4c719f..f720297dfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org). ## [Unreleased] +## [0.16.1] - 2022-08-21 +### Changed +- Changed usage of `utf8_decode()` function in favour of `mb_convert_encoding()` [#2376](https://github.com/zephir-lang/zephir/issues/2376) + +### Fixed +- Fixed generation of `ARG_INFO` for nullable object (`?object`) [#2374](https://github.com/zephir-lang/zephir/issues/2374) + ## [0.16.0] - 2022-03-20 ### Added - Added custom list of arg info definition (Phalcon only) [#2341](https://github.com/zephir-lang/zephir/issues/2341) @@ -573,6 +580,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). [Unreleased]: https://github.com/zephir-lang/zephir/compare/0.16.0...HEAD +[0.16.1]: https://github.com/zephir-lang/zephir/compare/0.16.0...0.16.1 [0.16.0]: https://github.com/zephir-lang/zephir/compare/0.15.2...0.16.0 [0.15.2]: https://github.com/zephir-lang/zephir/compare/0.15.1...0.15.2 [0.15.1]: https://github.com/zephir-lang/zephir/compare/0.15.0...0.15.1 diff --git a/Library/ArgInfoDefinition.php b/Library/ArgInfoDefinition.php index 43889ca010..8e55bc213d 100644 --- a/Library/ArgInfoDefinition.php +++ b/Library/ArgInfoDefinition.php @@ -167,13 +167,9 @@ public function render(): void private function richRenderStart(): void { - if (array_key_exists('object', $this->functionLike->getReturnTypes())) { - $class = 'NULL'; - - if (1 === count($this->functionLike->getReturnClassTypes())) { - $class = key($this->functionLike->getReturnClassTypes()); - $class = escape_class($this->compilationContext->getFullName($class)); - } + if (array_key_exists('object', $this->functionLike->getReturnTypes()) && 1 === count($this->functionLike->getReturnClassTypes())) { + $class = key($this->functionLike->getReturnClassTypes()); + $class = escape_class($this->compilationContext->getFullName($class)); $this->codePrinter->output( sprintf( @@ -237,6 +233,33 @@ private function richRenderStart(): void return; } + if ($this->functionLike->isReturnTypeNullableObject()) { + $this->codePrinter->output('#if PHP_VERSION_ID >= 80000'); + $this->codePrinter->output( + sprintf( + 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(%s, %d, %d, %s)', + $this->name, + (int) $this->returnByRef, + $this->functionLike->getNumberOfRequiredParameters(), + 'MAY_BE_NULL|MAY_BE_OBJECT', + ) + ); + $this->codePrinter->output('#else'); + $this->codePrinter->output( + sprintf( + 'ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(%s, %d, %d, %s, %d)', + $this->name, + (int) $this->returnByRef, + $this->functionLike->getNumberOfRequiredParameters(), + 'IS_OBJECT', + 1, + ) + ); + $this->codePrinter->output('#endif'); + + return; + } + if (count($this->functionLike->getReturnTypes()) > 1) { $types = []; $mayBeTypes = $this->functionLike->getMayBeArgTypes(); @@ -332,23 +355,19 @@ private function renderEnd(): void case '0:variable': case '1:variable': if (isset($parameter['cast'])) { - switch ($parameter['cast']['type']) { - case 'variable': - $value = $parameter['cast']['value']; - $this->codePrinter->output( - sprintf( - "\tZEND_ARG_OBJ_INFO(%d, %s, %s, %d)", - $this->passByReference($parameter), - $parameter['name'], - escape_class($this->compilationContext->getFullName($value)), - (int) $this->allowNull($parameter) - ) - ); - break; - - default: - throw new Exception('Unexpected exception'); + if ($parameter['cast']['type'] !== 'variable') { + throw new Exception('Unexpected exception'); } + + $this->codePrinter->output( + sprintf( + "\tZEND_ARG_OBJ_INFO(%d, %s, %s, %d)", + $this->passByReference($parameter), + $parameter['name'], + escape_class($this->compilationContext->getFullName($parameter['cast']['value'])), + (int) $this->allowNull($parameter) + ) + ); } else { $this->codePrinter->output( sprintf( @@ -450,7 +469,7 @@ private function allowNull(array $parameter): bool return false; } - if ('null' == $parameter['default']['type']) { + if ('null' === $parameter['default']['type']) { return true; } diff --git a/Library/Backends/ZendEngine3/Backend.php b/Library/Backends/ZendEngine3/Backend.php index 915f562d1e..61c53b8cea 100644 --- a/Library/Backends/ZendEngine3/Backend.php +++ b/Library/Backends/ZendEngine3/Backend.php @@ -27,9 +27,6 @@ use function Zephir\add_slashes; -/** - * Zephir\Backends\ZendEngine3\Backend. - */ class Backend extends BackendZendEngine2 { protected $name = 'ZendEngine3'; @@ -1001,12 +998,7 @@ public function copyOnWrite(Variable $target, $var, CompilationContext $context) public function forStatement(Variable $exprVariable, $keyVariable, $variable, $duplicateKey, $duplicateHash, $statement, $statementBlock, CompilationContext $compilationContext) { - /* - * Create a hash table and hash pointer temporary variables. - */ - //$arrayPointer = $compilationContext->symbolTable->addTemp('HashPosition', $compilationContext); - //$arrayHash = $compilationContext->symbolTable->addTemp('HashTable', $compilationContext); - /* + /** * Create a temporary zval to fetch the items from the hash. */ $compilationContext->headersManager->add('kernel/fcall'); diff --git a/Library/Backends/ZendEngine3/FcallManager.php b/Library/Backends/ZendEngine3/FcallManager.php index 995ad528ac..5647c3f2d5 100644 --- a/Library/Backends/ZendEngine3/FcallManager.php +++ b/Library/Backends/ZendEngine3/FcallManager.php @@ -15,14 +15,11 @@ use Zephir\Fcall\FcallManagerInterface; use function Zephir\file_put_contents_ex; -/** - * Zephir\Backends\ZendEngine3\FcallManager. - */ class FcallManager implements FcallManagerInterface { - protected $requiredMacros = []; + protected array $requiredMacros = []; - public function macroIsRequired($macro) + public function macroIsRequired($macro): bool { return isset($this->requiredMacros[$macro]); } diff --git a/Library/Builder/Statements/LetStatementBuilder.php b/Library/Builder/Statements/LetStatementBuilder.php index 9be3905c97..faf656e47b 100644 --- a/Library/Builder/Statements/LetStatementBuilder.php +++ b/Library/Builder/Statements/LetStatementBuilder.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Builder\Statements; /** diff --git a/Library/Cache/ClassEntryCache.php b/Library/Cache/ClassEntryCache.php index aeefd0e694..b652400c4a 100644 --- a/Library/Cache/ClassEntryCache.php +++ b/Library/Cache/ClassEntryCache.php @@ -9,18 +9,19 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Cache; use Zephir\CompilationContext; +use Zephir\Variable; /** - * ClassEntryCache. - * * Classes located in the PHP userland are cached to avoid further relocates */ class ClassEntryCache { - protected $cache = []; + protected array $cache = []; /** * Retrieves/Creates a class entry cache. @@ -29,11 +30,11 @@ class ClassEntryCache * @param bool $dynamic * @param CompilationContext $compilationContext * - * @return \Zephir\Variable + * @return Variable */ - public function get($className, $dynamic, CompilationContext $compilationContext) + public function get(string $className, bool $dynamic, CompilationContext $compilationContext): Variable { - /* + /** * Creates a guard variable if the class name is not dynamic */ if (!$dynamic) { diff --git a/Library/Cache/FunctionCache.php b/Library/Cache/FunctionCache.php index 2842e71b65..e44fa9c884 100644 --- a/Library/Cache/FunctionCache.php +++ b/Library/Cache/FunctionCache.php @@ -9,15 +9,14 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Cache; -use Zephir\Call; use Zephir\CompilationContext; use Zephir\Passes\CallGathererPass; /** - * FunctionCache. - * * Calls in Zephir implement monomorphic and polymorphic caches to * improve performance. Method/Functions lookups are cached in a standard * first-level method lookup cache. @@ -36,14 +35,14 @@ */ class FunctionCache { - protected $cache = []; + protected array $cache = []; - protected $gatherer; + protected ?CallGathererPass $gatherer = null; /** * FunctionCache constructor. * - * @param CallGathererPass $gatherer + * @param CallGathererPass|null $gatherer */ public function __construct(CallGathererPass $gatherer = null) { @@ -54,13 +53,12 @@ public function __construct(CallGathererPass $gatherer = null) * Retrieves/Creates a function cache for a function call. * * @param string $functionName - * @param Call $call * @param CompilationContext $compilationContext * @param bool $exists * * @return string */ - public function get($functionName, CompilationContext $compilationContext, Call $call, $exists) + public function get(string $functionName, CompilationContext $compilationContext, bool $exists): string { if (isset($this->cache[$functionName])) { return $this->cache[$functionName].', '.SlotsCache::getExistingFunctionSlot($functionName); @@ -73,13 +71,10 @@ public function get($functionName, CompilationContext $compilationContext, Call $cacheSlot = SlotsCache::getFunctionSlot($functionName); $number = 0; - if (!$compilationContext->insideCycle) { - $gatherer = $this->gatherer; - if ($gatherer) { - $number = $gatherer->getNumberOfFunctionCalls($functionName); - if ($number <= 1) { - return 'NULL, '.$cacheSlot; - } + if (!$compilationContext->insideCycle && $this->gatherer !== null) { + $number = $this->gatherer->getNumberOfFunctionCalls($functionName); + if ($number <= 1) { + return 'NULL, '.$cacheSlot; } } diff --git a/Library/Cache/MethodCache.php b/Library/Cache/MethodCache.php index 236ceabb4b..66238021c2 100644 --- a/Library/Cache/MethodCache.php +++ b/Library/Cache/MethodCache.php @@ -9,8 +9,12 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Cache; +use ReflectionClass; +use ReflectionException; use Zephir\ClassDefinition; use Zephir\CompilationContext; use Zephir\MethodCallWarmUp; @@ -18,8 +22,6 @@ use Zephir\Variable; /** - * MethodCache. - * * Calls in Zephir implement monomorphic and polymorphic caches to * improve performance. Method/Functions lookups are cached in a standard * first-level method lookup cache. @@ -38,14 +40,12 @@ */ class MethodCache { - protected $cache = []; + protected array $cache = []; - protected $gatherer; + protected ?CallGathererPass $gatherer = null; /** - * MethodCache. - * - * @param CallGathererPass $gatherer + * @param CallGathererPass|null $gatherer */ public function __construct(CallGathererPass $gatherer = null) { @@ -60,8 +60,10 @@ public function __construct(CallGathererPass $gatherer = null) * @param Variable $caller * * @return string + * + * @throws ReflectionException */ - public function get(CompilationContext $compilationContext, $methodName, Variable $caller) + public function get(CompilationContext $compilationContext, string $methodName, Variable $caller): string { $compiler = $compilationContext->compiler; @@ -185,27 +187,22 @@ public function get(CompilationContext $compilationContext, $methodName, Variabl /** * Checks if the class is suitable for caching. * - * @param ClassDefinition $classDefinition + * @param ClassDefinition|ReflectionClass|null $classDefinition * * @return bool */ - private function isClassCacheable($classDefinition) + private function isClassCacheable($classDefinition = null): bool { if ($classDefinition instanceof ClassDefinition) { return true; } - if ($classDefinition instanceof \ReflectionClass) { - if ($classDefinition->isInternal() && $classDefinition->isInstantiable()) { - $extension = $classDefinition->getExtension(); - switch ($extension->getName()) { - case 'Reflection': - case 'Core': - case 'SPL': - return true; - } - } + + if (!($classDefinition instanceof ReflectionClass)) { + return false; } - return false; + return $classDefinition->isInternal() && + $classDefinition->isInstantiable() && + in_array($classDefinition->getExtension()->getName(), ['Reflection', 'Core', 'SPL']); } } diff --git a/Library/Cache/SlotsCache.php b/Library/Cache/SlotsCache.php index 2836ba2c9e..eb211d71d7 100644 --- a/Library/Cache/SlotsCache.php +++ b/Library/Cache/SlotsCache.php @@ -9,25 +9,26 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Cache; use Zephir\ClassMethod; /** - * SlotsCache. - * * In order to reduce memory allocation when calling functions and method_exists * Zephir provides a global cache that store pointers to resolved functions * that aren't dynamical reducing the time to lookup functions and methods */ class SlotsCache { - const MAX_SLOTS_NUMBER = 512; - private static $slot = 1; + public const MAX_SLOTS_NUMBER = 512; + + private static int $slot = 1; - private static $cacheMethodSlots = []; + private static array $cacheMethodSlots = []; - private static $cacheFunctionSlots = []; + private static array $cacheFunctionSlots = []; /** * Returns or creates a cache slot for a function. @@ -36,7 +37,7 @@ class SlotsCache * * @return int */ - public static function getFunctionSlot($functionName) + public static function getFunctionSlot(string $functionName): int { if (isset(self::$cacheFunctionSlots[$functionName])) { return self::$cacheFunctionSlots[$functionName]; @@ -59,13 +60,9 @@ public static function getFunctionSlot($functionName) * * @return int */ - public static function getExistingFunctionSlot($functionName) + public static function getExistingFunctionSlot(string $functionName): int { - if (isset(self::$cacheFunctionSlots[$functionName])) { - return self::$cacheFunctionSlots[$functionName]; - } - - return 0; + return self::$cacheFunctionSlots[$functionName] ?? 0; } /** @@ -75,7 +72,7 @@ public static function getExistingFunctionSlot($functionName) * * @return int */ - public static function getMethodSlot(ClassMethod $method) + public static function getMethodSlot(ClassMethod $method): int { $className = $method->getClassDefinition()->getCompleteName(); $methodName = $method->getName(); @@ -101,15 +98,11 @@ public static function getMethodSlot(ClassMethod $method) * * @return int */ - public static function getExistingMethodSlot(ClassMethod $method) + public static function getExistingMethodSlot(ClassMethod $method): int { $className = $method->getClassDefinition()->getCompleteName(); $methodName = $method->getName(); - if (isset(self::$cacheMethodSlots[$className][$methodName])) { - return self::$cacheMethodSlots[$className][$methodName]; - } - - return 0; + return self::$cacheMethodSlots[$className][$methodName] ?? 0; } } diff --git a/Library/Cache/StaticMethodCache.php b/Library/Cache/StaticMethodCache.php index fde7997b59..6b88dc831a 100644 --- a/Library/Cache/StaticMethodCache.php +++ b/Library/Cache/StaticMethodCache.php @@ -11,12 +11,11 @@ namespace Zephir\Cache; +use ReflectionMethod; use Zephir\ClassMethod; use Zephir\CompilationContext; /** - * StaticMethodCache. - * * Calls in Zephir implement monomorphic and polymorphic caches to * improve performance. Method/Functions lookups are cached in a standard * first-level method lookup cache. @@ -35,45 +34,41 @@ */ class StaticMethodCache { - protected $cache = []; + protected array $cache = []; /** * MethodCache. * - * @param CompilationContext $compilationContext - * @param ClassMethod|\ReflectionMethod $method - * @param bool $allowNtsCache + * @param CompilationContext $compilationContext + * @param ClassMethod|ReflectionMethod|null $method + * @param bool $allowNtsCache * * @return string */ - public function get(CompilationContext $compilationContext, $method, $allowNtsCache = true) + public function get(CompilationContext $compilationContext, $method, bool $allowNtsCache = true): string { - if (!\is_object($method)) { + if ($method === null) { return 'NULL, 0'; } - if (!($method instanceof \ReflectionMethod)) { + if (!($method instanceof ReflectionMethod)) { $completeName = $method->getClassDefinition()->getCompleteName(); - /* - * Avoid generate caches for external classes + /** + * Avoid generate caches for external classes or for interfaces. */ - if ($method->getClassDefinition()->isExternal()) { + if ($method->getClassDefinition()->isExternal() || $method->getClassDefinition()->isInterface()) { return 'NULL, 0'; } if (isset($this->cache[$completeName][$method->getName()])) { return '&'.$this->cache[$completeName][$method->getName()]->getName().', '.SlotsCache::getExistingMethodSlot($method); } - - if ($method->getClassDefinition()->isInterface()) { - return 'NULL, 0'; - } } $mustBeCached = false; if (!$compilationContext->insideCycle) { - if (!($method instanceof \ReflectionMethod)) { + if (!($method instanceof ReflectionMethod)) { $classDefinition = $method->getClassDefinition(); if (!$classDefinition->isBundled() && $allowNtsCache) { $mustBeCached = !$compilationContext->backend->isZE3(); @@ -100,7 +95,7 @@ public function get(CompilationContext $compilationContext, $method, $allowNtsCa $functionCache->setMustInitNull(true); $functionCache->setReusable(false); - if (!($method instanceof \ReflectionMethod)) { + if (!($method instanceof ReflectionMethod)) { $this->cache[$completeName][$method->getName()] = $functionCache; } diff --git a/Library/ClassMethod.php b/Library/ClassMethod.php index 8c1ec47486..7802cf7ce4 100644 --- a/Library/ClassMethod.php +++ b/Library/ClassMethod.php @@ -2340,6 +2340,16 @@ public function isReturnTypesHintDetermined(): bool return true; } + /** + * Checks if method's return type is nullable object `?object`. + * + * @return bool + */ + public function isReturnTypeNullableObject(): bool + { + return count($this->returnTypes) === 2 && isset($this->returnTypes['object']) && isset($this->returnTypes['null']); + } + /** * Checks if the method have compatible return types. * diff --git a/Library/Classes/Entry.php b/Library/Classes/Entry.php index 400592fc95..2b1466cea2 100644 --- a/Library/Classes/Entry.php +++ b/Library/Classes/Entry.php @@ -125,7 +125,6 @@ public function get(): string $className = str_replace(self::NAMESPACE_SEPARATOR, self::NAMESPACE_SEPARATOR.self::NAMESPACE_SEPARATOR, strtolower($className)); return sprintf( - //'zend_lookup_class_ex(zend_string_init(ZEND_STRL("%s"), 0), NULL, 0)', 'zephir_get_internal_ce(SL("%s"))', $className, ); diff --git a/Library/Compiler.php b/Library/Compiler.php index 74c7443985..601465ecf7 100644 --- a/Library/Compiler.php +++ b/Library/Compiler.php @@ -1845,10 +1845,10 @@ public function createProjectFiles(string $project): bool '%PROJECT_LOWER%' => strtolower($project), '%PROJECT_UPPER%' => strtoupper($project), '%PROJECT_EXTNAME%' => strtolower($project), - '%PROJECT_NAME%' => utf8_decode($this->config->get('name')), - '%PROJECT_AUTHOR%' => utf8_decode($this->config->get('author')), - '%PROJECT_VERSION%' => utf8_decode($this->config->get('version')), - '%PROJECT_DESCRIPTION%' => utf8_decode($this->config->get('description')), + '%PROJECT_NAME%' => mb_convert_encoding($this->config->get('name'), 'ISO-8859-1', 'UTF-8'), + '%PROJECT_AUTHOR%' => mb_convert_encoding($this->config->get('author'), 'ISO-8859-1', 'UTF-8'), + '%PROJECT_VERSION%' => mb_convert_encoding($this->config->get('version'), 'ISO-8859-1', 'UTF-8'), + '%PROJECT_DESCRIPTION%' => mb_convert_encoding($this->config->get('description'), 'ISO-8859-1', 'UTF-8'), '%PROJECT_ZEPVERSION%' => Zephir::VERSION, '%EXTENSION_GLOBALS%' => $globalCode, '%EXTENSION_STRUCT_GLOBALS%' => $globalStruct, diff --git a/Library/Detectors/ForValueUseDetector.php b/Library/Detectors/ForValueUseDetector.php index 7e76e4084d..de0cb35bfa 100644 --- a/Library/Detectors/ForValueUseDetector.php +++ b/Library/Detectors/ForValueUseDetector.php @@ -12,8 +12,6 @@ namespace Zephir\Detectors; /** - * ForValueUseDetector. - * * Detects whether the traversed variable is modified within the 'for's block */ class ForValueUseDetector extends WriteDetector diff --git a/Library/Detectors/ReadDetector.php b/Library/Detectors/ReadDetector.php index ede40e043b..160f8d0019 100644 --- a/Library/Detectors/ReadDetector.php +++ b/Library/Detectors/ReadDetector.php @@ -9,68 +9,49 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Detectors; use Zephir\Variable; +use function is_array; + /** - * ReadDetector. - * * Detects if a variable is used in a given expression context * Since zvals are collected between executions to the same section of code * We need to ensure that a variable is not contained in the "right-side" expression - * used to assign the variable, avoiding premature initializations + * used to assign the variable, avoiding premature initializations. */ class ReadDetector { - public function detect($variable, array $expression) + public function detect($variable, array $expression): bool { if (!isset($expression['type'])) { return false; } - /* Remove branch from variable name */ + /** + * Remove branch from variable name. + */ $pos = strpos($variable, Variable::BRANCH_MAGIC); if ($pos > -1) { $variable = substr($variable, 0, $pos); } - if ('variable' == $expression['type']) { - if ($variable == $expression['value']) { - return true; - } - } - - if ('fcall' == $expression['type'] || 'mcall' == $expression['type'] || 'scall' == $expression['type']) { - if (isset($expression['parameters'])) { - foreach ($expression['parameters'] as $parameter) { - if (\is_array($parameter['parameter'])) { - if ('variable' == $parameter['parameter']['type']) { - if ($variable == $parameter['parameter']['value']) { - return true; - } - } - } - } - } - } - - if (isset($expression['left'])) { - if (\is_array($expression['left'])) { - if (true === $this->detect($variable, $expression['left'])) { - return true; - } - } + if ('variable' === $expression['type'] && $variable === $expression['value']) { + return true; } - if (isset($expression['right'])) { - if (\is_array($expression['right'])) { - if (true === $this->detect($variable, $expression['right'])) { + if (in_array($expression['type'], ['fcall', 'mcall', 'scall']) && isset($expression['parameters'])) { + foreach ($expression['parameters'] as $parameter) { + if (is_array($parameter['parameter']) && 'variable' === $parameter['parameter']['type'] && $variable == $parameter['parameter']['value']) { return true; } } } - return false; + return (isset($expression['left']) && is_array($expression['left']) && $this->detect($variable, $expression['left'])) || + (isset($expression['right']) && is_array($expression['right']) && $this->detect($variable, $expression['right'])); } } diff --git a/Library/Detectors/WriteDetector.php b/Library/Detectors/WriteDetector.php index 05a5e1ebd8..a16cf941ef 100644 --- a/Library/Detectors/WriteDetector.php +++ b/Library/Detectors/WriteDetector.php @@ -9,11 +9,11 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Detectors; /** - * WriteDetector. - * * Detects whether a variable is mutated in a given context * If a variable is not modified in a local context (method block) we can avoid allocate * memory for its body (zvalue) @@ -22,15 +22,16 @@ */ class WriteDetector { - const DETECT_NONE = 0; + public const DETECT_NONE = 0; - const DETECT_PARAM_PASS = 1; + public const DETECT_PARAM_PASS = 1; - const DETECT_ARRAY_USE = 2; + public const DETECT_ARRAY_USE = 2; - const DETECT_VALUE_IN_ASSIGNMENT = 4; + public const DETECT_VALUE_IN_ASSIGNMENT = 4; + + public const DETECT_ALL = 255; - const DETECT_ALL = 255; protected $detectionFlags = 0; protected $mutations = []; @@ -65,9 +66,9 @@ public function setDetectionFlags($flags) * * @param string $variable * - * @return ForValueUseDetector + * @return WriteDetector */ - public function increaseMutations($variable) + public function increaseMutations($variable): self { if (isset($this->mutations[$variable])) { ++$this->mutations[$variable]; @@ -177,7 +178,7 @@ public function passNew(array $expression) * * @param array $statement */ - public function declareVariables(array $statement) + public function declareVariables(array $statement): void { if (isset($statement['data-type'])) { if ('variable' != $statement['data-type']) { @@ -249,6 +250,22 @@ public function passExpression(array $expression) break; case 'typeof': + case 'minus': + case 'list': + case 'array-access': + case 'static-property-access': + case 'property-string-access': + case 'property-dynamic-access': + case 'property-access': + case 'ternary': + case 'unlikely': + case 'likely': + case 'clone': + case 'require_once': + case 'require': + case 'instanceof': + case 'empty': + case 'isset': case 'not': $this->passExpression($expression['left']); break; @@ -268,40 +285,11 @@ public function passExpression(array $expression) $this->passNew($expression); break; - case 'property-access': - case 'property-dynamic-access': - case 'property-string-access': - case 'static-property-access': - case 'array-access': - $this->passExpression($expression['left']); - break; - - case 'isset': - case 'empty': - case 'instanceof': - case 'require': - case 'require_once': - case 'clone': - case 'likely': - case 'unlikely': - /* do special pass later */ - case 'ternary': - $this->passExpression($expression['left']); - break; - case 'fetch': $this->increaseMutations($expression['left']['value']); $this->passExpression($expression['right']); break; - case 'minus': - $this->passExpression($expression['left']); - break; - - case 'list': - $this->passExpression($expression['left']); - break; - case 'cast': case 'type-hint': $this->passExpression($expression['right']); @@ -391,6 +379,7 @@ public function passStatementBlock(array $statements) } break; + case 'throw': case 'return': if (isset($statement['expr'])) { $this->passExpression($statement['expr']); @@ -419,12 +408,6 @@ public function passStatementBlock(array $statements) } break; - case 'throw': - if (isset($statement['expr'])) { - $this->passExpression($statement['expr']); - } - break; - case 'fetch': $this->passExpression($statement['expr']); break; diff --git a/Library/EventListener/ConsoleErrorListener.php b/Library/EventListener/ConsoleErrorListener.php index 77d3c81e9d..7c1d56acae 100644 --- a/Library/EventListener/ConsoleErrorListener.php +++ b/Library/EventListener/ConsoleErrorListener.php @@ -9,18 +9,17 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\EventListener; use Symfony\Component\Console\Event\ConsoleErrorEvent; use Zephir\Exception\CompilerException; use Zephir\Exception\ExceptionInterface; -/** - * Zephir\EventListener\ConsoleErrorListener. - */ class ConsoleErrorListener { - public function onCommandError(ConsoleErrorEvent $event) + public function onCommandError(ConsoleErrorEvent $event): void { if (!filter_var(getenv('ZEPHIR_DEBUG'), FILTER_VALIDATE_BOOLEAN)) { return; diff --git a/Library/Expression/Builder/Factory/OperatorsFactory.php b/Library/Expression/Builder/Factory/OperatorsFactory.php index 2b31e5c3b2..c5d4ccd9ed 100644 --- a/Library/Expression/Builder/Factory/OperatorsFactory.php +++ b/Library/Expression/Builder/Factory/OperatorsFactory.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Expression\Builder\Factory; use Zephir\Expression\Builder\AbstractBuilder; @@ -24,8 +26,7 @@ */ class OperatorsFactory { - protected $factory; - protected $assignFactory; + protected BuilderFactory $factory; /** * @param BuilderFactory $factory diff --git a/Library/Expression/Builder/Factory/StatementsFactory.php b/Library/Expression/Builder/Factory/StatementsFactory.php index 566f0f243a..99190f963c 100644 --- a/Library/Expression/Builder/Factory/StatementsFactory.php +++ b/Library/Expression/Builder/Factory/StatementsFactory.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Expression\Builder\Factory; use Zephir\Expression\Builder\AbstractBuilder; @@ -22,12 +24,9 @@ use Zephir\Expression\Builder\Statements\RawStatement; use Zephir\Expression\Builder\Statements\StatementsBlock; -/** - * Class StatementsFactory. - */ class StatementsFactory { - protected $factory; + protected BuilderFactory $factory; /** * @param BuilderFactory $factory diff --git a/Library/Expression/Closure.php b/Library/Expression/Closure.php index a8d4b186e1..3925339689 100644 --- a/Library/Expression/Closure.php +++ b/Library/Expression/Closure.php @@ -23,6 +23,7 @@ use Zephir\Exception; use Zephir\StatementsBlock; use Zephir\Variable; + use function is_array; /** diff --git a/Library/Expression/Constants.php b/Library/Expression/Constants.php index 3ce948fddc..587105183b 100644 --- a/Library/Expression/Constants.php +++ b/Library/Expression/Constants.php @@ -17,23 +17,31 @@ use Zephir\LiteralCompiledExpression; use Zephir\Variable; +use function constant; +use function defined; +use function gettype; +use function in_array; use function Zephir\add_slashes; /** - * Zephir\Expression\Constants. - * * Resolves PHP or Zephir constants into C-Code */ class Constants { - /** @var bool */ - protected $expecting = true; + /** + * @var bool + */ + protected bool $expecting = true; - /** @var bool */ - protected $readOnly = false; + /** + * @var bool + */ + protected bool $readOnly = false; - /** @var Variable */ - protected $expectingVariable; + /** + * @var Variable|null + */ + protected ?Variable $expectingVariable = null; /** * Reserved ENV Constants. @@ -42,7 +50,7 @@ class Constants * * @var array */ - protected $envConstants = [ + protected array $envConstants = [ 'PHP_VERSION', 'PHP_MAJOR_VERSION', 'PHP_MINOR_VERSION', @@ -70,7 +78,7 @@ class Constants * * @var array */ - protected $magicConstants = [ + protected array $magicConstants = [ '__LINE__', '__FILE__', '__DIR__', @@ -81,7 +89,7 @@ class Constants '__NAMESPACE__', ]; - protected $resources = [ + protected array $resources = [ 'STDIN', 'STDOUT', 'STDERR', @@ -91,10 +99,10 @@ class Constants * Sets if the variable must be resolved into a direct variable symbol * create a temporary value or ignore the return value. * - * @param bool $expecting - * @param Variable $expectingVariable + * @param bool $expecting + * @param Variable|null $expectingVariable */ - public function setExpectReturn($expecting, Variable $expectingVariable = null) + public function setExpectReturn(bool $expecting, ?Variable $expectingVariable = null) { $this->expecting = $expecting; $this->expectingVariable = $expectingVariable; @@ -105,7 +113,7 @@ public function setExpectReturn($expecting, Variable $expectingVariable = null) * * @param bool $readOnly */ - public function setReadOnly($readOnly) + public function setReadOnly(bool $readOnly): void { $this->readOnly = $readOnly; } @@ -128,7 +136,7 @@ public function compile(array $expression, CompilationContext $compilationContex $constantName = $expression['value']; $mergedConstants = array_merge($this->envConstants, $this->magicConstants, $this->resources); - if (!\defined($expression['value']) && !\in_array($constantName, $mergedConstants)) { + if (!defined($expression['value']) && !in_array($constantName, $mergedConstants)) { if (!$compilationContext->compiler->isConstant($constantName)) { $compilationContext->logger->warning( "Constant '".$constantName."' does not exist at compile time", @@ -138,22 +146,18 @@ public function compile(array $expression, CompilationContext $compilationContex $isZephirConstant = true; } } else { - if (false !== strpos($constantName, 'VERSION')) { - $isPhpConstant = false; - } else { - $isPhpConstant = true; - } + $isPhpConstant = false === strpos($constantName, 'VERSION'); } - if ($isZephirConstant && !\in_array($constantName, $this->resources)) { + if ($isZephirConstant && !in_array($constantName, $this->resources)) { $constant = $compilationContext->compiler->getConstant($constantName); return new LiteralCompiledExpression($constant[0], $constant[1], $expression); } - if ($isPhpConstant && !\in_array($constantName, $mergedConstants)) { - $constantName = \constant($constantName); - $type = strtolower(\gettype($constantName)); + if ($isPhpConstant && !in_array($constantName, $mergedConstants)) { + $constantName = constant($constantName); + $type = strtolower(gettype($constantName)); switch ($type) { case 'integer': @@ -172,7 +176,7 @@ public function compile(array $expression, CompilationContext $compilationContex } } - if (\in_array($constantName, $this->magicConstants)) { + if (in_array($constantName, $this->magicConstants)) { switch ($constantName) { case '__CLASS__': return new CompiledExpression( diff --git a/Library/Expression/NativeArray.php b/Library/Expression/NativeArray.php index 8e61539da7..72c87e03f7 100644 --- a/Library/Expression/NativeArray.php +++ b/Library/Expression/NativeArray.php @@ -9,38 +9,43 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Expression; +use ReflectionException; use Zephir\CompilationContext; use Zephir\CompiledExpression; +use Zephir\Exception; use Zephir\Exception\CompilerException; use Zephir\Expression; use Zephir\GlobalConstant; use Zephir\Variable; +use function count; +use function function_exists; + /** - * Zephir\Expression\NativeArray. - * * Resolves expressions that create arrays */ class NativeArray { - protected $expecting = true; + protected bool $expecting = true; - protected $readOnly = false; + protected bool $readOnly = false; - protected $noisy = true; + protected bool $noisy = true; - protected $expectingVariable; + protected ?Variable $expectingVariable = null; /** * Sets if the variable must be resolved into a direct variable symbol * create a temporary value or ignore the return value. * - * @param bool $expecting - * @param Variable $expectingVariable + * @param bool $expecting + * @param Variable|null $expectingVariable */ - public function setExpectReturn($expecting, Variable $expectingVariable = null) + public function setExpectReturn(bool $expecting, Variable $expectingVariable = null) { $this->expecting = $expecting; $this->expectingVariable = $expectingVariable; @@ -51,7 +56,7 @@ public function setExpectReturn($expecting, Variable $expectingVariable = null) * * @param bool $readOnly */ - public function setReadOnly($readOnly) + public function setReadOnly(bool $readOnly): void { $this->readOnly = $readOnly; } @@ -61,7 +66,7 @@ public function setReadOnly($readOnly) * * @param bool $noisy */ - public function setNoisy($noisy) + public function setNoisy(bool $noisy): void { $this->noisy = $noisy; } @@ -99,11 +104,11 @@ public function getArrayValue(CompiledExpression $exprCompiled, CompilationConte return $tempVar; case 'bool': - if ('true' == $exprCompiled->getCode()) { + if ('true' === $exprCompiled->getCode()) { return new GlobalConstant('ZEPHIR_GLOBAL(global_true)'); } - if ('false' == $exprCompiled->getCode()) { + if ('false' === $exprCompiled->getCode()) { return new GlobalConstant('ZEPHIR_GLOBAL(global_false)'); } @@ -172,10 +177,13 @@ public function getArrayValue(CompiledExpression $exprCompiled, CompilationConte * @param CompilationContext $compilationContext * * @return CompiledExpression + * + * @throws ReflectionException + * @throws Exception */ - public function compile($expression, CompilationContext $compilationContext) + public function compile(array $expression, CompilationContext $compilationContext) { - /* + /** * Resolves the symbol that expects the value */ if ($this->expecting) { @@ -195,7 +203,8 @@ public function compile($expression, CompilationContext $compilationContext) if ($this->expectingVariable) { $symbolVariable->initVariant($compilationContext); } - /*+ + + /** * Mark the variable as an array */ $symbolVariable->setDynamicTypes('array'); @@ -204,18 +213,17 @@ public function compile($expression, CompilationContext $compilationContext) } $codePrinter = $compilationContext->codePrinter; - $compilationContext->headersManager->add('kernel/array'); /** * This calculates a prime number bigger than the current array size to possibly * reduce hash collisions when adding new members to the array. */ - $arrayLength = \count($expression['left']); - if ($arrayLength >= 33 && \function_exists('gmp_nextprime')) { - $arrayLength = gmp_strval(gmp_nextprime($arrayLength - 1)); + $arrayLength = count($expression['left']); + if ($arrayLength >= 33 && function_exists('gmp_nextprime')) { + $arrayLength = (int) gmp_strval(gmp_nextprime($arrayLength - 1)); } - $oldSymbolVariable = $symbolVariable; + if ($this->expectingVariable && $symbolVariable->getVariantInits() >= 1) { $symbolVariable = $compilationContext->symbolTable->addTemp('variable', $compilationContext); $symbolVariable->initVariant($compilationContext); @@ -225,215 +233,15 @@ public function compile($expression, CompilationContext $compilationContext) if ($this->expectingVariable) { $symbolVariable->initVariant($compilationContext); } - /*+ + + /** * Mark the variable as an array */ $symbolVariable->setDynamicTypes('array'); $compilationContext->backend->initArray($symbolVariable, $compilationContext, $arrayLength > 0 ? $arrayLength : null); } foreach ($expression['left'] as $item) { - if (isset($item['key'])) { - $key = null; - $exprKey = new Expression($item['key']); - $resolvedExprKey = $exprKey->compile($compilationContext); - - switch ($resolvedExprKey->getType()) { - case 'string': - $expr = new Expression($item['value']); - $resolvedExpr = $expr->compile($compilationContext); - switch ($resolvedExpr->getType()) { - case 'int': - case 'uint': - case 'long': - case 'ulong': - case 'double': - $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); - break; - - case 'bool': - $compilationContext->headersManager->add('kernel/array'); - if ('true' == $resolvedExpr->getCode()) { - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'true', $compilationContext, 'PH_COPY | PH_SEPARATE'); - } else { - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'false', $compilationContext, 'PH_COPY | PH_SEPARATE'); - } - break; - - case 'string': - $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); - break; - - case 'null': - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'null', $compilationContext, 'PH_COPY | PH_SEPARATE'); - break; - - case 'array': - case 'variable': - $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $valueVariable, $compilationContext, 'PH_COPY | PH_SEPARATE'); - if ($valueVariable->isTemporal()) { - $valueVariable->setIdle(true); - } - break; - - default: - throw new CompilerException('Invalid value type: '.$resolvedExpr->getType(), $item['value']); - } - break; - - case 'int': - case 'uint': - case 'long': - case 'ulong': - $expr = new Expression($item['value']); - $resolvedExpr = $expr->compile($compilationContext); - switch ($resolvedExpr->getType()) { - case 'int': - case 'uint': - case 'long': - case 'ulong': - case 'double': - case 'string': - $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); - break; - - case 'bool': - if ('true' == $resolvedExpr->getCode()) { - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'true', $compilationContext, 'PH_COPY'); - } else { - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'false', $compilationContext, 'PH_COPY'); - } - break; - - case 'null': - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'null', $compilationContext, 'PH_COPY'); - break; - - case 'array': - case 'variable': - $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $valueVariable, $compilationContext, 'PH_COPY'); - if ($valueVariable->isTemporal()) { - $valueVariable->setIdle(true); - } - break; - - default: - throw new CompilerException('Invalid value type: '.$item['value']['type'], $item['value']); - } - break; - - case 'variable': - $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExprKey->getCode(), $compilationContext, $item['key']); - switch ($variableVariable->getType()) { - case 'int': - case 'uint': - case 'long': - case 'ulong': - $expr = new Expression($item['value']); - $resolvedExpr = $expr->compile($compilationContext); - switch ($resolvedExpr->getType()) { - case 'int': - case 'uint': - case 'long': - case 'ulong': - case 'bool': - case 'double': - case 'null': - case 'string': - $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); - break; - - case 'variable': - $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $valueVariable, $compilationContext, 'PH_COPY'); - if ($valueVariable->isTemporal()) { - $valueVariable->setIdle(true); - } - break; - - default: - throw new CompilerException('Invalid value type: '.$item['value']['type'], $item['value']); - } - break; - - case 'string': - $expr = new Expression($item['value']); - $resolvedExpr = $expr->compile($compilationContext); - switch ($resolvedExpr->getType()) { - case 'int': - case 'uint': - case 'long': - case 'ulong': - $codePrinter->output('add_assoc_long_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].'), '.$resolvedExpr->getCode().');'); - break; - - case 'double': - $codePrinter->output('add_assoc_double_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].'), '.$resolvedExpr->getCode().');'); - break; - - case 'bool': - $codePrinter->output('add_assoc_bool_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].'), '.$resolvedExpr->getBooleanCode().');'); - break; - - case 'string': - $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); - break; - - case 'null': - $codePrinter->output('add_assoc_null_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].') + 1);'); - break; - - case 'variable': - $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); - $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); - if ($valueVariable->isTemporal()) { - $valueVariable->setIdle(true); - } - break; - - default: - throw new CompilerException('Invalid value type: '.$resolvedExpr->getType(), $item['value']); - } - break; - - case 'variable': - $expr = new Expression($item['value']); - $resolvedExpr = $expr->compile($compilationContext); - switch ($resolvedExpr->getType()) { - case 'null': - case 'bool': - $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); - $compilationContext->backend->updateArray($symbolVariable, $variableVariable, $valueVariable, $compilationContext); - - if ($valueVariable->isTemporal()) { - $valueVariable->setIdle(true); - } - break; - - case 'variable': - $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); - $compilationContext->backend->updateArray($symbolVariable, $variableVariable, $valueVariable, $compilationContext); - - if ($valueVariable->isTemporal()) { - $valueVariable->setIdle(true); - } - break; - - default: - throw new CompilerException('Invalid value type: '.$item['value']['type'], $item['value']); - } - break; - - default: - throw new CompilerException('Cannot use variable type: '.$variableVariable->getType().' as array index', $item['key']); - } - break; - - default: - throw new CompilerException('Invalid key type: '.$resolvedExprKey->getType(), $item['key']); - } - } else { + if (!isset($item['key'])) { $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); $itemVariable = $this->getArrayValue($resolvedExpr, $compilationContext); @@ -443,6 +251,197 @@ public function compile($expression, CompilationContext $compilationContext) if ($itemVariable->isTemporal()) { $itemVariable->setIdle(true); } + + continue; + } + + $exprKey = new Expression($item['key']); + $resolvedExprKey = $exprKey->compile($compilationContext); + + switch ($resolvedExprKey->getType()) { + case 'string': + $expr = new Expression($item['value']); + $resolvedExpr = $expr->compile($compilationContext); + switch ($resolvedExpr->getType()) { + case 'int': + case 'uint': + case 'long': + case 'ulong': + case 'string': + case 'double': + $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); + break; + + case 'bool': + $compilationContext->headersManager->add('kernel/array'); + if ('true' == $resolvedExpr->getCode()) { + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'true', $compilationContext, 'PH_COPY | PH_SEPARATE'); + } else { + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'false', $compilationContext, 'PH_COPY | PH_SEPARATE'); + } + break; + + case 'null': + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'null', $compilationContext, 'PH_COPY | PH_SEPARATE'); + break; + + case 'array': + case 'variable': + $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $valueVariable, $compilationContext, 'PH_COPY | PH_SEPARATE'); + if ($valueVariable->isTemporal()) { + $valueVariable->setIdle(true); + } + break; + + default: + throw new CompilerException('Invalid value type: '.$resolvedExpr->getType(), $item['value']); + } + break; + + case 'int': + case 'uint': + case 'long': + case 'ulong': + $expr = new Expression($item['value']); + $resolvedExpr = $expr->compile($compilationContext); + switch ($resolvedExpr->getType()) { + case 'int': + case 'uint': + case 'long': + case 'ulong': + case 'double': + case 'string': + $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); + break; + + case 'bool': + if ('true' == $resolvedExpr->getCode()) { + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'true', $compilationContext, 'PH_COPY'); + } else { + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'false', $compilationContext, 'PH_COPY'); + } + break; + + case 'null': + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, 'null', $compilationContext, 'PH_COPY'); + break; + + case 'array': + case 'variable': + $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $valueVariable, $compilationContext, 'PH_COPY'); + if ($valueVariable->isTemporal()) { + $valueVariable->setIdle(true); + } + break; + + default: + throw new CompilerException('Invalid value type: '.$item['value']['type'], $item['value']); + } + break; + + case 'variable': + $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExprKey->getCode(), $compilationContext, $item['key']); + switch ($variableVariable->getType()) { + case 'int': + case 'uint': + case 'long': + case 'ulong': + $expr = new Expression($item['value']); + $resolvedExpr = $expr->compile($compilationContext); + switch ($resolvedExpr->getType()) { + case 'int': + case 'uint': + case 'long': + case 'ulong': + case 'bool': + case 'double': + case 'null': + case 'string': + $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); + break; + + case 'variable': + $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $valueVariable, $compilationContext, 'PH_COPY'); + if ($valueVariable->isTemporal()) { + $valueVariable->setIdle(true); + } + break; + + default: + throw new CompilerException('Invalid value type: '.$item['value']['type'], $item['value']); + } + break; + + case 'string': + $expr = new Expression($item['value']); + $resolvedExpr = $expr->compile($compilationContext); + switch ($resolvedExpr->getType()) { + case 'int': + case 'uint': + case 'long': + case 'ulong': + $codePrinter->output('add_assoc_long_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].'), '.$resolvedExpr->getCode().');'); + break; + + case 'double': + $codePrinter->output('add_assoc_double_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].'), '.$resolvedExpr->getCode().');'); + break; + + case 'bool': + $codePrinter->output('add_assoc_bool_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].'), '.$resolvedExpr->getBooleanCode().');'); + break; + + case 'string': + $compilationContext->backend->addArrayEntry($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); + break; + + case 'null': + $codePrinter->output('add_assoc_null_ex('.$symbolVariable->getName().', Z_STRVAL_P('.$resolvedExprKey->getCode().'), Z_STRLEN_P('.$item['key']['value'].') + 1);'); + break; + + case 'variable': + $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); + $compilationContext->backend->updateArray($symbolVariable, $resolvedExprKey, $resolvedExpr, $compilationContext); + if ($valueVariable->isTemporal()) { + $valueVariable->setIdle(true); + } + break; + + default: + throw new CompilerException('Invalid value type: '.$resolvedExpr->getType(), $item['value']); + } + break; + + case 'variable': + $expr = new Expression($item['value']); + $resolvedExpr = $expr->compile($compilationContext); + switch ($resolvedExpr->getType()) { + case 'null': + case 'variable': + case 'bool': + $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); + $compilationContext->backend->updateArray($symbolVariable, $variableVariable, $valueVariable, $compilationContext); + + if ($valueVariable->isTemporal()) { + $valueVariable->setIdle(true); + } + break; + + default: + throw new CompilerException('Invalid value type: '.$item['value']['type'], $item['value']); + } + break; + + default: + throw new CompilerException('Cannot use variable type: '.$variableVariable->getType().' as array index', $item['key']); + } + break; + + default: + throw new CompilerException('Invalid key type: '.$resolvedExprKey->getType(), $item['key']); } } diff --git a/Library/Expression/NativeArrayAccess.php b/Library/Expression/NativeArrayAccess.php index ed51ca6283..8fc2d54ded 100644 --- a/Library/Expression/NativeArrayAccess.php +++ b/Library/Expression/NativeArrayAccess.php @@ -11,15 +11,15 @@ namespace Zephir\Expression; +use ReflectionException; use Zephir\CompilationContext; use Zephir\CompiledExpression; +use Zephir\Exception; use Zephir\Exception\CompilerException; use Zephir\Expression; use Zephir\Variable; /** - * Zephir\Expression\NativeArrayAccess. - * * Resolves expressions that read array indexes */ class NativeArrayAccess @@ -88,7 +88,7 @@ public function compile($expression, CompilationContext $compilationContext) $expr->setReadOnly(true); $exprVariable = $expr->compile($compilationContext); - /* + /** * Only dynamic variables can be used as arrays */ switch ($exprVariable->getType()) { @@ -109,18 +109,16 @@ public function compile($expression, CompilationContext $compilationContext) throw new CompilerException('Cannot use expression: '.$exprVariable->getType().' as an array', $expression['left']); } - /* + /** * Resolve the dimension according to variable's type */ switch ($variableVariable->getType()) { - case 'variable': - return $this->_accessDimensionArray($expression, $variableVariable, $compilationContext); - case 'array': - return $this->_accessDimensionArray($expression, $variableVariable, $compilationContext); + case 'variable': + return $this->accessDimensionArray($expression, $variableVariable, $compilationContext); case 'string': - return $this->_accessStringOffset($expression, $variableVariable, $compilationContext); + return $this->accessStringOffset($expression, $variableVariable, $compilationContext); } } @@ -128,8 +126,13 @@ public function compile($expression, CompilationContext $compilationContext) * @param array $expression * @param Variable $variableVariable * @param CompilationContext $compilationContext + * + * @return CompiledExpression + * + * @throws ReflectionException + * @throws Exception */ - protected function _accessStringOffset($expression, Variable $variableVariable, CompilationContext $compilationContext) + protected function accessStringOffset(array $expression, Variable $variableVariable, CompilationContext $compilationContext) { if ($this->expecting) { if ($this->expectingVariable) { @@ -189,7 +192,7 @@ protected function _accessStringOffset($expression, Variable $variableVariable, * * @return CompiledExpression */ - protected function _accessDimensionArray($expression, Variable $variableVariable, CompilationContext $compilationContext) + protected function accessDimensionArray($expression, Variable $variableVariable, CompilationContext $compilationContext) { $arrayAccess = $expression; @@ -198,7 +201,7 @@ protected function _accessDimensionArray($expression, Variable $variableVariable throw new CompilerException('Cannot use non-initialized variable as an array', $arrayAccess['left']); } - /* + /** * Trying to use a non-object dynamic variable as object */ if ($variableVariable->hasDifferentDynamicType(['undefined', 'array', 'null'])) { @@ -217,7 +220,7 @@ protected function _accessDimensionArray($expression, Variable $variableVariable if ($this->readOnly) { if ($this->expecting && $this->expectingVariable) { - /* + /** * If a variable is assigned once in the method, we try to promote it * to a read only variable */ @@ -234,7 +237,7 @@ protected function _accessDimensionArray($expression, Variable $variableVariable } } - /* + /** * Variable is not read-only or it wasn't promoted */ if (!$readOnly) { @@ -250,11 +253,11 @@ protected function _accessDimensionArray($expression, Variable $variableVariable } } else { if ($this->expecting && $this->expectingVariable) { - /* + /** * If a variable is assigned once in the method, we try to promote it * to a read only variable */ - if ('return_value' != $symbolVariable->getName()) { + if ('return_value' !== $symbolVariable->getName()) { $line = $compilationContext->symbolTable->getLastCallLine(); if (false === $line || ($line > 0 && $line < $expression['line'])) { $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName()); @@ -267,7 +270,7 @@ protected function _accessDimensionArray($expression, Variable $variableVariable } } - /* + /** * Variable is not read-only or it wasn't promoted */ if (!$readOnly) { @@ -283,30 +286,22 @@ protected function _accessDimensionArray($expression, Variable $variableVariable } } - /* + /** * Variable that receives property accesses must be polymorphic */ if (!$symbolVariable->isVariable()) { throw new CompilerException('Cannot use variable: '.$symbolVariable->getType().' to assign array index', $expression); } - /* + /** * At this point, we don't know the type fetched from the index */ $symbolVariable->setDynamicTypes('undefined'); if ($this->readOnly || $readOnly) { - if ($this->noisy) { - $flags = 'PH_NOISY | PH_READONLY'; - } else { - $flags = 'PH_READONLY'; - } + $flags = $this->noisy ? 'PH_NOISY | PH_READONLY' : 'PH_READONLY'; } else { - if ($this->noisy) { - $flags = 'PH_NOISY'; - } else { - $flags = '0'; - } + $flags = $this->noisy ? 'PH_NOISY' : '0'; } /** @@ -315,9 +310,11 @@ protected function _accessDimensionArray($expression, Variable $variableVariable $expr = new Expression($arrayAccess['right']); $exprIndex = $expr->compile($compilationContext); $compilationContext->headersManager->add('kernel/array'); - if ('variable' == $exprIndex->getType()) { + + if ('variable' === $exprIndex->getType()) { $exprIndex = $compilationContext->symbolTable->getVariableForRead($exprIndex->getCode(), $compilationContext, $expression); } + $compilationContext->backend->arrayFetch($symbolVariable, $variableVariable, $exprIndex, $flags, $arrayAccess, $compilationContext); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); diff --git a/Library/FunctionCall.php b/Library/FunctionCall.php index 49a1d931c3..e7c9ec1d9a 100644 --- a/Library/FunctionCall.php +++ b/Library/FunctionCall.php @@ -15,9 +15,7 @@ use Zephir\Optimizers\OptimizerAbstract; /** - * Zephir\FunctionCall. - * - * Call functions. By default functions are called in the PHP userland if an optimizer + * Call functions. By default, functions are called in the PHP userland if an optimizer * was not found or there is not a user-handler for it */ class FunctionCall extends Call @@ -388,7 +386,6 @@ protected function _callNormal(array $expression, CompilationContext $compilatio } $exists = $this->functionExists($funcName, $compilationContext); - if (!$exists) { $compilationContext->logger->warning( sprintf('Function "%s" does not exist at compile time', $funcName), @@ -396,7 +393,7 @@ protected function _callNormal(array $expression, CompilationContext $compilatio ); } - /* + /** * Static variables can be passed using local variables saving memory if the function is read only */ if ($exists) { @@ -405,7 +402,7 @@ protected function _callNormal(array $expression, CompilationContext $compilatio $readOnly = false; } - /* + /** * Resolve parameters */ if (isset($expression['parameters'])) { @@ -418,14 +415,14 @@ protected function _callNormal(array $expression, CompilationContext $compilatio $params = []; } - /* + /** * Some functions receive parameters as references * We mark those parameters temporary as references to properly pass them */ $this->markReferences($funcName, $params, $compilationContext, $references, $expression); $codePrinter = $compilationContext->codePrinter; - /* + /** * Process the expected symbol to be returned */ $this->processExpectedObservedReturn($compilationContext); @@ -443,19 +440,19 @@ protected function _callNormal(array $expression, CompilationContext $compilatio ); } - /* + /** * We don't know the exact dynamic type returned by the method call */ $symbolVariable->setDynamicTypes('undefined'); $symbol = $compilationContext->backend->getVariableCodePointer($symbolVariable); } - /* + /** * Include fcall header */ $compilationContext->headersManager->add('kernel/fcall'); - /* + /** * Call functions must grown the stack */ $compilationContext->symbolTable->mustGrownStack(true); @@ -464,9 +461,9 @@ protected function _callNormal(array $expression, CompilationContext $compilatio * Check if the function can have an inline cache. */ $functionCache = $compilationContext->cacheManager->getFunctionCache(); - $cachePointer = $functionCache->get($funcName, $compilationContext, $this, $exists); + $cachePointer = $functionCache->get($funcName, $compilationContext, $exists); - /* + /** * Add the last call status to the current symbol table */ $this->addCallStatusFlag($compilationContext); @@ -525,7 +522,7 @@ protected function _callNormal(array $expression, CompilationContext $compilatio } } - /* + /** * Temporary variables must be copied if they have more than one reference */ foreach ($this->getMustCheckForCopyVariables() as $checkVariable) { @@ -543,7 +540,7 @@ protected function _callNormal(array $expression, CompilationContext $compilatio $this->addCallStatusOrJump($compilationContext); - /* + /** * We can mark temporary variables generated as idle */ foreach ($this->getTemporalVariables() as $tempVariable) { @@ -585,7 +582,7 @@ protected function _callDynamic(array $expression, CompilationContext $compilati ); } - /* + /** * Resolve parameters */ if (isset($expression['parameters'])) { @@ -596,7 +593,7 @@ protected function _callDynamic(array $expression, CompilationContext $compilati $codePrinter = $compilationContext->codePrinter; - /* + /** * Process the expected symbol to be returned */ $this->processExpectedObservedReturn($compilationContext); @@ -614,23 +611,23 @@ protected function _callDynamic(array $expression, CompilationContext $compilati ); } - /* + /** * We don't know the exact dynamic type returned by the method call */ $symbolVariable->setDynamicTypes('undefined'); } - /* + /** * Include fcall header */ $compilationContext->headersManager->add('kernel/fcall'); - /* + /** * Add the last call status to the current symbol table */ $this->addCallStatusFlag($compilationContext); - /* + /** * Call functions must grown the stack */ $compilationContext->symbolTable->mustGrownStack(true); @@ -680,7 +677,7 @@ protected function _callDynamic(array $expression, CompilationContext $compilati } } - /* + /** * Temporary variables must be copied if they have more than one reference */ foreach ($this->getMustCheckForCopyVariables() as $checkVariable) { @@ -689,7 +686,7 @@ protected function _callDynamic(array $expression, CompilationContext $compilati $this->addCallStatusOrJump($compilationContext); - /* + /** * We can mark temporary variables generated as idle */ foreach ($this->getTemporalVariables() as $tempVariable) { diff --git a/Library/Types/StringType.php b/Library/Types/StringType.php index bd2cd40ecb..b18a7d5539 100644 --- a/Library/Types/StringType.php +++ b/Library/Types/StringType.php @@ -40,7 +40,6 @@ class StringType extends AbstractType 'parsecsv' => 'str_getcsv', 'parsejson' => 'json_decode', 'tojson' => 'json_encode', - 'toutf8' => 'utf8_encode', 'repeat' => 'str_repeat', 'shuffle' => 'str_shuffle', 'split' => 'str_split', diff --git a/Library/Variable/Globals.php b/Library/Variable/Globals.php index 1cc80cf289..22b2a4669f 100644 --- a/Library/Variable/Globals.php +++ b/Library/Variable/Globals.php @@ -9,6 +9,8 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir\Variable; /** diff --git a/Library/Zephir.php b/Library/Zephir.php index 52ae64c3d8..31a6a4b97c 100644 --- a/Library/Zephir.php +++ b/Library/Zephir.php @@ -9,11 +9,10 @@ * the LICENSE file that was distributed with this source code. */ +declare(strict_types=1); + namespace Zephir; -/** - * Zephir\Zephir. - */ final class Zephir { public const VERSION = '0.16.0-$Id$'; diff --git a/Library/functions.php b/Library/functions.php index 2d8d8a5e49..910128f3e3 100644 --- a/Library/functions.php +++ b/Library/functions.php @@ -217,18 +217,6 @@ function is_macos() return 0 === stripos(PHP_OS, 'DARWIN'); } -/** - * Checks if currently running under BSD based OS. - * - * @see https://en.wikipedia.org/wiki/List_of_BSD_operating_systems - * - * @return bool - */ -function is_bsd() -{ - return false !== stripos(PHP_OS, 'BSD'); -} - /** * Checks if current PHP is thread safe. * diff --git a/ext/config.m4 b/ext/config.m4 index f654c649f3..c1fb9625c7 100644 --- a/ext/config.m4 +++ b/ext/config.m4 @@ -215,6 +215,7 @@ if test "$PHP_STUB" = "yes"; then stub/typeoff.zep.c stub/types/maybe.zep.c stub/types/mixedtype.zep.c + stub/types/obj.zep.c stub/unknownclass.zep.c stub/unsettest.zep.c stub/usetest.zep.c diff --git a/ext/config.w32 b/ext/config.w32 index cea2bd118b..505cec6072 100644 --- a/ext/config.w32 +++ b/ext/config.w32 @@ -38,7 +38,7 @@ if (PHP_STUB != "no") { ADD_SOURCES(configure_module_dirname + "/stub/requires", "external3.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/router", "exception.zep.c route.zep.c", "stub"); ADD_SOURCES(configure_module_dirname + "/stub/typehinting", "testabstract.zep.c", "stub"); - ADD_SOURCES(configure_module_dirname + "/stub/types", "maybe.zep.c mixedtype.zep.c", "stub"); + ADD_SOURCES(configure_module_dirname + "/stub/types", "maybe.zep.c mixedtype.zep.c obj.zep.c", "stub"); ADD_FLAG("CFLAGS_STUB", "/D ZEPHIR_RELEASE /Oi /Ot /Oy /Ob2 /Gs /GF /Gy /GL"); ADD_FLAG("CFLAGS", "/D ZEPHIR_RELEASE /Oi /Ot /Oy /Ob2 /Gs /GF /Gy /GL"); ADD_FLAG("LDFLAGS", "/LTCG"); diff --git a/ext/stub.c b/ext/stub.c index aa6294f0a7..23752125f1 100644 --- a/ext/stub.c +++ b/ext/stub.c @@ -243,6 +243,7 @@ zend_class_entry *stub_typeinstances_ce; zend_class_entry *stub_typeoff_ce; zend_class_entry *stub_types_maybe_ce; zend_class_entry *stub_types_mixedtype_ce; +zend_class_entry *stub_types_obj_ce; zend_class_entry *stub_unknownclass_ce; zend_class_entry *stub_unsettest_ce; zend_class_entry *stub_usetest_ce; @@ -473,6 +474,7 @@ static PHP_MINIT_FUNCTION(stub) ZEPHIR_INIT(Stub_Typeoff); ZEPHIR_INIT(Stub_Types_MayBe); ZEPHIR_INIT(Stub_Types_MixedType); + ZEPHIR_INIT(Stub_Types_Obj); ZEPHIR_INIT(Stub_UnknownClass); ZEPHIR_INIT(Stub_Unsettest); ZEPHIR_INIT(Stub_UseTest); diff --git a/ext/stub.h b/ext/stub.h index 942bbaba88..48be14ef4f 100644 --- a/ext/stub.h +++ b/ext/stub.h @@ -210,6 +210,7 @@ #include "stub/typeoff.zep.h" #include "stub/types/maybe.zep.h" #include "stub/types/mixedtype.zep.h" +#include "stub/types/obj.zep.h" #include "stub/unknownclass.zep.h" #include "stub/unsettest.zep.h" #include "stub/usetest.zep.h" diff --git a/ext/stub/types/obj.zep.c b/ext/stub/types/obj.zep.c new file mode 100644 index 0000000000..d2b119f0ca --- /dev/null +++ b/ext/stub/types/obj.zep.c @@ -0,0 +1,43 @@ + +#ifdef HAVE_CONFIG_H +#include "../../ext_config.h" +#endif + +#include +#include "../../php_ext.h" +#include "../../ext.h" + +#include +#include +#include + +#include "kernel/main.h" +#include "kernel/object.h" + + +ZEPHIR_INIT_CLASS(Stub_Types_Obj) +{ + ZEPHIR_REGISTER_CLASS(Stub\\Types, Obj, stub, types_obj, stub_types_obj_method_entry, 0); + + return SUCCESS; +} + +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnNull) +{ + zval *this_ptr = getThis(); + + + + RETURN_NULL(); +} + +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnObj) +{ + zval *this_ptr = getThis(); + + + + object_init(return_value); + return; +} + diff --git a/ext/stub/types/obj.zep.h b/ext/stub/types/obj.zep.h new file mode 100644 index 0000000000..6854113fa9 --- /dev/null +++ b/ext/stub/types/obj.zep.h @@ -0,0 +1,27 @@ + +extern zend_class_entry *stub_types_obj_ce; + +ZEPHIR_INIT_CLASS(Stub_Types_Obj); + +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnNull); +PHP_METHOD(Stub_Types_Obj, nullableObjectReturnObj); + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stub_types_obj_nullableobjectreturnnull, 0, 0, MAY_BE_NULL|MAY_BE_OBJECT) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_obj_nullableobjectreturnnull, 0, 0, IS_OBJECT, 1) +#endif +ZEND_END_ARG_INFO() + +#if PHP_VERSION_ID >= 80000 +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_stub_types_obj_nullableobjectreturnobj, 0, 0, MAY_BE_NULL|MAY_BE_OBJECT) +#else +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_stub_types_obj_nullableobjectreturnobj, 0, 0, IS_OBJECT, 1) +#endif +ZEND_END_ARG_INFO() + +ZEPHIR_INIT_FUNCS(stub_types_obj_method_entry) { + PHP_ME(Stub_Types_Obj, nullableObjectReturnNull, arginfo_stub_types_obj_nullableobjectreturnnull, ZEND_ACC_PUBLIC) + PHP_ME(Stub_Types_Obj, nullableObjectReturnObj, arginfo_stub_types_obj_nullableobjectreturnobj, ZEND_ACC_PUBLIC) + PHP_FE_END +}; diff --git a/stub/types/obj.zep b/stub/types/obj.zep new file mode 100644 index 0000000000..65134b3eef --- /dev/null +++ b/stub/types/obj.zep @@ -0,0 +1,15 @@ + +namespace Stub\Types; + +class Obj +{ + public function nullableObjectReturnNull() -> object | null + { + return null; + } + + public function nullableObjectReturnObj() -> object | null + { + return new \stdClass(); + } +} diff --git a/tests/Extension/Types/ObjTypeTest.php b/tests/Extension/Types/ObjTypeTest.php new file mode 100644 index 0000000000..0cab73519d --- /dev/null +++ b/tests/Extension/Types/ObjTypeTest.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view + * the LICENSE file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Extension\Types; + +use PHPUnit\Framework\TestCase; +use stdClass; +use Stub\Types\Obj; + +final class ObjTypeTest extends TestCase +{ + public function testIntFalse(): void + { + $class = new Obj(); + + $this->assertNull($class->nullableObjectReturnNull()); + $this->assertInstanceOf(stdClass::class, $class->nullableObjectReturnObj()); + } +}