Transforms calls to method "join" to function calls to "join".

@param object             $caller
@param CompilationContext $compilationContext
@param Call               $call
@param array              $expression

@return bool|\Zephir\CompiledExpression
172194
public function join($caller, CompilationContext $compilationContext, Call $call, array $expression)
    {
        $functionCall = BuilderFactory::getInstance()->statements()
            ->functionCall('join', $expression['parameters'])
            ->addArgument($caller)
            ->setFile($expression['file'])
            ->setLine($expression['line'])
            ->setChar($expression['char']);

        $expression = new Expression($functionCall->build());

        return $expression->compile($compilationContext);
    }
1109979
public function compile(array $expression, CompilationContext $compilationContext)
    {
        $expr = new Expression($expression['left']);
        $expr->setReadOnly(true);
        $expr->setExpectReturn(true);

        $exprPath = $expr->compile($compilationContext);
        if ('variable' == $exprPath->getType()) {
            $exprVariable = $compilationContext->symbolTable->getVariableForRead($exprPath->getCode(), $compilationContext, $expression);
            $exprVar = $compilationContext->backend->getVariableCode($exprVariable);
            if ('variable' == $exprVariable->getType()) {
                if ($exprVariable->hasDifferentDynamicType(['undefined', 'string'])) {
                    $compilationContext->logger->warning(
                        'Possible attempt to use invalid type as path in "require" operator',
                        ['non-valid-require', $expression]
                    );
                }
            }
        } else {
            $exprVar = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
            $compilationContext->backend->assignString($exprVar, $exprPath->getCode(), $compilationContext);
            $exprVar = $compilationContext->backend->getVariableCode($exprVar);
        }

        $symbolVariable = false;
        if ($this->isExpecting()) {
            $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression);
        }

        $compilationContext->headersManager->add('kernel/memory');
        $compilationContext->headersManager->add('kernel/require');

        $codePrinter = $compilationContext->codePrinter;

        if ($symbolVariable) {
            $codePrinter->output('ZEPHIR_OBSERVE_OR_NULLIFY_PPZV(&'.$symbolVariable->getName().');');
            $symbol = $compilationContext->backend->getVariableCodePointer($symbolVariable);
            $codePrinter->output('if (zephir_require_zval_ret('.$symbol.', '.$exprVar.' TSRMLS_CC) == FAILURE) {');
        } else {
            $codePrinter->output('if (zephir_require_zval('.$exprVar.' TSRMLS_CC) == FAILURE) {');
        }
        $codePrinter->output("\t".'RETURN_MM_NULL();');
        $codePrinter->output('}');

        if ($symbolVariable) {
            return new CompiledExpression('variable', $symbolVariable->getName(), $expression);
        }

        return new CompiledExpression('null', null, $expression);
    }
451930
public function when($condition, callable $call, callable $notCall = null)
    {
        if (is_callable($condition)) {
            $result = call_user_func($condition);
        } else {
            $result = $condition;
        }
        if ($result) {
            call_user_func_array($call, [$this]);
        } else {
            if ($notCall) {
                call_user_func_array($notCall, [$this]);
            }
        }
        return $this;
    }
592852
public function optimize(array $expression, Call $call, CompilationContext $context)
    {
        if (!isset($expression['parameters'])) {
            return false;
        }

        if (1 != \count($expression['parameters'])) {
            return false;
        }

        $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);

        return new CompiledExpression('bool', 'zephir_is_scalar('.$resolvedParams[0].')', $expression);
    }
719318
public function compile($expression, CompilationContext $compilationContext)
    {
        /*
         * This variable is used to check if the compound and expression is evaluated as true or false:
         * Ensure that newly allocated variables are local-only (setReadOnly)
         */
        $this->setReadOnly(false);
        $returnVariable = $this->getExpected($compilationContext, $expression, false);
        /* Make sure that passed variables (passed symbol variables) are promoted */
        $returnVariable->setLocalOnly(false);

        if ('variable' != $returnVariable->getType() || 'return_value' == $returnVariable->getName()) {
            $returnVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext);
            if ($returnVariable->isTemporal()) {
                $returnVariable->skipInitVariant(2);
            }
        }

        $ifBuilder = new IfStatementBuilder(
            new UnaryOperatorBuilder(
                'not',
                $expression['left']
            ),
            new StatementsBlockBuilder([
                /*
                 * Create an implicit 'let' operation to update the evaluated right operator
                 */
                new LetStatementBuilder([
                    'assign-type' => 'variable',
                    'variable' => $returnVariable->getName(),
                    'operator' => 'assign',
                    'expr' => $expression['extra'],
                    'file' => $expression['file'],
                    'line' => $expression['line'],
                    'char' => $expression['char'],
                ], $expression['extra']),
            ]),
            new StatementsBlockBuilder([
                /*
                 * Create an implicit 'let' operation to update the evaluated right operator
                 */
                new LetStatementBuilder([
                    'assign-type' => 'variable',
                    'variable' => $returnVariable->getName(),
                    'operator' => 'assign',
                    'expr' => $expression['left'],
                    'file' => $expression['file'],
                    'line' => $expression['line'],
                    'char' => $expression['char'],
                ], $expression['extra']),
            ])
        );

        $ifStatement = new IfStatement($ifBuilder->get());
        $ifStatement->compile($compilationContext);

        return new CompiledExpression('variable', $returnVariable->getName(), $expression);
    }
