protected function _callNormal(array $expression, CompilationContext $compilationContext)
{
$funcName = strtolower($expression['name']);
if ($funcName == 'array') {
throw new CompilerException("Cannot use 'array' as a function call", $expression);
}
/**
* Try to optimize function calls using existing optimizers
*/
$compiledExpr = $this->optimize($funcName, $expression, $this, $compilationContext);
if (is_object($compiledExpr)) {
return $compiledExpr;
}
$exists = true;
if (!$this->functionExists($funcName, $compilationContext)) {
$compilationContext->logger->warning("Function \"{$funcName}\" does not exist at compile time", "nonexistent-function", $expression);
$exists = false;
}
/**
* Static variables can be passed using local variables saving memory if the function is read only
*/
if ($exists) {
$readOnly = $this->isReadOnly($funcName, $expression);
} else {
$readOnly = false;
}
/**
* Resolve parameters
*/
if (isset($expression['parameters'])) {
if ($readOnly) {
$params = $this->getReadOnlyResolvedParams($expression['parameters'], $compilationContext, $expression);
} else {
$params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression);
}
} else {
$params = array();
}
/**
* 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);
/**
* At this point the function will be called in the PHP userland.
* PHP functions only return zvals so we need to validate the target variable is also a zval
*/
$symbolVariable = $this->getSymbolVariable();
if ($symbolVariable) {
if (!$symbolVariable->isVariable()) {
throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression);
}
/**
* 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);
/**
* Check if the function can have an inline cache
*/
$functionCache = $compilationContext->cacheManager->getFunctionCache();
$cachePointer = $functionCache->get($funcName, $compilationContext, $this, $exists);
/**
* Add the last call status to the current symbol table
*/
$this->addCallStatusFlag($compilationContext);
if (!count($params)) {
if ($this->isExpectingReturn()) {
if ($symbolVariable->getName() == 'return_value') {
$codePrinter->output('ZEPHIR_RETURN_CALL_FUNCTION("' . $funcName . '", ' . $cachePointer . ');');
} else {
if ($this->mustInitSymbolVariable()) {
$symbolVariable->setMustInitNull(true);
$symbolVariable->trackVariant($compilationContext);
}
$codePrinter->output('ZEPHIR_CALL_FUNCTION(' . $symbol . ', "' . $funcName . '", ' . $cachePointer . ');');
}
} else {
$codePrinter->output('ZEPHIR_CALL_FUNCTION(NULL, "' . $funcName . '", ' . $cachePointer . ');');
}
} else {
if ($this->isExpectingReturn()) {
if ($symbolVariable->getName() == 'return_value') {
$codePrinter->output('ZEPHIR_RETURN_CALL_FUNCTION("' . $funcName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');');
} else {
if ($this->mustInitSymbolVariable()) {
$symbolVariable->setMustInitNull(true);
$symbolVariable->trackVariant($compilationContext);
}
$codePrinter->output('ZEPHIR_CALL_FUNCTION(' . $symbol . ', "' . $funcName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');');
}
} else {
$codePrinter->output('ZEPHIR_CALL_FUNCTION(NULL, "' . $funcName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');');
}
}
/**
* Temporary variables must be copied if they have more than one reference
*/
foreach ($this->getMustCheckForCopyVariables() as $checkVariable) {
$codePrinter->output('zephir_check_temp_parameter(' . $checkVariable . ');');
}
if (is_array($references)) {
foreach ($references as $reference) {
$variable = $compilationContext->symbolTable->getVariable($reference, $compilationContext);
$compilationContext->codePrinter->output('ZEPHIR_UNREF(' . $compilationContext->backend->getVariableCode($variable) . ');');
}
}
$this->addCallStatusOrJump($compilationContext);
/**
* We can mark temporary variables generated as idle
*/
foreach ($this->getTemporalVariables() as $tempVariable) {
$tempVariable->setIdle(true);
}
if ($this->isExpectingReturn()) {
return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression);
}
return new CompiledExpression('null', null, $expression);
}