configurator = $configurator; } /** * Create and return the source of a bundle based on given Configurator instance * * Options: * * - autoInclude: automatically load the source of the PHP renderer (default: true) * * @param string $className Name of the bundle class * @param array $options Associative array of optional settings * @return string PHP source for the bundle */ public function generate($className, array $options = []) { // Add default options $options += ['autoInclude' => true]; // Copy the PHP files header if applicable if ($this->configurator->rendering->engine instanceof PHP) { $this->configurator->rendering->engine->phpHeader = $this->configurator->phpHeader; } // Get the parser and renderer $objects = $this->configurator->finalize(); $parser = $objects['parser']; $renderer = $objects['renderer']; // Split the bundle's class name and its namespace $namespace = ''; if (preg_match('#(.*)\\\\([^\\\\]+)$#', $className, $m)) { $namespace = $m[1]; $className = $m[2]; } // Start with the standard header $php = []; $php[] = $this->configurator->phpHeader; if ($namespace) { $php[] = 'namespace ' . $namespace . ';'; $php[] = ''; } // Generate and append the bundle class $php[] = 'abstract class ' . $className . ' extends \\s9e\\TextFormatter\\Bundle'; $php[] = '{'; $php[] = ' /**'; $php[] = ' * @var s9e\\TextFormatter\\Parser Singleton instance used by parse()'; $php[] = ' */'; $php[] = ' protected static $parser;'; $php[] = ''; $php[] = ' /**'; $php[] = ' * @var s9e\\TextFormatter\\Renderer Singleton instance used by render()'; $php[] = ' */'; $php[] = ' protected static $renderer;'; $php[] = ''; // Add the event callbacks if applicable $events = [ 'beforeParse' => 'Callback executed before parse(), receives the original text as argument', 'afterParse' => 'Callback executed after parse(), receives the parsed text as argument', 'beforeRender' => 'Callback executed before render(), receives the parsed text as argument', 'afterRender' => 'Callback executed after render(), receives the output as argument', 'beforeUnparse' => 'Callback executed before unparse(), receives the parsed text as argument', 'afterUnparse' => 'Callback executed after unparse(), receives the original text as argument' ]; foreach ($events as $eventName => $eventDesc) { if (isset($options[$eventName])) { $php[] = ' /**'; $php[] = ' * @var ' . $eventDesc; $php[] = ' */'; $php[] = ' public static $' . $eventName . ' = ' . var_export($options[$eventName], true) . ';'; $php[] = ''; } } if (isset($objects['js'])) { $php[] = ' /**'; $php[] = ' * {@inheritdoc}'; $php[] = ' */'; $php[] = ' public static function getJS()'; $php[] = ' {'; $php[] = ' return ' . var_export($objects['js'], true) . ';'; $php[] = ' }'; $php[] = ''; } $php[] = ' /**'; $php[] = ' * {@inheritdoc}'; $php[] = ' */'; $php[] = ' public static function getParser()'; $php[] = ' {'; if (isset($options['parserSetup'])) { $php[] = ' $parser = ' . $this->exportObject($parser) . ';'; $php[] = ' ' . $this->exportCallback($namespace, $options['parserSetup'], '$parser') . ';'; $php[] = ''; $php[] = ' return $parser;'; } else { $php[] = ' return ' . $this->exportObject($parser) . ';'; } $php[] = ' }'; $php[] = ''; $php[] = ' /**'; $php[] = ' * {@inheritdoc}'; $php[] = ' */'; $php[] = ' public static function getRenderer()'; $php[] = ' {'; // If this is a PHP renderer and we know where it's saved, automatically load it as needed if (!empty($options['autoInclude']) && $this->configurator->rendering->engine instanceof PHP && isset($this->configurator->rendering->engine->lastFilepath)) { $className = get_class($renderer); $filepath = realpath($this->configurator->rendering->engine->lastFilepath); $php[] = ' if (!class_exists(' . var_export($className, true) . ', false)'; $php[] = ' && file_exists(' . var_export($filepath, true) . '))'; $php[] = ' {'; $php[] = ' include ' . var_export($filepath, true) . ';'; $php[] = ' }'; $php[] = ''; } if (isset($options['rendererSetup'])) { $php[] = ' $renderer = ' . $this->exportObject($renderer) . ';'; $php[] = ' ' . $this->exportCallback($namespace, $options['rendererSetup'], '$renderer') . ';'; $php[] = ''; $php[] = ' return $renderer;'; } else { $php[] = ' return ' . $this->exportObject($renderer) . ';'; } $php[] = ' }'; $php[] = '}'; return implode("\n", $php); } /** * Export a given callback as PHP code * * @param string $namespace Namespace in which the callback is execute * @param callable $callback Original callback * @param string $argument Callback's argument (as PHP code) * @return string PHP code */ protected function exportCallback($namespace, callable $callback, $argument) { if (is_array($callback) && is_string($callback[0])) { // Replace ['foo', 'bar'] with 'foo::bar' $callback = $callback[0] . '::' . $callback[1]; } if (!is_string($callback)) { return 'call_user_func(' . var_export($callback, true) . ', ' . $argument . ')'; } // Ensure that the callback starts with a \ if ($callback[0] !== '\\') { $callback = '\\' . $callback; } // Replace \foo\bar::baz() with bar::baz() if we're in namespace foo if (substr($callback, 0, 2 + strlen($namespace)) === '\\' . $namespace . '\\') { $callback = substr($callback, 2 + strlen($namespace)); } return $callback . '(' . $argument . ')'; } /** * Serialize and export a given object as PHP code * * @param object $obj Original object * @return string PHP code */ protected function exportObject($obj) { // Serialize the object $str = call_user_func($this->serializer, $obj); // Export the object's source $str = var_export($str, true); return $this->unserializer . '(' . $str . ')'; } }