1317114
public function compile(array $expression, CompilationContext $compilationContext)
    {
        $classDefinition = new ClassDefinition(
            $compilationContext->config->get('namespace'),
            self::$id.'__closure'
        );

        $classDefinition->setIsFinal(true);

        $compilerFile = new CompilerFileAnonymous($classDefinition, $compilationContext->config, $compilationContext);
        $compilerFile->setLogger($compilationContext->logger);

        $compilationContext->compiler->addClassDefinition($compilerFile, $classDefinition);

        if (isset($expression['left'])) {
            $parameters = new ClassMethodParameters($expression['left']);
        } else {
            $parameters = null;
        }

        if (isset($expression['right'])) {
            $block = $expression['right'];
        } else {
            $block = [];
        }

        $staticVariables = [];
        if (isset($expression['use']) && \is_array($expression['use'])) {
            foreach ($expression['use'] as $parameter) {
                $staticVariables[$parameter['name']] = $compilationContext->symbolTable->getVariable($parameter['name']);
            }
        }

        foreach ($staticVariables as $var) {
            $classDefinition->addProperty(new \Zephir\ClassProperty(
                $classDefinition,
                ['public', 'static'],
                $var->getName(),
                null,
                null,
                null
            ));
        }

        $classMethod = new ClassMethod(
            $classDefinition,
            ['public', 'final'],
            '__invoke',
            $parameters,
            new StatementsBlock($block),
            null,
            null,
            $expression,
            $staticVariables
        );
        $classDefinition->addMethod($classMethod, $block);

        $compilationContext->headersManager->add('kernel/object');

        if ($this->expecting) {
            if ($this->expectingVariable) {
                $symbolVariable = $this->expectingVariable;
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
            }
        } else {
            $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
        }

        $symbolVariable->initVariant($compilationContext);
        $compilationContext->backend->createClosure($symbolVariable, $classDefinition, $compilationContext);
        $compilationContext->headersManager->add('kernel/object');
        foreach ($staticVariables as $var) {
            if ('variable' == $var->getType() || 'array' == $var->getType()) {
                $compilationContext->backend->updateStaticProperty($classDefinition->getClassEntry(), $var->getName(), $var, $compilationContext);
            } else {
                $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true);
                switch ($var->getType()) {
                    case 'int':
                    case 'uint':
                    case 'long':
                    case 'ulong':
                    case 'char':
                    case 'uchar':
                        $compilationContext->backend->assignLong($tempVariable, $var, $compilationContext);
                        break;
                    case 'double':
                        $compilationContext->backend->assignDouble($tempVariable, $var, $compilationContext);
                        break;
                    case 'bool':
                        $compilationContext->backend->assignBool($tempVariable, $var, $compilationContext);
                        break;
                    case 'string':
                        $compilationContext->backend->assignString($tempVariable, $var, $compilationContext);
                        break;
                    default:
                        break;
                }
                $compilationContext->backend->updateStaticProperty($classDefinition->getClassEntry(), $var->getName(), $tempVariable, $compilationContext);
            }
        }
        ++self::$id;

        return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
    }
1298380
public function rightJoin($table, callable $callable)
    {
        $table = $this->getPrefix().$table;

        if (is_null($this->join)) {
            $this->join = 'right join `'.$table.'`';

            if (is_callable($callable)) {
                $callable($this);
            }

            return $this;
        }

        if (!preg_match('/^(inner|left)\sjoin\s.*/', $this->join)) {
            $this->join .= ', `'.$table.'`';

            if (is_callable($callable)) {
                $callable($this);
            }

            return $this;
        }

        throw new QueryBuilderException(
            'The inner join clause is already initialized.',
            E_ERROR
        );
    }
