/** * Creates a closure * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws \Zephir\CompilerException */ 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->logger); $compilationContext->compiler->addClassDefinition($compilerFile, $classDefinition); /** * Builds parameters using the only parameter available */ $parameters = new ClassMethodParameters(array(array('type' => 'parameter', 'name' => $expression['left']['value'], 'const' => 0, 'data-type' => 'variable', 'mandatory' => 0, 'reference' => 0, 'file' => $expression['left']['file'], 'line' => $expression['left']['line'], 'char' => $expression['left']['char']))); $statementBlock = new StatementsBlockBuilder(array(new ReturnStatementBuilder(new RawExpressionBuilder($expression['right'])))); $block = $statementBlock->get(); $classMethod = new ClassMethod($classDefinition, array('public', 'final'), '__invoke', $parameters, new StatementsBlock($block), null, null, $expression); $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->codePrinter->output('zephir_create_closure_ex(' . $symbolVariable->getName() . ', NULL, ' . $classDefinition->getClassEntry() . ', SS("__invoke") TSRMLS_CC);'); self::$id++; return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Creates a closure * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws \Zephir\CompilerException */ 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->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 = array(); } $classMethod = new ClassMethod($classDefinition, array('public', 'final'), '__invoke', $parameters, new StatementsBlock($block), null, null, $expression); $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); self::$id++; return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Creates a closure * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws \Zephir\CompilerException */ 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->logger); $compilationContext->compiler->addClassDefinition($compilerFile, $classDefinition); /** * Builds parameters using the only parameter available */ $parameters = new ClassMethodParameters(array(array('type' => 'parameter', 'name' => $expression['left']['value'], 'const' => 0, 'data-type' => 'variable', 'mandatory' => 0, 'reference' => 0, 'file' => $expression['left']['file'], 'line' => $expression['left']['line'], 'char' => $expression['left']['char']))); $exprBuilder = BuilderFactory::getInstance(); $statementBlockBuilder = $exprBuilder->statements()->block(array($exprBuilder->statements()->returnX($exprBuilder->raw($expression['right'])))); $block = $statementBlockBuilder->build(); $classMethod = new ClassMethod($classDefinition, array('public', 'final'), '__invoke', $parameters, new StatementsBlock($block), null, null, $expression); $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); self::$id++; return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * @return LetStatementBuilder */ protected function getLetStatement() { if (!$this->isStatic()) { return new LetStatementBuilder(array('assign-type' => 'object-property', 'operator' => 'assign', 'variable' => 'this', 'property' => $this->name, 'file' => $this->original['default']['file'], 'line' => $this->original['default']['line'], 'char' => $this->original['default']['char']), $this->original['default']); } return new LetStatementBuilder(array('assign-type' => 'static-property', 'operator' => 'assign', 'variable' => '\\' . $this->classDefinition->getCompleteName(), 'property' => $this->name, 'file' => $this->original['default']['file'], 'line' => $this->original['default']['line'], 'char' => $this->original['default']['char']), $this->original['default']); }
/** * @return LetStatementBuilder */ protected function getLetStatement() { if ($this->isStatic()) { return new LetStatementBuilder(array('assign-type' => 'static-property', 'operator' => 'assign', 'variable' => '\\' . $this->classDefinition->getCompleteName(), 'property' => $this->name, 'file' => $this->original['default']['file'], 'line' => $this->original['default']['line'], 'char' => $this->original['default']['char']), $this->original['default']); } $lsb = new LetStatementBuilder(array('assign-type' => 'object-property', 'operator' => 'assign', 'variable' => 'this', 'property' => $this->name, 'file' => $this->original['default']['file'], 'line' => $this->original['default']['line'], 'char' => $this->original['default']['char']), $this->original['default']); return new IfStatementBuilder(new BinaryOperatorBuilder('equals', new BinaryOperatorBuilder('property-access', new VariableBuilder('this'), new LiteralBuilder('string', $this->name)), new LiteralBuilder('null', null)), new StatementsBlockBuilder(array($lsb))); }
public function getOptimizedMethod() { $optimizedName = $this->getName() . '_zephir_internal_call'; $optimizedMethod = $this->classDefinition->getMethod($optimizedName, false); if (!$optimizedMethod || !$this->optimizable) { return $this; } return $optimizedMethod; }
/** * Checks if the class is suitable for caching * * @param ClassDefinition $classDefinition * @return boolean */ private function isClassCacheable($classDefinition) { if ($classDefinition instanceof ClassDefinition) { return true; } if ($classDefinition instanceof \ReflectionClass) { if ($classDefinition->isInternal() && $classDefinition->isInstantiable()) { $extension = $classDefinition->getExtension(); switch ($extension->getName()) { case 'Reflection': case 'Core': case 'SPL': return true; } } } return false; }
/** * @return $this|ExpressionLetStatement */ protected function getLetStatement() { $exprBuilder = BuilderFactory::getInstance(); if ($this->isStatic()) { $className = '\\' . $this->classDefinition->getCompleteName(); $expr = $exprBuilder->raw($this->original['default']); return $exprBuilder->statements()->let(array($exprBuilder->operators()->assignStaticProperty($className, $this->name, $expr)->setFile($this->original['default']['file'])->setLine($this->original['default']['line'])->setChar($this->original['default']['char']))); } $lsb = $exprBuilder->statements()->let(array($exprBuilder->operators()->assignProperty('this', $this->name, $exprBuilder->raw($this->original['default']))->setFile($this->original['default']['file'])->setLine($this->original['default']['line'])->setChar($this->original['default']['char']))); return $exprBuilder->statements()->ifX()->setCondition($exprBuilder->operators()->binary(BinaryOperator::OPERATOR_EQUALS, $exprBuilder->operators()->binary(BinaryOperator::OPERATOR_ACCESS_PROPERTY, $exprBuilder->variable('this'), $exprBuilder->literal(Types::STRING, $this->name)), $exprBuilder->literal(Types::NULL_)))->setStatements($exprBuilder->statements()->block(array($lsb))); }
protected function buildClass(ClassDefinition $class) { $source = <<<EOF <?php namespace {$class->getNamespace()}; EOF; if ($class->isFinal()) { $source .= 'final '; } elseif ($class->isAbstract()) { $source .= 'abstract '; } $source .= $class->getType() . ' ' . $class->getName(); if ($extendsClassDefinition = $class->getExtendsClassDefinition()) { $source .= ' extends \\' . $extendsClassDefinition->getCompleteName(); } if ($implementedInterfaces = $class->getImplementedInterfaces()) { $interfaces = array_map(function ($val) { return '\\' . $val; }, $implementedInterfaces); $source .= ' implements ' . join(', ', $interfaces); } $source .= PHP_EOL . '{' . PHP_EOL; foreach ($class->getConstants() as $constant) { $source .= $this->buildConstant($constant) . PHP_EOL; } foreach ($class->getProperties() as $property) { $source .= $this->buildProperty($property) . PHP_EOL; } $source .= PHP_EOL; foreach ($class->getMethods() as $method) { $source .= $this->buildMethod($method, $class->getType() === 'interface') . "\n\n"; } return $source . '}' . PHP_EOL; }
public function getData() { $nsPieces = explode('\\', $this->class->getNamespace()); $nsPathes = array(); $nsStr = ""; foreach ($nsPieces as $n) { if (strlen($nsStr) > 0) { $nsStr .= '\\'; } $nsStr .= $n; $nsPathes[$n] = $nsStr; } return array("classDefinition" => $this->class, "compilerFile" => $this->compilerFile, "className" => $this->class->getName(), "classNamespace" => $this->class->getNamespace(), "fullName" => $this->class->getCompleteName(), "methods" => $this->class->getMethods(), "namespacePieces" => $nsPathes); }
/** * Creates a definition for a class * * @param CompilationContext $compilationContext * @param string $namespace * @param array $topStatement * @param array $docblock */ public function preCompileClass(CompilationContext $compilationContext, $namespace, $topStatement, $docblock) { $classDefinition = new ClassDefinition($namespace, $topStatement['name']); $classDefinition->setIsExternal($this->_external); if (isset($topStatement['extends'])) { $classDefinition->setExtendsClass($this->getFullName($topStatement['extends'])); } if (isset($topStatement['implements'])) { foreach ($topStatement['implements'] as &$implement) { $implement['value'] = $this->getFullName($implement['value']); } $classDefinition->setImplementsInterfaces($topStatement['implements']); } if (isset($topStatement['abstract'])) { $classDefinition->setIsAbstract($topStatement['abstract']); } if (isset($topStatement['final'])) { $classDefinition->setIsFinal($topStatement['final']); } if (is_array($docblock)) { $classDefinition->setDocBlock($docblock["value"]); } if (isset($topStatement['definition'])) { $definition = $topStatement['definition']; if (isset($definition['properties'])) { foreach ($definition['properties'] as $property) { /** * Add property to the definition */ $classDefinition->addProperty(new ClassProperty($classDefinition, $property['visibility'], $property['name'], isset($property['default']) ? $property['default'] : null, isset($property['docblock']) ? $property['docblock'] : null, $property)); /** * Check and process shortcuts */ if (isset($property['shortcuts'])) { $this->_processShorcuts($property, $classDefinition); } } } /** * Register constants */ if (isset($definition['constants'])) { foreach ($definition['constants'] as $constant) { $classDefinition->addConstant(new ClassConstant($constant['name'], isset($constant['default']) ? $constant['default'] : null, isset($constant['docblock']) ? $constant['docblock'] : null)); } } /** * Register methods */ if (isset($definition['methods'])) { foreach ($definition['methods'] as $method) { $classDefinition->addMethod(new ClassMethod($classDefinition, $method['visibility'], $method['name'], isset($method['parameters']) ? new ClassMethodParameters($method['parameters']) : null, isset($method['statements']) ? new StatementsBlock($method['statements']) : null, isset($method['docblock']) ? $method['docblock'] : null, isset($method['return-type']) ? $method['return-type'] : null, $method), $method); } } } $this->_classDefinition = $classDefinition; /** * Assign current class definition to the compilation context */ $compilationContext->classDefinition = $classDefinition; /** * Run pre-compilation passes */ $classDefinition->preCompile($compilationContext); }
/** * Build class * * @param ClassDefinition $class * @param string $indent * @return string */ protected function buildClass(ClassDefinition $class, $indent) { $source = <<<EOF <?php namespace {$class->getNamespace()}; EOF; $source .= new DocBlock($class->getDocBlock(), '') . "\n"; if ($class->isFinal()) { $source .= 'final '; } elseif ($class->isAbstract()) { $source .= 'abstract '; } $source .= $class->getType() . ' ' . $class->getName(); if ($class->getExtendsClass()) { $extendsClassDefinition = $class->getExtendsClassDefinition(); if (!$extendsClassDefinition) { throw new \RuntimeException('Class "' . $class->getName() . '" does not have a extendsClassDefinition'); } $source .= ' extends ' . ($extendsClassDefinition->isBundled() ? '' : '\\') . $extendsClassDefinition->getCompleteName(); } if ($implementedInterfaces = $class->getImplementedInterfaces()) { $interfaces = array_map(function ($val) { return '\\' . $val; }, $implementedInterfaces); $keyword = $class->getType() == 'interface' ? ' extends ' : ' implements '; $source .= $keyword . join(', ', $interfaces); } $source .= PHP_EOL . '{' . PHP_EOL; foreach ($class->getConstants() as $constant) { $source .= $this->buildConstant($constant, $indent) . PHP_EOL . PHP_EOL; } foreach ($class->getProperties() as $property) { $source .= $this->buildProperty($property, $indent) . PHP_EOL . PHP_EOL; } $source .= PHP_EOL; foreach ($class->getMethods() as $method) { if ($method->isInternal()) { continue; } $source .= $this->buildMethod($method, $class->getType() === 'interface', $indent) . "\n\n"; } return $source . '}' . PHP_EOL; }
/** * Calls static methods on the some class context * * @param string $methodName * @param array $expression * @param Variable $symbolVariable * @param boolean $mustInit * @param boolean $isExpecting * @param ClassDefinition $classDefinition * @param CompilationContext $compilationContext * @param ClassMethod $method */ protected function callFromClass($methodName, array $expression, $symbolVariable, $mustInit, $isExpecting, ClassDefinition $classDefinition, CompilationContext $compilationContext, ClassMethod $method) { $codePrinter = $compilationContext->codePrinter; if ($classDefinition->isBundled()) { //if (!$compilationContext->symbolTable->hasVariable($variableName)) { $classEntryVariable = $compilationContext->symbolTable->addTemp('zend_class_entry', $compilationContext); $compilationContext->backend->fetchClass($classEntryVariable, 'SL("' . str_replace('\\', '\\\\', $classDefinition->getName()) . '")', false, $compilationContext); //} //$classEntryVariable = $compilationContext->symbolTable->getVariableForWrite($variableName, $compilationContext, $expression); $classEntry = $classEntryVariable->getName(); } else { $classEntry = $classDefinition->getClassEntry($compilationContext); } /** * Call static methods must grown the stack */ $compilationContext->symbolTable->mustGrownStack(true); if ($mustInit) { $symbolVariable->setMustInitNull(true); $symbolVariable->trackVariant($compilationContext); } if ($method) { $method = $method->getOptimizedMethod(); } /** * Check if the method call can have an inline cache */ $methodCache = $compilationContext->cacheManager->getStaticMethodCache(); $cachePointer = $methodCache->get($compilationContext, isset($method) ? $method : null); if (isset($expression['parameters']) && count($expression['parameters'])) { $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression); } else { $params = array(); } if ($symbolVariable) { $symbol = $compilationContext->backend->getVariableCodePointer($symbolVariable); } $paramCount = count($params); $paramsStr = $paramCount ? ', ' . join(', ', $params) : ''; if ($method->isInternal()) { $ce = $classDefinition->getClassEntry($compilationContext); if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $macro = $compilationContext->backend->getFcallManager()->getMacro(true, true, $paramCount); $codePrinter->output($macro . '(' . $ce . ', ' . $method->getInternalName() . $paramsStr . ');'); } else { $macro = $compilationContext->backend->getFcallManager()->getMacro(true, 2, $paramCount); $codePrinter->output($macro . '(' . $symbol . ', ' . $ce . ', ' . $method->getInternalName() . $paramsStr . ');'); } } else { $macro = $compilationContext->backend->getFcallManager()->getMacro(true, false, $paramCount); $codePrinter->output($macro . '(' . $ce . ', ' . $method->getInternalName() . $paramsStr . ');'); } } else { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_CE_STATIC(' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . $paramsStr . ');'); } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(' . $symbol . ', ' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . $paramsStr . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(NULL, ' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . $paramsStr . ');'); } } /** * Temporary variables must be copied if they have more than one reference */ foreach ($this->getMustCheckForCopyVariables() as $checkVariable) { $codePrinter->output('zephir_check_temp_parameter(' . $checkVariable . ');'); } $this->addCallStatusOrJump($compilationContext); }
/** * Builds a class definition from reflection * * @param \ReflectionClass $class * * @return ClassDefinition */ public static function buildFromReflection(\ReflectionClass $class) { $classDefinition = new ClassDefinition($class->getNamespaceName(), $class->getName()); $methods = $class->getMethods(); if (count($methods) > 0) { foreach ($methods as $method) { $parameters = array(); foreach ($method->getParameters() as $row) { $params = array('type' => 'parameter', 'name' => $row->getName(), 'const' => 0, 'data-type' => 'variable', 'mandatory' => !$row->isOptional()); if (!$params['mandatory']) { try { $params['default'] = $row->getDefaultValue(); } catch (\ReflectionException $e) { // TODO: dummy default value $params['default'] = true; } } $parameters[] = $params; } $classMethod = new ClassMethod($classDefinition, array(), $method->getName(), new ClassMethodParameters($parameters)); $classMethod->setIsStatic($method->isStatic()); $classMethod->setIsBundled(true); $classDefinition->addMethod($classMethod); } } $constants = $class->getConstants(); if (count($constants) > 0) { foreach ($constants as $constantName => $constantValue) { $type = self::_convertPhpConstantType(gettype($constantValue)); $classConstant = new ClassConstant($constantName, array('value' => $constantValue, 'type' => $type), null); $classDefinition->addConstant($classConstant); } } $properties = $class->getProperties(); if (count($properties) > 0) { foreach ($properties as $property) { $visibility = array(); if ($property->isPublic()) { $visibility[] = 'public'; } if ($property->isPrivate()) { $visibility[] = 'private'; } if ($property->isProtected()) { $visibility[] = 'protected'; } if ($property->isStatic()) { $visibility[] = 'static'; } $classProperty = new ClassProperty($classDefinition, $visibility, $property->getName(), null, null, null); $classDefinition->addProperty($classProperty); } } $classDefinition->setIsBundled(true); return $classDefinition; }
public static function sourceUrl(ClassDefinition $c) { return "/source/" . str_replace("\\", "/", $c->getCompleteName()) . ".html"; }
/** * Returns class the class definition from a given class name * * @param string $className * * @return ClassDefinition */ public function getInternalClassDefinition($className) { if (!isset(self::$internalDefinitions[$className])) { $reflection = new \ReflectionClass($className); self::$internalDefinitions[$className] = ClassDefinition::buildFromReflection($reflection); } return self::$internalDefinitions[$className]; }
/** * Calls static methods on the some class context * * @param string $methodName * @param array $expression * @param Variable $symbolVariable * @param boolean $mustInit * @param boolean $isExpecting * @param ClassDefinition $classDefinition * @param CompilationContext $compilationContext * @param ClassMethod $method */ protected function callFromClass($methodName, array $expression, $symbolVariable, $mustInit, $isExpecting, ClassDefinition $classDefinition, CompilationContext $compilationContext, $method) { $codePrinter = $compilationContext->codePrinter; if ($classDefinition->isInternal()) { $variableName = str_replace('\\', '_', $classDefinition->getSCName('local')); if (!$compilationContext->symbolTable->hasVariable($variableName)) { $classEntryVariable = $compilationContext->symbolTable->addVariable('zend_class_entry', $variableName, $compilationContext); $codePrinter->output($classEntryVariable->getName() . ' = zend_fetch_class(SL("\\\\' . str_replace('\\', '\\\\', $classDefinition->getName()) . '"), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);'); } $classEntryVariable = $compilationContext->symbolTable->getVariableForWrite($variableName, 'zend_class_entry', $compilationContext); $classEntry = $classEntryVariable->getName(); } else { $classEntry = $classDefinition->getClassEntry(); } /** * Call static methods must grown the stack */ $compilationContext->symbolTable->mustGrownStack(true); if ($mustInit) { $symbolVariable->setMustInitNull(true); $symbolVariable->trackVariant($compilationContext); } /** * Check if the method call can have an inline cache */ $methodCache = $compilationContext->cacheManager->getStaticMethodCache(); $cachePointer = $methodCache->get($compilationContext, isset($method) ? $method : null); if (isset($expression['parameters']) && count($expression['parameters'])) { $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression); } else { $params = array(); } if (!count($params)) { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_CE_STATIC(' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . ');'); } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(&' . $symbolVariable->getName() . ', ' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(NULL, ' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . ');'); } } else { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_CE_STATIC(' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');'); } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(&' . $symbolVariable->getName() . ', ' . $classEntry . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(NULL, ' . $classEntry . ', "' . $methodName . '", ' . $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 . ');'); } $this->addCallStatusOrJump($compilationContext); }