/** * Compiles the file * * @param Compiler $compiler * @param StringsManager $stringsManager */ public function compile(Compiler $compiler, StringsManager $stringsManager) { if (!$this->_ir) { throw new CompilerException('IR related to compiled file is missing'); } /** * External classes should not be compiled as part of the extension */ if ($this->_external) { return; } /** * Compilation context stores common objects required by compilation entities */ $compilationContext = new CompilationContext(); /** * Set global compiler in the compilation context */ $compilationContext->compiler = $compiler; /** * Set global config in the compilation context */ $compilationContext->config = $this->_config; /** * Set global logger in the compilation context */ $compilationContext->logger = $this->_logger; /** * Set global strings manager */ $compilationContext->stringsManager = $stringsManager; $compilationContext->backend = $compiler->backend; /** * Headers manager */ $headersManager = new HeadersManager(); $compilationContext->headersManager = $headersManager; /** * Main code-printer for the file */ $codePrinter = new CodePrinter(); $compilationContext->codePrinter = $codePrinter; /** * Alias manager */ $compilationContext->aliasManager = $this->_aliasManager; $codePrinter->outputBlankLine(); $class = false; $interface = false; foreach ($this->_ir as $topStatement) { switch ($topStatement['type']) { case 'class': if ($interface || $class) { throw new CompilerException("More than one class defined in the same file", $topStatement); } $class = true; $this->compileClass($compilationContext, $this->_namespace, $topStatement); break; case 'interface': if ($interface || $class) { throw new CompilerException("More than one class defined in the same file", $topStatement); } $class = true; $this->compileClass($compilationContext, $this->_namespace, $topStatement); break; case 'comment': $this->compileComment($compilationContext, $topStatement); break; } } /* ensure functions are handled last */ foreach ($this->_functionDefinitions as $funcDef) { $this->compileFunction($compilationContext, $funcDef); } /* apply headers */ $this->applyClassHeaders($compilationContext); $classDefinition = $this->_classDefinition; if (!$classDefinition) { $this->_ir = null; return; } $classDefinition->setOriginalNode($this->_originalNode); $completeName = $classDefinition->getCompleteName(); $path = str_replace('\\', DIRECTORY_SEPARATOR, strtolower($completeName)); $filePath = 'ext/' . $path . '.zep.c'; $filePathHeader = 'ext/' . $path . '.zep.h'; if (strpos($path, DIRECTORY_SEPARATOR)) { $dirname = dirname($filePath); if (!is_dir($dirname)) { mkdir($dirname, 0755, true); } } if ($codePrinter) { /** * If the file does not exists we create it for the first time */ if (!file_exists($filePath)) { file_put_contents($filePath, $codePrinter->getOutput()); if ($compilationContext->headerPrinter) { file_put_contents($filePathHeader, $compilationContext->headerPrinter->getOutput()); } } else { $fileSystem = $compiler->getFileSystem(); /** * Use md5 hash to avoid rewrite the file again and again when it hasn't changed * thus avoiding unnecessary recompilations */ $output = $codePrinter->getOutput(); $hash = $fileSystem->getHashFile('md5', $filePath, true); if (md5($output) != $hash) { file_put_contents($filePath, $output); } if ($compilationContext->headerPrinter) { $output = $compilationContext->headerPrinter->getOutput(); $hash = $fileSystem->getHashFile('md5', $filePathHeader, true); if (md5($output) != $hash) { file_put_contents($filePathHeader, $output); } } } } /** * Add to file compiled */ $this->_compiledFile = $path . '.c'; $this->_ir = null; }
/** * Compiles a class/interface * * @param CompilationContext $compilationContext * * @throws CompilerException * @throws Exception */ public function compile(CompilationContext $compilationContext) { /** * Sets the current object as global class definition */ $compilationContext->classDefinition = $this; /** * Get the global codePrinter */ $codePrinter = $compilationContext->codePrinter; /** * The ZEPHIR_INIT_CLASS defines properties and constants exported by the class */ $initClassName = $this->getCNamespace() . '_' . $this->getName(); $codePrinter->output('ZEPHIR_INIT_CLASS(' . $initClassName . ') {'); $codePrinter->outputBlankLine(); $codePrinter->increaseLevel(); /** * Method entry */ $methods =& $this->methods; $initMethod = $this->getLocalOrParentInitMethod(); if (count($methods) || $initMethod) { $methodEntry = strtolower($this->getCNamespace()) . '_' . strtolower($this->getName()) . '_method_entry'; } else { $methodEntry = 'NULL'; } foreach ($methods as $method) { $method->setupOptimized($compilationContext); } $namespace = str_replace('\\', '_', $compilationContext->config->get('namespace')); $flags = '0'; if ($this->isAbstract()) { $flags = 'ZEND_ACC_EXPLICIT_ABSTRACT_CLASS'; } if ($this->isFinal()) { if ($flags == '0') { $flags = 'ZEND_ACC_FINAL_CLASS'; } else { $flags .= '|ZEND_ACC_FINAL_CLASS'; } } /** * Register the class with extends + interfaces */ $classExtendsDefinition = null; if ($this->extendsClass) { $classExtendsDefinition = $this->extendsClassDefinition; if ($classExtendsDefinition instanceof ClassDefinition && !$classExtendsDefinition->isBundled()) { $classEntry = $classExtendsDefinition->getClassEntry($compilationContext); } else { $classEntry = $this->getClassEntryByClassName($classExtendsDefinition->getName(), $compilationContext); } if ($this->getType() == 'class') { $codePrinter->output('ZEPHIR_REGISTER_CLASS_EX(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $classEntry . ', ' . $methodEntry . ', ' . $flags . ');'); $codePrinter->outputBlankLine(); } else { $codePrinter->output('ZEPHIR_REGISTER_INTERFACE_EX(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $classEntry . ', ' . $methodEntry . ');'); $codePrinter->outputBlankLine(); } } else { if ($this->getType() == 'class') { $codePrinter->output('ZEPHIR_REGISTER_CLASS(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $methodEntry . ', ' . $flags . ');'); } else { $codePrinter->output('ZEPHIR_REGISTER_INTERFACE(' . $this->getNCNamespace() . ', ' . $this->getName() . ', ' . $namespace . ', ' . strtolower($this->getSCName($namespace)) . ', ' . $methodEntry . ');'); } $codePrinter->outputBlankLine(); } /** * Compile properties * @var $property ClassProperty */ foreach ($this->getProperties() as $property) { $docBlock = $property->getDocBlock(); if ($docBlock) { $codePrinter->outputDocBlock($docBlock, true); } $property->compile($compilationContext); $codePrinter->outputBlankLine(); } $initMethod = $this->getInitMethod(); if ($initMethod) { $codePrinter->output($namespace . '_' . strtolower($this->getSCName($namespace)) . '_ce->create_object = ' . $initMethod->getName() . ';'); } /** * Compile constants * @var $constant ClassConstant */ foreach ($this->getConstants() as $constant) { $docBlock = $constant->getDocBlock(); if ($docBlock) { $codePrinter->outputDocBlock($docBlock, true); } $constant->compile($compilationContext); $codePrinter->outputBlankLine(); } /** * Implemented interfaces */ $interfaces = $this->interfaces; $compiler = $compilationContext->compiler; if (is_array($interfaces)) { $codePrinter->outputBlankLine(true); foreach ($interfaces as $interface) { /** * Try to find the interface */ $classEntry = false; if ($compiler->isInterface($interface)) { $classInterfaceDefinition = $compiler->getClassDefinition($interface); $classEntry = $classInterfaceDefinition->getClassEntry($compilationContext); } else { if ($compiler->isBundledInterface($interface)) { $classInterfaceDefinition = $compiler->getInternalClassDefinition($interface); $classEntry = $this->getClassEntryByClassName($classInterfaceDefinition->getName(), $compilationContext); } } if (!$classEntry) { if ($compiler->isClass($interface)) { throw new CompilerException("Cannot locate interface " . $interface . " when implementing interfaces on " . $this->getCompleteName() . '. ' . $interface . ' is currently a class', $this->originalNode); } else { throw new CompilerException("Cannot locate interface " . $interface . " when implementing interfaces on " . $this->getCompleteName(), $this->originalNode); } } /** * We don't check if abstract classes implement the methods in their interfaces */ if (!$this->isAbstract() && !$this->isInterface()) { $this->checkInterfaceImplements($this, $classInterfaceDefinition); } $codePrinter->output('zend_class_implements(' . $this->getClassEntry() . ' TSRMLS_CC, 1, ' . $classEntry . ');'); } } if (!$this->isAbstract() && !$this->isInterface()) { /** * Interfaces in extended classes may have */ if ($classExtendsDefinition) { if ($classExtendsDefinition instanceof ClassDefinition && !$classExtendsDefinition->isBundled()) { $interfaces = $classExtendsDefinition->getImplementedInterfaces(); if (is_array($interfaces)) { foreach ($interfaces as $interface) { $classInterfaceDefinition = null; if ($compiler->isInterface($interface)) { $classInterfaceDefinition = $compiler->getClassDefinition($interface); } else { if ($compiler->isBundledInterface($interface)) { $classInterfaceDefinition = $compiler->getInternalClassDefinition($interface); } } if ($classInterfaceDefinition) { $this->checkInterfaceImplements($this, $classInterfaceDefinition); } } } } } } $codePrinter->output('return SUCCESS;'); $codePrinter->outputBlankLine(); $codePrinter->decreaseLevel(); $codePrinter->output('}'); $codePrinter->outputBlankLine(); /** * Compile methods */ foreach ($methods as $method) { $docBlock = $method->getDocBlock(); if ($docBlock) { $codePrinter->outputDocBlock($docBlock); } if ($this->getType() == 'class') { if (!$method->isInternal()) { $codePrinter->output('PHP_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ') {'); } else { $codePrinter->output($compilationContext->backend->getInternalSignature($method, $compilationContext) . ' {'); } $codePrinter->outputBlankLine(); if (!$method->isAbstract()) { $method->compile($compilationContext); } $codePrinter->output('}'); $codePrinter->outputBlankLine(); } else { $codePrinter->output('ZEPHIR_DOC_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ');'); $codePrinter->outputBlankLine(); } } /** * Check whether classes must be exported */ $exportClasses = $compilationContext->config->get('export-classes', 'extra'); if ($exportClasses) { $exportAPI = 'extern ZEPHIR_API'; } else { $exportAPI = 'extern'; } /** * Create a code printer for the header file */ $codePrinter = new CodePrinter(); $codePrinter->outputBlankLine(); $codePrinter->output($exportAPI . ' zend_class_entry *' . $this->getClassEntry() . ';'); $codePrinter->outputBlankLine(); $codePrinter->output('ZEPHIR_INIT_CLASS(' . $this->getCNamespace() . '_' . $this->getName() . ');'); $codePrinter->outputBlankLine(); if ($this->getType() == 'class') { if (count($methods)) { foreach ($methods as $method) { if (!$method->isInternal()) { $codePrinter->output('PHP_METHOD(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ');'); } else { $internalSignature = $compilationContext->backend->getInternalSignature($method, $compilationContext); $codePrinter->output($internalSignature . ';'); } } $codePrinter->outputBlankLine(); } } /** * Create argument info */ foreach ($methods as $method) { $parameters = $method->getParameters(); if (count($parameters)) { $codePrinter->output('ZEND_BEGIN_ARG_INFO_EX(arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', 0, 0, ' . $method->getNumberOfRequiredParameters() . ')'); foreach ($parameters->getParameters() as $parameter) { switch ($parameter['data-type']) { case 'array': $codePrinter->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']; $codePrinter->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 { $codePrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')'); } break; default: $codePrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')'); break; } } $codePrinter->output('ZEND_END_ARG_INFO()'); $codePrinter->outputBlankLine(); } } if (count($methods)) { $codePrinter->output('ZEPHIR_INIT_FUNCS(' . strtolower($this->getCNamespace() . '_' . $this->getName()) . '_method_entry) {'); foreach ($methods as $method) { $parameters = $method->getParameters(); if ($this->getType() == 'class') { if (!$method->isInternal()) { if (count($parameters)) { $codePrinter->output("\t" . 'PHP_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', ' . $method->getModifiers() . ')'); } else { $codePrinter->output("\t" . 'PHP_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', NULL, ' . $method->getModifiers() . ')'); } } } else { if ($method->isStatic()) { if (count($parameters)) { $codePrinter->output("\t" . 'ZEND_FENTRY(' . $method->getName() . ', NULL, arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ', ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)'); } else { $codePrinter->output("\t" . 'ZEND_FENTRY(' . $method->getName() . ', NULL, NULL, ZEND_ACC_STATIC|ZEND_ACC_ABSTRACT|ZEND_ACC_PUBLIC)'); } } else { if (count($parameters)) { $codePrinter->output("\t" . 'PHP_ABSTRACT_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', arginfo_' . strtolower($this->getCNamespace() . '_' . $this->getName() . '_' . $method->getName()) . ')'); } else { $codePrinter->output("\t" . 'PHP_ABSTRACT_ME(' . $this->getCNamespace() . '_' . $this->getName() . ', ' . $method->getName() . ', NULL)'); } } } } $codePrinter->output("\t" . 'PHP_FE_END'); $codePrinter->output('};'); } $compilationContext->headerPrinter = $codePrinter; }
/** * Compiles the file * * @param Compiler $compiler * @param StringsManager $stringsManager */ public function compile(Compiler $compiler, StringsManager $stringsManager) { /** * Compilation context stores common objects required by compilation entities */ $compilationContext = new CompilationContext(); /** * Set global compiler in the compilation context */ $compilationContext->compiler = $compiler; /** * Set global config in the compilation context */ $compilationContext->config = $this->config; /** * Set global logger in the compilation context */ $compilationContext->logger = $this->logger; /** * Set global strings manager */ $compilationContext->stringsManager = $stringsManager; $compilationContext->backend = $compiler->backend; /** * Headers manager */ $headersManager = new HeadersManager(); $compilationContext->headersManager = $headersManager; /** * Main code-printer for the file */ $codePrinter = new CodePrinter(); $compilationContext->codePrinter = $codePrinter; /** * Alias manager */ $compilationContext->aliasManager = new AliasManager(); $codePrinter->outputBlankLine(); $classDefinition = $this->classDefinition; $this->compileClass($classDefinition, $compilationContext); $completeName = $classDefinition->getCompleteName(); $path = str_replace('\\', DIRECTORY_SEPARATOR, strtolower($completeName)); $filePath = 'ext/' . $path . '.zep.c'; $filePathHeader = 'ext/' . $path . '.zep.h'; if (strpos($path, DIRECTORY_SEPARATOR)) { $dirname = dirname($filePath); if (!is_dir($dirname)) { mkdir($dirname, 0755, true); } } if ($codePrinter) { /** * If the file does not exists we create it for the first time */ if (!file_exists($filePath)) { file_put_contents($filePath, $codePrinter->getOutput()); if ($compilationContext->headerPrinter) { file_put_contents($filePathHeader, $compilationContext->headerPrinter->getOutput()); } } else { /** * Use md5 hash to avoid rewrite the file again and again when it hasn't changed * thus avoiding unnecessary recompilations */ $output = $codePrinter->getOutput(); $hash = hash_file('md5', $filePath); if (md5($output) != $hash) { file_put_contents($filePath, $output); } if ($compilationContext->headerPrinter) { $output = $compilationContext->headerPrinter->getOutput(); $hash = hash_file('md5', $filePathHeader); if (md5($output) != $hash) { file_put_contents($filePathHeader, $output); } } } } /** * Add to file compiled */ $this->compiledFile = $path . '.c'; }
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()); }