426291
public function processExpectedReturn(CompilationContext $compilationContext)
    {
        $expr = $this->expression;
        $expression = $expr->getExpression();

        /**
         * Create temporary variable if needed.
         */
        $mustInit = false;
        $symbolVariable = null;
        $isExpecting = $expr->isExpectingReturn();
        if ($isExpecting) {
            $symbolVariable = $expr->getExpectingVariable();
            if (\is_object($symbolVariable)) {
                $readDetector = new ReadDetector();
                if ($readDetector->detect($symbolVariable->getName(), $expression)) {
                    $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite(
                        'variable',
                        $compilationContext,
                        $expression
                    );
                } else {
                    $mustInit = true;
                }
            } else {
                $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression);
            }
        }

        $this->mustInit = $mustInit;
        $this->symbolVariable = $symbolVariable;
        $this->isExpecting = $isExpecting;
    }
137608
public function compile(CompilationContext $compilationContext)
    {
        $exprRaw = $this->statement['expr'];

        /*
         * @TODO implement optimizers here
         */
        if ('fcall' == $exprRaw['type']) {
            if ('range' == $exprRaw['name']) {
                $status = $this->compileRange($exprRaw, $compilationContext);
                if (false !== $status) {
                    return;
                }
            }

            if ('iterator' == $exprRaw['name']) {
                $status = $this->compileIterator($exprRaw, $compilationContext);
                if (false !== $status) {
                    return;
                }
            }
        }

        $expr = new Expression($exprRaw);
        $expr->setReadOnly(true);
        $expression = $expr->compile($compilationContext);

        /*
         * Check for traversing a constant string
         */
        if ('string' == $expression->getType()) {
            $this->compileStringTraverse($expression, $compilationContext, null);

            return;
        }

        if ('variable' != $expression->getType() && 'array' != $expression->getType()) {
            throw new CompilerException('Unknown type: '.$expression->getType(), $exprRaw);
        }

        $exprVariable = $compilationContext->symbolTable->getVariableForRead($expression->getCode(), $compilationContext, $this->statement['expr']);
        switch ($exprVariable->getType()) {
            case 'variable':
            case 'array':
                $this->compileHashTraverse($expression, $compilationContext, $exprVariable);
                break;

            case 'string':
                $this->compileStringTraverse($expression, $compilationContext, $exprVariable);
                break;

            default:
                throw new CompilerException('Cannot traverse value type: '.$exprVariable->getType(), $exprRaw);
        }
    }
191200
public function callParty($caller, $callee, $args)
    {
      $new_call = Call::create($caller, $callee, $this->id, $args);
      $this->calls ++; 

      return Constructor::Make($this, $data->get());
    }
928863
private function invoke($pattern, callable $call)
    {
        $pattern = is_array($pattern) ? $pattern : [$pattern];
        foreach ($pattern as $val) {
            $this->setRoute('INVOKE', [$val, $call]);
        }
        return $this;
    }
1062255
public function compile(CompilationContext $compilationContext)
    {
        $expression = [
            'type' => 'require',
            'left' => $this->statement['expr'],
            'file' => $this->statement['file'],
            'line' => $this->statement['line'],
            'char' => $this->statement['char'],
        ];

        $expr = new Expression($expression);
        $expr->setExpectReturn(false, null);
        $expr->compile($compilationContext);
    }
359647
public function optimize(array $expression, Call $call, CompilationContext $context)
    {
        if (!isset($expression['parameters'])) {
            return false;
        }

        if (\count($expression['parameters']) > 1) {
            return false;
        }

        /*
         * Resolve parameters as vars
         */
        $call->getResolvedParams($expression['parameters'], $context, $expression);
        /**
         * Get CompiledExpression(s) for resolved var(s).
         */
        $resolvedParams = $call->getResolvedParamsAsExpr($expression['parameters'], $context, $expression);
        $compiledExpression = $resolvedParams[0];

        switch ($compiledExpression->getType()) {
            case 'int':
            case 'float':
            case 'long':
            case 'ulong':
            case 'double':
                $context->headersManager->add('math');

                return $this->passNativeFCall($compiledExpression, $expression);
                break;
            case 'variable':
                $variable = $context->symbolTable->getVariable($compiledExpression->getCode());
                switch ($variable->getType()) {
                    case 'int':
                    case 'float':
                    case 'long':
                    case 'ulong':
                    case 'double':
                        $context->headersManager->add('math');

                        return $this->passNativeFCall($compiledExpression, $expression);
                        break;
                    case 'variable':
                        $context->headersManager->add('kernel/math');

                        return new CompiledExpression(
                            'double',
                            'zephir_'.$this->getFunctionName().'('.$context->backend->getVariableCode($variable).' TSRMLS_CC)',
                            $expression
                        );
                        break;
                }
                break;
        }

        return false;
    }
