/** * Generates the C sources from Zephir without compiling them * * @param CommandInterface $command * @return bool * @throws Exception */ public function generate(CommandInterface $command) { /** * Get global namespace */ $namespace = $this->checkDirectory(); /** * Check whether there are external dependencies */ $externalDependencies = $this->config->get('external-dependencies'); if (is_array($externalDependencies)) { foreach ($externalDependencies as $dependencyNs => $location) { if (!file_exists($location)) { throw new CompilerException('Location of dependency "' . $dependencyNs . '" does not exist. Check the config.json for more information'); } $this->addExternalDependency($dependencyNs, $location); } } /** * Check if there are module/request/global destructors */ $destructors = $this->config->get('destructors'); if (is_array($destructors)) { $invokeDestructors = $this->processCodeInjection($destructors); $includes = $invokeDestructors[0]; $destructors = $invokeDestructors[1]; } /** * Check if there are module/request/global initializers */ $initializers = $this->config->get('initializers'); if (is_array($initializers)) { $invokeInitializers = $this->processCodeInjection($initializers); $includes = $invokeInitializers[0]; $initializers = $invokeInitializers[1]; } /** * Round 1. pre-compile all files in memory */ $this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace)); if (!count($this->files)) { throw new Exception("Zephir files to compile couldn't be found. Did you add a first class to the extension?"); } /** * Round 2. Check 'extends' and 'implements' dependencies */ foreach ($this->files as $compileFile) { $compileFile->checkDependencies($this); } /** * Sort the files by dependency ranking */ $files = array(); $rankedFiles = array(); $this->calculateDependencies($this->files); foreach ($this->files as $rankFile) { $rank = $rankFile->getClassDefinition()->getDependencyRank(); $rankedFiles[$rank][] = $rankFile; } krsort($rankedFiles); foreach ($rankedFiles as $rank => $rankFiles) { $files = array_merge($files, $rankFiles); } $this->files = $files; /** * Convert C-constants into PHP constants */ $constantsSources = $this->config->get('constants-sources'); if (is_array($constantsSources)) { $this->loadConstantsSources($constantsSources); } /** * Set extension globals */ $globals = $this->config->get('globals'); if (is_array($globals)) { $this->setExtensionGlobals($globals); } /** * Load function optimizers */ if (self::$loadedPrototypes === false) { FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall'); $optimizerDirs = $this->config->get('optimizer-dirs'); if (is_array($optimizerDirs)) { foreach ($optimizerDirs as $directory) { FunctionCall::addOptimizerDir(realpath($directory)); } } if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) { /** * Load additional extension prototypes * @var $file \DirectoryIterator */ foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) { if (!$file->isDir()) { $extension = str_replace('.php', '', $file); if (!extension_loaded($extension)) { require $file->getRealPath(); } } } } /** * Load customer additional extension prototypes */ $prototypeDirs = $this->config->get('prototype-dir'); if (is_array($prototypeDirs)) { foreach ($prototypeDirs as $prototype => $prototypeDir) { /** * Check if the extension is installed */ if (!extension_loaded($prototype)) { $prototypeRealpath = realpath($prototypeDir); if ($prototypeRealpath) { foreach (new \DirectoryIterator($prototypeRealpath) as $file) { if (!$file->isDir()) { require $file->getRealPath(); } } } } } } self::$loadedPrototypes = true; } /** * Round 3. Compile all files to C sources */ $files = array(); $hash = ""; foreach ($this->files as $compileFile) { /** * Only compile classes in the local extension, ignore external classes */ if (!$compileFile->isExternal()) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; if ($method->isInitializer() && $method->isStatic()) { $this->internalInitializers[] = "\t" . $method->getName() . '(TSRMLS_C);'; } } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } } /** * Round 3.2. Compile anonymous classes */ foreach ($this->anonymousFiles as $compileFile) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } $hash = md5($hash); $this->compiledFiles = $files; /** * Round 3.3. Load extra C-sources */ $extraSources = $this->config->get('extra-sources'); if (is_array($extraSources)) { $this->extraFiles = $extraSources; } else { $this->extraFiles = array(); } /** * Round 3.4. Load extra classes sources */ $extraClasses = $this->config->get('extra-classes'); if (is_array($extraClasses)) { foreach ($extraClasses as $value) { if (isset($value['source'])) { $this->extraFiles[] = $value['source']; } } } /** * Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files */ $namespace = str_replace('\\', '_', $namespace); $extensionName = $this->config->get('extension-name'); if (empty($extensionName) || !is_string($extensionName)) { $extensionName = $namespace; } $needConfigure = $this->createConfigFiles($extensionName); $needConfigure |= $this->createProjectFiles($extensionName); $needConfigure |= $this->checkIfPhpized(); $version = self::getCurrentVersion(); /** * When a new file is added or removed we need to run configure again */ if (!$command instanceof CommandGenerate) { if (!$this->fileSystem->exists($version . '/compiled-files-sum')) { $needConfigure = true; $this->fileSystem->write($version . '/compiled-files-sum', $hash); } else { if ($this->fileSystem->read($version . '/compiled-files-sum') != $hash) { $needConfigure = true; $this->fileSystem->write($version . '/compiled-files-sum', $hash); } } } /** * Round 5. Generate concatenation functions */ $this->stringManager->genConcatCode(); $this->fcallManager->genFcallCode(); if ($this->config->get('stubs-run-after-generate', 'stubs')) { $this->stubs($command, true); } return $needConfigure; }