Prepares a class name to be used as a C-string
public static escapeClassName ( string $className ) : string | ||
$className | string | |
Результат | string |
/** * Creates a new instance * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; /** * Resolves the symbol that expects the value */ $this->_literalOnly = false; $symbolVariable = $this->getExpectedNonLiteral($compilationContext, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("Objects can only be instantiated into dynamic variables", $expression); } if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot use non-heap variable to store new instance", $expression); } if ($symbolVariable->getName() != 'return_value') { if ($symbolVariable->hasDifferentDynamicType(array('unknown', 'undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non-object in "new" operator', 'non-valid-new', $expression); } } /** * Mark variables as dynamic objects */ $symbolVariable->setDynamicTypes('object'); $dynamic = false; if ($expression['class'] == 'self' || $expression['class'] == 'static') { $className = $compilationContext->classDefinition->getCompleteName(); } else { $className = $expression['class']; $dynamic = $expression['dynamic']; if (!$dynamic) { $className = $compilationContext->getFullName($expression['class']); } } if (!$className) { throw new CompilerException("A class name is required to instantiate the object", $expression); } /** * stdclass doesn't have constructors */ $lowerClassName = strtolower($className); $isStdClass = $lowerClassName === 'stdclass' || $lowerClassName === '\\stdclass'; if ($isStdClass) { if (isset($expression['parameters']) && count($expression['parameters']) > 0) { throw new CompilerException("stdclass does not receive parameters in its constructor", $expression); } $compilationContext->backend->initObject($symbolVariable, null, $compilationContext); $symbolVariable->setClassTypes('stdclass'); } else { $classDefinition = false; if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); } /** * Classes inside the same extension */ if ($classDefinition) { $compilationContext->backend->initObject($symbolVariable, $classDefinition->getClassEntry($compilationContext), $compilationContext); $symbolVariable->setClassTypes($className); $symbolVariable->setAssociatedClass($classDefinition); } else { /** * Classes outside the extension */ if ($dynamic) { $classNameVariable = $compilationContext->symbolTable->getVariableForRead($className, $compilationContext, $expression); if ($classNameVariable->isNotVariableAndString()) { throw new CompilerException("Only dynamic/string variables can be used in new operator. " . $classNameVariable->getName(), $expression); } /** * Use a safe string version of the variable to avoid segfaults */ $compilationContext->headersManager->add('kernel/object'); $safeSymbolVariable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext, $expression); $safeSymbolVariable->setMustInitNull(true); $safeSymbolVariable->setIsInitialized(true, $compilationContext, $expression); $safeSymbolVariable->increaseUses(); $compilationContext->codePrinter->output('zephir_fetch_safe_class(' . $safeSymbolVariable->getName() . ', ' . $classNameVariable->getName() . ');'); $safeSymbol = $compilationContext->backend->getVariableCode($safeSymbolVariable); $classNameToFetch = 'Z_STRVAL_P(' . $safeSymbol . '), Z_STRLEN_P(' . $safeSymbol . ')'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, true, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { if (!class_exists($className, false)) { $compilationContext->logger->warning('Class "' . $className . '" does not exist at compile time', "nonexistent-class", $expression); $classNameToFetch = 'SL("' . Utils::escapeClassName($className) . '")'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { $reflectionClass = new \ReflectionClass($className); if ($reflectionClass->isInterface()) { throw new CompilerException('Interfaces cannot be instantiated', $expression); } else { if (method_exists($reflectionClass, 'isTrait')) { if ($reflectionClass->isTrait()) { throw new CompilerException('Traits cannot be instantiated', $expression); } } } $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, $compilationContext, true); if (!$classEntry) { $classNameToFetch = 'SL("' . Utils::escapeClassName($className) . '")'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { $symbolVariable->setAssociatedClass($reflectionClass); } } $symbolVariable->setClassTypes($className); } $compilationContext->backend->initObject($symbolVariable, $classEntry, $compilationContext); } } /** * Mark variable initialized */ $symbolVariable->setIsInitialized(true, $compilationContext, $expression); /** * Don't check the constructor for stdclass instances */ if ($isStdClass) { return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } /** * Call the constructor * For classes in the same extension we check if the class does implement a constructor * For external classes we always assume the class does implement a constructor */ $callConstructor = false; if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); if ($classDefinition->getType() != 'class') { throw new CompilerException("Only classes can be instantiated", $expression); } $callConstructor = $classDefinition->hasMethod("__construct"); } else { if ($compilationContext->compiler->isBundledClass($className)) { $classDefinition = $compilationContext->compiler->getInternalClassDefinition($className); $callConstructor = $classDefinition->hasMethod("__construct"); } } /* @TODO use the MethodBuilder here */ if (isset($expression['parameters'])) { $callExpr = new Expression(array('variable' => array('type' => 'variable', 'value' => $symbolVariable->getRealName()), 'name' => '__construct', 'parameters' => $expression['parameters'], 'call-type' => MethodCall::CALL_NORMAL, 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char'], 'check' => $callConstructor)); } else { $callExpr = new Expression(array('variable' => array('type' => 'variable', 'value' => $symbolVariable->getRealName()), 'name' => '__construct', 'call-type' => MethodCall::CALL_NORMAL, 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char'], 'check' => $callConstructor)); } /** * If we are certain that there is a constructor we call it, otherwise we checked it at runtime. */ if ($callConstructor) { $methodCall = new MethodCall(); $callExpr->setExpectReturn(false); $methodCall->compile($callExpr, $compilationContext); } else { $compilationContext->headersManager->add('kernel/fcall'); /* @todo, generate the code using builders */ $compilationContext->backend->checkConstructor($symbolVariable, $compilationContext); $codePrinter->increaseLevel(); $methodCall = new MethodCall(); $callExpr->setExpectReturn(false); $methodCall->compile($callExpr, $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); } return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Convert Class/Interface name to C ClassEntry * * @param string $className * @param CompilationContext $compilationContext * @param boolean $check * @return string * @throws CompilerException */ public function getClassEntryByClassName($className, CompilationContext $compilationContext, $check = true) { switch (strtolower($className)) { /** * Zend classes */ case 'exception': $classEntry = 'zend_exception_get_default(TSRMLS_C)'; break; /** * Zend interfaces (Zend/zend_interfaces.h) */ /** * Zend interfaces (Zend/zend_interfaces.h) */ case 'iterator': $classEntry = 'zend_ce_iterator'; break; case 'arrayaccess': $classEntry = 'zend_ce_arrayaccess'; break; case 'serializable': $classEntry = 'zend_ce_serializable'; break; case 'iteratoraggregate': $classEntry = 'zend_ce_aggregate'; break; /** * SPL Exceptions */ /** * SPL Exceptions */ case 'logicexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_LogicException'; break; case 'badfunctioncallexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_BadFunctionCallException'; break; case 'badmethodcallexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_BadMethodCallException'; break; case 'domainexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_DomainException'; break; case 'invalidargumentexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_InvalidArgumentException'; break; case 'lengthexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_LengthException'; break; case 'outofrangeexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_OutOfRangeException'; break; case 'runtimeexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_RuntimeException'; break; case 'outofboundsexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_OutOfBoundsException'; break; case 'overflowexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_OverflowException'; break; case 'rangeexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_RangeException'; break; case 'underflowexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_UnderflowException'; break; case 'unexpectedvalueexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_UnexpectedValueException'; break; /** * SPL Iterators Interfaces (spl/spl_iterators.h) */ /** * SPL Iterators Interfaces (spl/spl_iterators.h) */ case 'recursiveiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveIterator'; break; case 'recursiveiteratoriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveIteratorIterator'; break; case 'recursivetreeiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveTreeIterator'; break; case 'filteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_FilterIterator'; break; case 'recursivefilteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveFilterIterator'; break; case 'parentiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_ParentIterator'; break; case 'seekableiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_SeekableIterator'; break; case 'limititerator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_LimitIterator'; break; case 'cachingiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_CachingIterator'; break; case 'recursivecachingiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveCachingIterator'; break; case 'outeriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_OuterIterator'; break; case 'iteratoriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_IteratorIterator'; break; case 'norewinditerator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_NoRewindIterator'; break; case 'infiniteiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_InfiniteIterator'; break; case 'emptyiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_EmptyIterator'; break; case 'appenditerator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_AppendIterator'; break; case 'regexiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RegexIterator'; break; case 'recursiveregexiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveRegexIterator'; break; case 'directoryiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_DirectoryIterator'; break; case 'filesystemiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_FilesystemIterator'; break; case 'recursivedirectoryiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_RecursiveDirectoryIterator'; break; case 'globiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_GlobIterator'; break; case 'splfileobject': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_SplFileObject'; break; case 'spltempfileobject': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_SplTempFileObject'; break; case 'countable': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_Countable'; break; case 'callbackfilteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_CallbackFilterIterator'; break; case 'recursivecallbackfilteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveCallbackFilterIterator'; break; case 'arrayobject': $compilationContext->headersManager->add('ext/spl/spl_array'); $classEntry = 'spl_ce_ArrayObject'; break; case 'splfixedarray': $compilationContext->headersManager->add('ext/spl/spl_fixedarray'); $classEntry = 'spl_ce_SplFixedArray'; break; case 'splpriorityqueue': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplPriorityQueue'; break; case 'splfileinfo': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_SplFileInfo'; break; case 'splheap': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplHeap'; break; case 'splminheap': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplMinHeap'; break; case 'splmaxheap': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplMaxHeap'; break; case 'splstack': $compilationContext->headersManager->add('ext/spl/spl_dllist'); $classEntry = 'spl_ce_SplStack'; break; case 'splqueue': $compilationContext->headersManager->add('ext/spl/spl_dllist'); $classEntry = 'spl_ce_SplQueue'; break; case 'spldoublylinkedlist': $compilationContext->headersManager->add('ext/spl/spl_dllist'); $classEntry = 'spl_ce_SplDoublyLinkedList'; break; case 'stdclass': $classEntry = 'zend_standard_class_def'; break; case 'closure': $compilationContext->headersManager->add('Zend/zend_closures'); $classEntry = 'zend_ce_closure'; break; case 'pdo': $compilationContext->headersManager->add('ext/pdo/php_pdo_driver'); $classEntry = 'php_pdo_get_dbh_ce()'; break; case 'pdostatement': $compilationContext->headersManager->add('kernel/main'); $classEntry = $compilationContext->backend->fetchClassEntry("pdostatement"); break; case 'pdoexception': $compilationContext->headersManager->add('ext/pdo/php_pdo_driver'); $classEntry = 'php_pdo_get_exception()'; break; case 'datetime': $compilationContext->headersManager->add('ext/date/php_date'); $classEntry = 'php_date_get_date_ce()'; break; case 'datetimezone': $compilationContext->headersManager->add('ext/date/php_date'); $classEntry = 'php_date_get_timezone_ce()'; break; // Reflection /*case 'reflector': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflector_ptr'; break; case 'reflectionexception': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_exception_ptr'; break; case 'reflection': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_ptr'; break; case 'reflectionfunctionabstract': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_abstract_ptr'; break; case 'reflectionfunction': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_ptr'; break; case 'reflectionparameter': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_parameter_ptr'; break; case 'reflectionclass': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_class_ptr'; break; case 'reflectionobject': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_object_ptr'; break; case 'reflectionmethod': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_method_ptr'; break; case 'reflectionproperty': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_property_ptr'; break; case 'reflectionextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_extension_ptr'; break; case 'reflectionzendextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_zend_extension_ptr'; break;*/ // Reflection /*case 'reflector': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflector_ptr'; break; case 'reflectionexception': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_exception_ptr'; break; case 'reflection': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_ptr'; break; case 'reflectionfunctionabstract': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_abstract_ptr'; break; case 'reflectionfunction': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_ptr'; break; case 'reflectionparameter': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_parameter_ptr'; break; case 'reflectionclass': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_class_ptr'; break; case 'reflectionobject': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_object_ptr'; break; case 'reflectionmethod': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_method_ptr'; break; case 'reflectionproperty': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_property_ptr'; break; case 'reflectionextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_extension_ptr'; break; case 'reflectionzendextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_zend_extension_ptr'; break;*/ default: if (!$check) { throw new CompilerException('Unknown class entry for "' . $className . '"'); } else { $classEntry = $compilationContext->backend->fetchClassEntry(Utils::escapeClassName(strtolower($className))); } } return $classEntry; }
public function generateFunctionInformation() { $headerPrinter = new CodePrinter(); $entryPrinter = new CodePrinter(); /** * Create argument info */ foreach ($this->functionDefinitions as $func) { $funcName = $func->getInternalName(); $argInfoName = 'arginfo_' . strtolower($funcName); $headerPrinter->output('PHP_FUNCTION(' . $funcName . ');'); $parameters = $func->getParameters(); if (count($parameters)) { $headerPrinter->output('ZEND_BEGIN_ARG_INFO_EX(' . $argInfoName . ', 0, 0, ' . $func->getNumberOfRequiredParameters() . ')'); foreach ($parameters->getParameters() as $parameter) { switch ($parameter['data-type']) { case 'array': $headerPrinter->output("\t" . 'ZEND_ARG_ARRAY_INFO(0, ' . $parameter['name'] . ', ' . (isset($parameter['default']) ? 1 : 0) . ')'); break; case 'variable': if (isset($parameter['cast'])) { switch ($parameter['cast']['type']) { case 'variable': $value = $parameter['cast']['value']; $headerPrinter->output("\t" . 'ZEND_ARG_OBJ_INFO(0, ' . $parameter['name'] . ', ' . Utils::escapeClassName($compilationContext->getFullName($value)) . ', ' . (isset($parameter['default']) ? 1 : 0) . ')'); break; default: throw new Exception('Unexpected exception'); } } else { $headerPrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')'); } break; default: $headerPrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')'); break; } } $headerPrinter->output('ZEND_END_ARG_INFO()'); $headerPrinter->outputBlankLine(); } /** Generate FE's */ $paramData = count($parameters) ? $argInfoName : 'NULL'; if ($func->isGlobal()) { $entryPrinter->output('ZEND_NAMED_FE(' . $func->getName() . ', ZEND_FN(' . $funcName . '), ' . $paramData . ')'); } else { $entryPrinter->output('ZEND_NS_NAMED_FE("' . str_replace('\\', '\\\\', $func->getNamespace()) . '", ' . $func->getName() . ', ZEND_FN(' . $funcName . '), ' . $paramData . ')'); } } $entryPrinter->output('ZEND_FE_END'); return array($headerPrinter->getOutput(), $entryPrinter->getOutput()); }
/** * Compiles the method * * @param CompilationContext $compilationContext * @return null * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { /** * Set the method currently being compiled */ $compilationContext->currentMethod = $this; if (is_object($this->_statements)) { /** * This pass checks for zval variables than can be potentially * used without allocating memory and track it * these variables are stored in the stack */ if ($compilationContext->config->get('local-context-pass', 'optimizations')) { $localContext = new LocalContextPass(); $localContext->pass($this->_statements); } else { $localContext = null; } /** * This pass tries to infer types for dynamic variables * replacing them by low level variables */ if ($compilationContext->config->get('static-type-inference', 'optimizations')) { $typeInference = new StaticTypeInference(); $typeInference->pass($this->_statements); if ($compilationContext->config->get('static-type-inference-second-pass', 'optimizations')) { $typeInference->reduce(); $typeInference->pass($this->_statements); } } else { $typeInference = null; } /** * This pass counts how many times a specific */ if ($compilationContext->config->get('call-gatherer-pass', 'optimizations')) { $callGathererPass = new CallGathererPass($compilationContext); $callGathererPass->pass($this->_statements); } else { $callGathererPass = null; } } else { $localContext = null; $typeInference = null; $callGathererPass = null; } /** * Every method has its own symbol table */ $symbolTable = new SymbolTable($compilationContext); if ($localContext) { $symbolTable->setLocalContext($localContext); } /** * Parameters has an additional extra mutation */ $parameters = $this->_parameters; if ($localContext) { if (is_object($parameters)) { foreach ($parameters->getParameters() as $parameter) { $localContext->increaseMutations($parameter['name']); } } } /** * 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); /** * Cache Manager manages both function and method call caches */ $cacheManager = new CacheManager(); $cacheManager->setGatherer($callGathererPass); $compilationContext->branchManager = $branchManager; $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; if (is_object($parameters)) { /** * Round 1. Create variables in parameters in the symbol table */ $classCastChecks = array(); foreach ($parameters->getParameters() as $parameter) { /** * Change dynamic variables to low level types */ if ($typeInference) { if (isset($parameter['data-type'])) { if ($parameter['data-type'] == 'variable') { $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); break; default: $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext); $symbolParam = $symbolTable->addVariable('variable', $parameter['name'] . '_param', $compilationContext); if ($parameter['data-type'] == 'string' || $parameter['data-type'] == 'array') { $symbol->setMustInitNull(true); } break; } } else { $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext); } /** * 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, $parameter); /** * Initialize auxiliar parameter zvals to null */ $symbolParam->setMustInitNull(true); /** * Increase uses */ $symbolParam->increaseUses(); } else { if (isset($parameter['default'])) { if (isset($parameter['data-type'])) { if ($parameter['data-type'] == 'variable') { $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, $parameter); /** * 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[] = array($symbol, $parameter); } else { if (isset($parameter['data-type'])) { if ($parameter['data-type'] == 'variable') { $symbol->setDynamicTypes('undefined'); } } else { $symbol->setDynamicTypes('undefined'); } } } $compilationContext->codePrinter->increaseLevel(); /** * Checks that a class-hinted variable meets its declaration */ foreach ($classCastChecks as $classCastCheck) { foreach ($classCastCheck[0]->getClassTypes() as $className) { /** * If the parameter is nullable check it must pass the 'instanceof' validation */ if (!isset($classCastCheck[1]['default'])) { $evalExpr = new UnaryOperatorBuilder('not', new BinaryOperatorBuilder('instanceof', new VariableBuilder($classCastCheck[0]->getName()), new VariableBuilder('\\' . $className))); } else { $evalExpr = new BinaryOperatorBuilder('and', new BinaryOperatorBuilder('not-equals', new TypeOfOperatorBuilder(new VariableBuilder($classCastCheck[0]->getName())), new LiteralBuilder("string", "null")), new UnaryOperatorBuilder('not', new BinaryOperatorBuilder('instanceof', new VariableBuilder($classCastCheck[0]->getName()), new VariableBuilder('\\' . $className)))); } $ifCheck = new IfStatementBuilder($evalExpr, new StatementsBlockBuilder(array(new ThrowStatementBuilder(new NewInstanceOperatorBuilder('\\InvalidArgumentException', array(new ParameterBuilder(new LiteralBuilder("string", "Parameter '" . $classCastCheck[0]->getName() . "' must be an instance of '" . Utils::escapeClassName($className) . "'")))))))); $ifStatement = new IfStatement($ifCheck->get()); $ifStatement->compile($compilationContext); } } $compilationContext->codePrinter->decreaseLevel(); } /** * 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 default values in dynamic variables */ $initVarCode = ""; foreach ($symbolTable->getVariables() as $variable) { /** * Initialize 'dynamic' variables with default values */ if ($variable->getType() == 'variable') { if ($variable->getNumberUses() > 0) { if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); switch ($defaultValue['type']) { case 'int': case 'uint': case 'long': case 'char': case 'uchar': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL; break; case 'null': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL; break; case 'double': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_DOUBLE(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL; break; case 'string': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL; break; case 'array': case 'empty-array': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL; break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } } continue; } /** * Initialize 'string' variables with default values */ if ($variable->getType() == 'string') { if ($variable->getNumberUses() > 0) { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); switch ($defaultValue['type']) { case 'string': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL; break; case 'null': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_EMPTY_STRING(' . $variable->getName() . ');' . PHP_EOL; break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } continue; } /** * Initialize 'array' variables with default values */ if ($variable->getType() == 'array') { if ($variable->getNumberUses() > 0) { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); switch ($defaultValue['type']) { case 'null': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL; break; case 'array': case 'empty-array': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL; break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } } } /** * Fetch parameters from vm-top */ $initCode = ""; $code = ""; if (is_object($parameters)) { /** * Round 2. Fetch the parameters in the method */ $params = array(); $requiredParams = array(); $optionalParams = array(); $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': $params[] = '&' . $parameter['name']; break; default: $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 = array(); 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 ($dataType != 'variable') { /** * 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 'string': case 'array': 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 */ $initCode .= "\t" . 'if (!' . $name . ') {' . PHP_EOL; $initCode .= $this->assignDefaultValue($parameter, $compilationContext); if (isset($parametersToSeparate[$name]) || $dataType != 'variable') { $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(); $compilationContext->headersManager->add('kernel/memory'); if ($symbolTable->getMustGrownStack()) { $code .= "\t" . 'zephir_fetch_params(1, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL; } else { $code .= "\t" . 'zephir_fetch_params(0, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL; } $code .= PHP_EOL; } $code .= $initCode . $initVarCode; $codePrinter->preOutput($code); /** * 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 = array(); $completeName = $compilationContext->classDefinition->getCompleteName(); foreach ($symbolTable->getVariables() as $variable) { if ($variable->getNumberUses() <= 0) { if ($variable->isExternal() == false) { $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 ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') { $type = $variable->getType(); if (!isset($usedVariables[$type])) { $usedVariables[$type] = array(); } $usedVariables[$type][] = $variable; } } if (count($usedVariables)) { $codePrinter->preOutputBlankLine(); } /** * Generate the variable definition for variables used */ foreach ($usedVariables as $type => $variables) { $pointer = null; switch ($type) { case 'int': $code = 'int '; break; case 'uint': $code = 'unsigned int '; break; case 'char': $code = 'char '; break; case 'uchar': $code = 'unsigned char '; break; case 'long': $code = 'long '; break; case 'ulong': $code = 'unsigned long '; break; case 'bool': $code = 'zend_bool '; break; case 'double': $code = 'double '; break; case 'string': case 'variable': case 'array': case 'null': $pointer = '*'; $code = 'zval '; break; case 'HashTable': $pointer = '*'; $code = 'HashTable '; break; case 'HashPosition': $code = 'HashPosition '; break; case 'zend_class_entry': $pointer = '*'; $code = 'zend_class_entry '; break; case 'zend_function': $pointer = '*'; $code = 'zend_function '; break; case 'zend_object_iterator': $pointer = '*'; $code = 'zend_object_iterator '; break; case 'zend_property_info': $pointer = '*'; $code = 'zend_property_info '; break; case 'zephir_fcall_cache_entry': $pointer = '*'; $code = 'zephir_fcall_cache_entry '; break; case 'static_zephir_fcall_cache_entry': $pointer = '*'; $code = 'zephir_nts_static zephir_fcall_cache_entry '; break; default: throw new CompilerException("Unsupported type in declare: " . $type); } $groupVariables = array(); $defaultValues = array(); /** * @var $variables Variable[] */ foreach ($variables as $variable) { if (($type == 'variable' || $type == 'string' || $type == 'array' || $type == 'resource' || $type == 'callable' || $type == 'object') && $variable->mustInitNull()) { if ($variable->isLocalOnly()) { $groupVariables[] = $variable->getName() . ' = zval_used_for_init'; } else { if ($variable->isDoublePointer()) { $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL'; } else { $groupVariables[] = $pointer . $variable->getName() . ' = NULL'; } } } else { if ($variable->isLocalOnly()) { $groupVariables[] = $variable->getName(); } else { if ($variable->isDoublePointer()) { if ($variable->mustInitNull()) { $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL'; } else { $groupVariables[] = $pointer . $pointer . $variable->getName(); } } else { $defaultValue = $variable->getDefaultInitValue(); if ($defaultValue !== null) { switch ($type) { case 'variable': case 'string': case 'array': case 'resource': case 'callable': case 'object': $groupVariables[] = $pointer . $variable->getName(); break; default: $groupVariables[] = $pointer . $variable->getName() . ' = ' . $defaultValue; break; } } else { if ($variable->mustInitNull() && $pointer) { $groupVariables[] = $pointer . $variable->getName() . ' = NULL'; } else { $groupVariables[] = $pointer . $variable->getName(); } } } } } } $codePrinter->preOutput("\t" . $code . join(', ', $groupVariables) . ';'); } /** * 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 ($lastType != 'return' && $lastType != 'throw' && !$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']); } } } /** * Remove macros that restore the memory 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; }
/** * @param $expression * @param CompilationContext $context * @return CompiledExpression * @throws CompilerException * @throws \Zephir\Exception */ public function compile($expression, CompilationContext $context) { $left = new Expression($expression['left']); $resolved = $left->compile($context); if ($resolved->getType() != 'variable') { throw new CompilerException("InstanceOf requires a 'dynamic variable' in the left operand", $expression); } $symbolVariable = $context->symbolTable->getVariableForRead($resolved->getCode(), $context, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("InstanceOf requires a 'dynamic variable' in the left operand", $expression); } $right = new Expression($expression['right']); $resolved = $right->compile($context); $resolvedVariable = $resolved->getCode(); switch ($resolved->getType()) { case 'string': $className = Utils::getFullName($resolvedVariable, $context->classDefinition->getNamespace(), $context->aliasManager); if ($context->compiler->isClass($className)) { $classDefinition = $context->compiler->getClassDefinition($className); $classEntry = $classDefinition->getClassEntry($context); } else { if (!class_exists($className, false)) { $code = 'SL("' . $resolvedVariable . '")'; } else { $classEntry = $context->classDefinition->getClassEntryByClassName($className, $context, true); if (!$classEntry) { $code = 'SL("' . $resolvedVariable . '")'; } } } break; default: switch ($resolved->getType()) { case 'variable': if ($resolvedVariable == 'this') { /** * @todo It's an optimization variant, but maybe we need to get entry in runtime? */ $classEntry = $context->classDefinition->getClassEntry($context); } elseif (!$context->symbolTable->hasVariable($resolvedVariable)) { $className = $context->getFullName($resolvedVariable); if ($className == 'Traversable') { $symbol = $context->backend->getVariableCode($symbolVariable); return new CompiledExpression('bool', 'zephir_zval_is_traversable(' . $symbol . ' TSRMLS_CC)', $expression); } if ($context->compiler->isClass($className)) { $classDefinition = $context->compiler->getClassDefinition($className); $classEntry = $classDefinition->getClassEntry($context); } else { if ($context->compiler->isInterface($className)) { $classDefinition = $context->compiler->getClassDefinition($className); $classEntry = $classDefinition->getClassEntry($context); } else { if (!class_exists($className, false)) { $code = 'SL("' . trim(Utils::escapeClassName($className), "\\") . '")'; } else { $classEntry = $context->classDefinition->getClassEntryByClassName($className, $context, true); if (!$classEntry) { $code = 'SL("' . trim(Utils::escapeClassName($className), "\\") . '")'; } } } } } else { $code = 'Z_STRVAL_P(' . $resolvedVariable . '), Z_STRLEN_P(' . $resolvedVariable . ')'; } break; case 'property-access': case 'array-access': $context->headersManager->add('kernel/operators'); $tempVariable = $context->symbolTable->getTempVariableForWrite('string', $context); $tempVariable->setMustInitNull(true); $tempVariableName = $tempVariable->getName(); $context->codePrinter->output('zephir_get_strval(' . $tempVariableName . ', ' . $resolvedVariable . ');'); $code = 'Z_STRVAL_P(' . $tempVariableName . '), Z_STRLEN_P(' . $tempVariableName . ')'; break; default: throw new CompilerException("InstanceOf requires a 'variable' or a 'string' in the right operand", $expression); } } /* @TODO, Possible optimization is use zephir_is_instance when the const class name is an internal class or interface */ $context->headersManager->add('kernel/object'); $symbol = $context->backend->getVariableCode($symbolVariable); if (isset($code)) { return new CompiledExpression('bool', 'zephir_is_instance_of(' . $symbol . ', ' . $code . ' TSRMLS_CC)', $expression); } return new CompiledExpression('bool', 'zephir_instance_of_ev(' . $symbol . ', ' . $classEntry . ' TSRMLS_CC)', $expression); }
/** * Test escapeClassName method. */ public function testEscapeClassName() { $classname = '\\Bar\\Foo'; $this->assertSame(Utils::escapeClassName($classname), '\\\\Bar\\\\Foo'); }