663163
public function optimize(array $expression, Call $call, CompilationContext $context)
    {
        if (!isset($expression['parameters'])) {
            return false;
        }

        /*
         * Process the expected symbol to be returned
         */
        $call->processExpectedReturn($context);

        $symbolVariable = $call->getSymbolVariable();
        if ($symbolVariable) {
            if (!$symbolVariable->isVariable()) {
                throw new CompilerException('Returned values by functions can only be assigned to variant variables', $expression);
            }
        }

        $context->headersManager->add('kernel/variables');
        $resolvedParams = $call->getResolvedParamsAsExpr($expression['parameters'], $context, $expression);
        $resolvedParam = $resolvedParams[0];

        if (!$symbolVariable || !$symbolVariable->isVariable()) {
            /*
             * Complex expressions require a temporary variable
             */
            switch ($resolvedParam->getType()) {
                case 'array':
                    $type = 'array';
                    break;
                default:
                    $type = 'variable';
                    break;
            }

            $variable = $context->symbolTable->addTemp($type, $context);
            $variable->initVariant($context);

            $statement = new LetStatement([
                'type' => 'let',
                'assignments' => [
                    [
                        'assign-type' => $type,
                        'variable' => $variable->getName(),
                        'operator' => 'assign',
                        'expr' => [
                            'type' => $resolvedParam->getType(),
                            'value' => $resolvedParam->getCode(),
                            'file' => $expression['file'],
                            'line' => $expression['line'],
                            'char' => $expression['char'],
                        ],
                        'file' => $expression['file'],
                        'line' => $expression['line'],
                        'char' => $expression['char'],
                    ],
                ],
            ]);
            $statement->compile($context);
        } else {
            /**
             * This mark the variable as used.
             */
            $variable = $context->symbolTable->getVariableForRead($resolvedParam->getCode(), $context, $expression);
        }
        $variableSymbol = $context->backend->getVariableCodePointer($variable);

        /*
         * let a = var_export(val);
         */
        if ($symbolVariable) {
            if ('return_value' == $symbolVariable->getName()) {
                $symbolVariable = $context->symbolTable->getTempVariableForWrite('variable', $context);
            } else {
                $symbolVariable->initVariant($context);
            }
            $symbol = $context->backend->getVariableCode($symbolVariable);
            $context->codePrinter->output('zephir_var_export_ex('.$symbol.', '.$variableSymbol.' TSRMLS_CC);');

            return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
        }

        $context->codePrinter->output('zephir_var_export('.$variableSymbol.' TSRMLS_CC);');

        return new CompiledExpression('null', 'null', $expression);
    }
357221
public function makeCallerLockAware(Caller $caller)
    {
        $lock = $this->caller($caller);

        $caller->setLock($lock);

        return $caller;
    }
