Пример #1
0
 /**
  * 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;
 }
Пример #2
0
 /**
  * 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;
 }
Пример #3
0
 /**
  * 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';
 }
Пример #4
0
 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());
 }