424005
public function compile(CompilationContext $compilationContext)
    {
        /*
         * Set the method currently being compiled
         */
        $compilationContext->currentMethod = $this;

        /*
         * Initialize the method warm-up to null
         */
        $compilationContext->methodWarmUp = null;

        /**
         * Assign pre-made compilation passes.
         */
        $localContext = $this->localContext;
        $typeInference = $this->typeInference;
        $callGathererPass = $this->callGathererPass;

        /**
         * Initialization of parameters happens in a fictitious external branch.
         */
        $branch = new Branch();
        $branch->setType(Branch::TYPE_EXTERNAL);
        /**
         * BranchManager helps to create graphs of conditional/loop/root/jump branches.
         */
        $branchManager = new BranchManager();
        $branchManager->addBranch($branch);

        $compilationContext->branchManager = $branchManager;

        /**
         * Every method has its own symbol table.
         */
        $symbolTable = new SymbolTable($compilationContext);
        if ($localContext) {
            $symbolTable->setLocalContext($localContext);
        }

        if ($this->staticVariables) {
            foreach ($this->staticVariables as $var) {
                $localVar = clone $var;
                $localVar->setIsExternal(true);
                $localVar->setLocalOnly(true);
                $localVar->setType('variable');
                $symbolTable->addRawVariable($localVar);
            }
        }

        /**
         * Parameters has an additional extra mutation.
         */
        $parameters = $this->parameters;
        if ($localContext) {
            if (\is_object($parameters)) {
                foreach ($parameters->getParameters() as $parameter) {
                    $localContext->increaseMutations($parameter['name']);
                }
            }
        }

        /**
         * Cache Manager manages function calls, method calls and class entries caches.
         */
        $cacheManager = new CacheManager();
        $cacheManager->setGatherer($callGathererPass);

        $compilationContext->cacheManager = $cacheManager;
        $compilationContext->typeInference = $typeInference;
        $compilationContext->symbolTable = $symbolTable;

        $oldCodePrinter = $compilationContext->codePrinter;

        /**
         * Change the code printer to a single method instance.
         */
        $codePrinter = new CodePrinter();
        $compilationContext->codePrinter = $codePrinter;

        /*
         * Set an empty function cache
         */
        $compilationContext->functionCache = null;

        /*
         * Reset try/catch and loop counter
         */
        $compilationContext->insideCycle = 0;
        $compilationContext->insideTryCatch = 0;
        $compilationContext->currentTryCatch = 0;

        if (\is_object($parameters)) {
            /**
             * Round 1. Create variables in parameters in the symbol table.
             */
            $classCastChecks = [];
            $substituteVars = [];
            foreach ($parameters->getParameters() as $parameter) {
                /*
                 * Change dynamic variables to low level types
                 */
                if ($typeInference) {
                    if (isset($parameter['data-type'])) {
                        if ('variable' == $parameter['data-type']) {
                            $type = $typeInference->getInferedType($parameter['name']);
                            if (\is_string($type)) {
                                /* promote polymorphic parameters to low level types */
                            }
                        }
                    } else {
                        $type = $typeInference->getInferedType($parameter['name']);
                        if (\is_string($type)) {
                            /* promote polymorphic parameters to low level types */
                        }
                    }
                }

                $symbolParam = null;

                if (isset($parameter['data-type'])) {
                    switch ($parameter['data-type']) {
                        case 'object':
                        case 'callable':
                        case 'resource':
                        case 'variable':
                            $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                            /* TODO: Move this to the respective backend, which requires refactoring how this works */
                            if ($compilationContext->backend->isZE3()) {
                                $symbol->setIsDoublePointer(true);
                                $substituteVars[$parameter['name']] = $symbolTable->addVariable('variable', $parameter['name'].'_sub', $compilationContext);
                            }
                            break;

                        default:
                            $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext);
                            $symbolParam = $symbolTable->addVariable('variable', $parameter['name'].'_param', $compilationContext);
                            /* TODO: Move this to the respective backend, which requires refactoring how this works */
                            if ($compilationContext->backend->isZE3()) {
                                $symbolParam->setIsDoublePointer(true);
                            }
                            if ('string' == $parameter['data-type'] || 'array' == $parameter['data-type']) {
                                $symbol->setMustInitNull(true);
                            }
                            break;
                    }
                } else {
                    $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext);
                }

                /* ZE3 only */
                if (isset($substituteVars[$parameter['name']])) {
                    $substituteVar = $substituteVars[$parameter['name']];
                    $substituteVar->increaseUses();
                }

                /*
                 * Some parameters can be read-only
                 */
                if (isset($parameter['const']) && $parameter['const']) {
                    $symbol->setReadOnly(true);
                    if (\is_object($symbolParam)) {
                        $symbolParam->setReadOnly(true);
                    }
                }

                if (\is_object($symbolParam)) {
                    /*
                     * Parameters are marked as 'external'
                     */
                    $symbolParam->setIsExternal(true);

                    /*
                     * Assuming they're initialized
                     */
                    $symbolParam->setIsInitialized(true, $compilationContext);

                    /*
                     * Initialize auxiliary parameter zvals to null
                     */
                    $symbolParam->setMustInitNull(true);

                    /*
                     * Increase uses
                     */
                    $symbolParam->increaseUses();
                } else {
                    if (isset($parameter['default'])) {
                        if (isset($parameter['data-type'])) {
                            if ('variable' == $parameter['data-type']) {
                                $symbol->setMustInitNull(true);
                            }
                        } else {
                            $symbol->setMustInitNull(true);
                        }
                    }
                }

                /*
                 * Original node where the variable was declared
                 */
                $symbol->setOriginal($parameter);

                /*
                 * Parameters are marked as 'external'
                 */
                $symbol->setIsExternal(true);

                /*
                 * Assuming they're initialized
                 */
                $symbol->setIsInitialized(true, $compilationContext);

                /*
                 * Variables with class/type must be objects across the execution
                 */
                if (isset($parameter['cast'])) {
                    $symbol->setDynamicTypes('object');
                    $symbol->setClassTypes($compilationContext->getFullName($parameter['cast']['value']));
                    $classCastChecks[] = [$symbol, $parameter];
                } else {
                    if (isset($parameter['data-type'])) {
                        if ('variable' == $parameter['data-type']) {
                            $symbol->setDynamicTypes('undefined');
                        }
                    } else {
                        $symbol->setDynamicTypes('undefined');
                    }
                }
            }
        }

        $compilationContext->backend->onPreCompile($this, $compilationContext);

        /*
         * Compile the block of statements if any
         */
        if (\is_object($this->statements)) {
            if ($this->hasModifier('static')) {
                $compilationContext->staticContext = true;
            } else {
                $compilationContext->staticContext = false;
            }

            /*
             * Compile the statements block as a 'root' branch
             */
            $this->statements->compile($compilationContext, false, Branch::TYPE_ROOT);
        }

        /**
         * Initialize variable default values.
         */
        $initVarCode = $compilationContext->backend->initializeVariableDefaults($symbolTable->getVariables(), $compilationContext);

        /**
         * Fetch parameters from vm-top.
         */
        $initCode = '';
        $code = '';
        if (\is_object($parameters)) {
            /**
             * Round 2. Fetch the parameters in the method.
             */
            $params = [];
            $requiredParams = [];
            $optionalParams = [];
            $numberRequiredParams = 0;
            $numberOptionalParams = 0;
            foreach ($parameters->getParameters() as $parameter) {
                if (isset($parameter['data-type'])) {
                    $dataType = $parameter['data-type'];
                } else {
                    $dataType = 'variable';
                }

                switch ($dataType) {
                    case 'object':
                    case 'callable':
                    case 'resource':
                    case 'variable':
                        if (!$this->isInternal()) {
                            $params[] = '&'.$parameter['name'];
                        } else {
                            $params[] = $parameter['name'];
                        }
                        break;

                    default:
                        if (!$this->isInternal()) {
                            $params[] = '&'.$parameter['name'].'_param';
                        } else {
                            $params[] = $parameter['name'].'_param';
                        }
                        break;
                }

                if (isset($parameter['default'])) {
                    $optionalParams[] = $parameter;
                    ++$numberOptionalParams;
                } else {
                    $requiredParams[] = $parameter;
                    ++$numberRequiredParams;
                }
            }

            /**
             * Pass the write detector to the method statement block to check if the parameter
             * variable is modified so as do the proper separation.
             */
            $parametersToSeparate = [];
            if (\is_object($this->statements)) {
                /*
                 * If local context is not available
                 */
                if (!$localContext) {
                    $writeDetector = new WriteDetector();
                }

                foreach ($parameters->getParameters() as $parameter) {
                    if (isset($parameter['data-type'])) {
                        $dataType = $parameter['data-type'];
                    } else {
                        $dataType = 'variable';
                    }

                    switch ($dataType) {
                        case 'variable':
                        case 'string':
                        case 'array':
                        case 'resource':
                        case 'object':
                        case 'callable':
                            $name = $parameter['name'];
                            if (!$localContext) {
                                if ($writeDetector->detect($name, $this->statements->getStatements())) {
                                    $parametersToSeparate[$name] = true;
                                }
                            } else {
                                if ($localContext->getNumberOfMutations($name) > 1) {
                                    $parametersToSeparate[$name] = true;
                                }
                            }
                            break;
                    }
                }
            }

            /*
             * Initialize required parameters
             */
            foreach ($requiredParams as $parameter) {
                if (isset($parameter['mandatory'])) {
                    $mandatory = $parameter['mandatory'];
                } else {
                    $mandatory = 0;
                }

                if (isset($parameter['data-type'])) {
                    $dataType = $parameter['data-type'];
                } else {
                    $dataType = 'variable';
                }

                if ('variable' != $dataType) {
                    /*
                     * Assign value from zval to low level type
                     */
                    if ($mandatory) {
                        $initCode .= $this->checkStrictType($parameter, $compilationContext);
                    } else {
                        $initCode .= $this->assignZvalValue($parameter, $compilationContext);
                    }
                }

                switch ($dataType) {
                    case 'variable':
                    case 'resource':
                    case 'object':
                    case 'callable':
                        if (isset($parametersToSeparate[$parameter['name']])) {
                            $symbolTable->mustGrownStack(true);
                            $initCode .= "\t".'ZEPHIR_SEPARATE_PARAM('.$parameter['name'].');'.PHP_EOL;
                        }
                        break;
                }
            }

            /*
             * Initialize optional parameters
             */
            foreach ($optionalParams as $parameter) {
                if (isset($parameter['mandatory'])) {
                    $mandatory = $parameter['mandatory'];
                } else {
                    $mandatory = 0;
                }

                if (isset($parameter['data-type'])) {
                    $dataType = $parameter['data-type'];
                } else {
                    $dataType = 'variable';
                }

                switch ($dataType) {
                    case 'object':
                    case 'callable':
                    case 'resource':
                    case 'variable':
                        $name = $parameter['name'];
                        break;

                    default:
                        $name = $parameter['name'].'_param';
                        break;
                }

                /**
                 * Assign the default value according to the variable's type.
                 */
                $targetVar = $compilationContext->symbolTable->getVariableForWrite($name, $compilationContext);
                $initCode .= "\t".$compilationContext->backend->ifVariableValueUndefined($targetVar, $compilationContext, false, false).PHP_EOL;

                if ($compilationContext->backend->isZE3()) {
                    if ($targetVar->isDoublePointer() && isset($substituteVars[$parameter['name']])) {
                        $substituteVar = $substituteVars[$parameter['name']];
                        $initCode .= "\t\t".$targetVar->getName().' = &'.$substituteVar->getName().';'.PHP_EOL;
                    }
                }
                $initCode .= $this->assignDefaultValue($parameter, $compilationContext);

                if (isset($parametersToSeparate[$name]) || 'variable' != $dataType) {
                    $initCode .= "\t".'} else {'.PHP_EOL;
                    if (isset($parametersToSeparate[$name])) {
                        $initCode .= "\t\t".'ZEPHIR_SEPARATE_PARAM('.$name.');'.PHP_EOL;
                    } else {
                        if ($mandatory) {
                            $initCode .= $this->checkStrictType($parameter, $compilationContext, $mandatory);
                        } else {
                            $initCode .= "\t".$this->assignZvalValue($parameter, $compilationContext);
                        }
                    }
                }

                $initCode .= "\t".'}'.PHP_EOL;
            }

            /*
             * Fetch the parameters to zval pointers
             */
            $codePrinter->preOutputBlankLine();

            if (!$this->isInternal()) {
                $compilationContext->headersManager->add('kernel/memory');
                if ($symbolTable->getMustGrownStack()) {
                    $code .= "\t".'zephir_fetch_params(1, '.$numberRequiredParams.', '.$numberOptionalParams.', '.implode(', ', $params).');'.PHP_EOL;
                } else {
                    $code .= "\t".'zephir_fetch_params(0, '.$numberRequiredParams.', '.$numberOptionalParams.', '.implode(', ', $params).');'.PHP_EOL;
                }
            } else {
                foreach ($params as $param) {
                    /* TODO: Migrate all this code to codeprinter, get rid of temp code printer */
                    $tempCodePrinter = new CodePrinter();
                    $realCodePrinter = $compilationContext->codePrinter;
                    $compilationContext->codePrinter = $tempCodePrinter;
                    $paramVar = $compilationContext->symbolTable->getVariableForRead($param, $compilationContext);
                    $compilationContext->backend->assignZval($paramVar, $param.'_ext', $compilationContext);
                    $code .= "\t".$tempCodePrinter->getOutput().PHP_EOL;
                    $compilationContext->codePrinter = $realCodePrinter;
                }
            }
            $code .= PHP_EOL;
        }

        $code .= $initCode.$initVarCode;
        $codePrinter->preOutput($code);

        $compilationContext->headersManager->add('kernel/object');
        /*
         * Fetch used superglobals
         */
        foreach ($symbolTable->getVariables() as $name => $variable) {
            if ($variable->isSuperGlobal()) {
                $globalVar = $symbolTable->getVariable($name);
                $codePrinter->preOutput("\t".$compilationContext->backend->fetchGlobal($globalVar, $compilationContext, false));
            }
            if ($variable->isLocalSatic()) {
                $staticVar = $symbolTable->getVariable($name);

                $codePrinter->preOutput(sprintf(
                    "\t".'zephir_read_static_property_ce(%s%s, %s, SL("%s"), PH_NOISY_CC%s);',
                    $staticVar->isDoublePointer() ? '' : '&',
                    $staticVar->getName(),
                    $this->classDefinition->getClassEntry(),
                    $staticVar->getName(),
                    ''
                ));
            }
        }

        /*
         * Grow the stack if needed
         */
        if ($symbolTable->getMustGrownStack()) {
            $compilationContext->headersManager->add('kernel/memory');
            $codePrinter->preOutput("\t".'ZEPHIR_MM_GROW();');
        }

        /**
         * Check if there are unused variables.
         */
        $usedVariables = [];
        $classDefinition = $this->getClassDefinition();
        if ($classDefinition) {
            $completeName = $classDefinition->getCompleteName();
        } else {
            $completeName = '[unknown]';
        }

        foreach ($symbolTable->getVariables() as $variable) {
            if ($variable->getNumberUses() <= 0) {
                if (false == $variable->isExternal()) {
                    $compilationContext->logger->warning(
                        'Variable "'.$variable->getName().'" declared but not used in '.$completeName.'::'.$this->getName(),
                        ['unused-variable', $variable->getOriginal()]
                    );
                    continue;
                }
                $compilationContext->logger->warning(
                    'Variable "'.$variable->getName().'" declared but not used in '.$completeName.'::'.$this->getName(),
                    ['unused-variable-external', $variable->getOriginal()]
                );
            }

            if ('this_ptr' != $variable->getName() && 'return_value' != $variable->getName() && 'return_value_ptr' != $variable->getName()) {
                $type = $variable->getType();
                if (!isset($usedVariables[$type])) {
                    $usedVariables[$type] = [];
                }
                $usedVariables[$type][] = $variable;
            }
        }

        /*
         * Check if there are assigned but not used variables
         * Warn whenever a variable is unused aside from its declaration.
         */
        foreach ($symbolTable->getVariables() as $variable) {
            if (true == $variable->isExternal() || $variable->isTemporal()) {
                continue;
            }

            if ('this_ptr' == $variable->getName() || 'return_value' == $variable->getName() || 'return_value_ptr' == $variable->getName() || 'ZEPHIR_LAST_CALL_STATUS' == $variable->getName()) {
                continue;
            }

            if (!$variable->isUsed()) {
                $node = $variable->getLastUsedNode();
                if (\is_array($node)) {
                    $expression = isset($node['expr']) ? $node['expr'] : $node;
                    $compilationContext->logger->warning(
                        'Variable "'.$variable->getName().'" assigned but not used in '.$completeName.'::'.$this->getName(),
                        ['unused-variable', $expression]
                    );
                } else {
                    $compilationContext->logger->warning(
                        'Variable "'.$variable->getName().'" assigned but not used in '.$completeName.'::'.$this->getName(),
                        ['unused-variable', $variable->getOriginal()]
                    );
                }
            }
        }

        if (\count($usedVariables)) {
            $codePrinter->preOutputBlankLine();
        }

        /**
         * Generate the variable definition for variables used.
         */
        $initCode = "\t".implode(PHP_EOL."\t", $compilationContext->backend->declareVariables($this, $usedVariables, $compilationContext));
        if ($initCode) {
            $codePrinter->preOutput($initCode);
        }

        /*
         * Finalize the method compilation
         */
        if (\is_object($this->statements)) {
            /**
             * If the last statement is not a 'return' or 'throw' we need to
             * restore the memory stack if needed.
             */
            $lastType = $this->statements->getLastStatementType();

            if ('return' != $lastType && 'throw' != $lastType && !$this->hasChildReturnStatementType($this->statements->getLastStatement())) {
                if ($symbolTable->getMustGrownStack()) {
                    $compilationContext->headersManager->add('kernel/memory');
                    $codePrinter->output("\t".'ZEPHIR_MM_RESTORE();');
                }

                /*
                 * If a method has return-type hints we need to ensure the last statement is a 'return' statement
                 */
                if ($this->hasReturnTypes()) {
                    throw new CompilerException(
                        'Reached end of the method without returning a valid type specified in the return-type hints',
                        $this->expression['return-type']
                    );
                }
            }
        }

        $compilationContext->backend->onPostCompile($this, $compilationContext);

        /**
         * Remove macros that grow/restore the memory frame stack if it wasn't used.
         */
        $code = $this->removeMemoryStackReferences($symbolTable, $codePrinter->getOutput());

        /*
         * Restore the compilation context
         */
        $oldCodePrinter->output($code);
        $compilationContext->codePrinter = $oldCodePrinter;

        $compilationContext->branchManager = null;
        $compilationContext->cacheManager = null;
        $compilationContext->typeInference = null;

        $codePrinter->clear();

        return null;
    }
748439
private function processOptionals(array &$expression, Call $call, CompilationContext $context)
    {
        $flags = null;
        $offset = null;

        $offsetParamOffset = 4;
        if (isset($expression['parameters'][4]) && 'int' === $expression['parameters'][4]['parameter']['type']) {
            $offset = $expression['parameters'][4]['parameter']['value'].' ';
            unset($expression['parameters'][4]);
        }

        if (isset($expression['parameters'][3]) && 'int' === $expression['parameters'][3]['parameter']['type']) {
            $flags = $expression['parameters'][3]['parameter']['value'].' ';
            $offsetParamOffset = 3;
            unset($expression['parameters'][3]);
        }

        try {
            $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression);

            if (null === $offset && isset($resolvedParams[$offsetParamOffset])) {
                $context->headersManager->add('kernel/operators');
                $offset = 'zephir_get_intval('.$resolvedParams[$offsetParamOffset].') ';
            }

            if (null === $flags && isset($resolvedParams[3])) {
                $context->headersManager->add('kernel/operators');
                $flags = 'zephir_get_intval('.$resolvedParams[3].') ';
            }
        } catch (Exception $e) {
            throw new CompilerException($e->getMessage(), $expression, $e->getCode(), $e);
        }

        if (null === $flags) {
            $flags = '0 ';
        }

        if (null === $offset) {
            $offset = '0 ';
        }

        return [$flags, $offset];
    }