/** * Creates a class inheritance diagram. * * @param ProjectDescriptor $project * @param Transformation $transformation * * @return void */ public function processClass(ProjectDescriptor $project, Transformation $transformation) { try { $this->checkIfGraphVizIsInstalled(); } catch (\Exception $e) { echo $e->getMessage(); return; } if ($transformation->getParameter('font') !== null && $transformation->getParameter('font')->getValue()) { $this->nodeFont = $transformation->getParameter('font')->getValue(); } else { $this->nodeFont = 'Courier'; } $filename = $this->getDestinationPath($transformation); $graph = GraphVizGraph::create()->setRankSep('1.0')->setCenter('true')->setRank('source')->setRankDir('RL')->setSplines('true')->setConcentrate('true'); $this->buildNamespaceTree($graph, $project->getNamespace()); $classes = $project->getIndexes()->get('classes', new Collection())->getAll(); $interfaces = $project->getIndexes()->get('interfaces', new Collection())->getAll(); $traits = $project->getIndexes()->get('traits', new Collection())->getAll(); /** @var ClassDescriptor[]|InterfaceDescriptor[]|TraitDescriptor[] $containers */ $containers = array_merge($classes, $interfaces, $traits); foreach ($containers as $container) { $from_name = $container->getFullyQualifiedStructuralElementName(); $parents = array(); $implemented = array(); if ($container instanceof ClassDescriptor) { if ($container->getParent()) { $parents[] = $container->getParent(); } $implemented = $container->getInterfaces()->getAll(); } if ($container instanceof InterfaceDescriptor) { $parents = $container->getParent()->getAll(); } /** @var string|ClassDescriptor|InterfaceDescriptor $parent */ foreach ($parents as $parent) { $edge = $this->createEdge($graph, $from_name, $parent); $edge->setArrowHead('empty'); $graph->link($edge); } /** @var string|ClassDescriptor|InterfaceDescriptor $parent */ foreach ($implemented as $parent) { $edge = $this->createEdge($graph, $from_name, $parent); $edge->setStyle('dotted'); $edge->setArrowHead('empty'); $graph->link($edge); } } $graph->export('svg', $filename); }
/** * Calls the wkhtmltopdf executable to generate a PDF. * * @param \DOMDocument $structure Structure source * use as basis for the transformation. * @param \phpDocumentor\Transformer\Transformation $transformation Transformation * that supplies the meta-data for this writer. * * @return void */ public function transform(\DOMDocument $structure, \phpDocumentor\Transformer\Transformation $transformation) { $artifact = $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getArtifact(); $transformation->setArtifact($artifact); $source = substr($transformation->getSource(), 0, 1) != DIRECTORY_SEPARATOR ? $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getSource() : $transformation->getSource(); $transformation->setSource($source); $options = ''; if ($transformation->getParameter('toc', 'false') == 'true') { $options = ' toc '; } // TODO: add parameter to provide a cover HTML // TODO: add a parameter to provide a header HTML // TODO: add a parameter to provide a footer HTML // first try if there is a wkhtmltopdf in the global path, this helps // windows users exec('wkhtmltopdf ' . $options . ' ' . $transformation->getSource() . ' ' . $transformation->getArtifact() . ' 2>&1', $output, $error); $output = implode(PHP_EOL, $output); // this notice is linux specific; if it is found no global wkhtmltopdf // was installed; try the one which is included with phpDocumentor if (strpos($output, 'wkhtmltopdf: not found') !== false) { // TODO: replace the below with a decent way to find the executable exec(dirname(__FILE__) . '/../../../src/wkhtmltopdf/wkhtmltopdf-i386 ' . $options . ' ' . $transformation->getSource() . ' ' . $transformation->getArtifact() . ' 2>&1', $output, $error); $output = implode(PHP_EOL, $output) . PHP_EOL; } // log message and output $this->log('Generating PDF file ' . $transformation->getArtifact() . ' from ' . $transformation->getSource()); $this->log($output, $error == 0 ? \phpDocumentor\Plugin\Core\Log::INFO : \phpDocumentor\Plugin\Core\Log::CRIT); // CRASH! if ($error != 0) { throw new \phpDocumentor\Plugin\Core\Exception('Conversion to PDF failed, see output for details'); } }
/** * Generates an array containing class to path references and then invokes * the Source specific method. * * @param \DOMDocument $structure Structure source * use as basis for the transformation. * @param \phpDocumentor\Transformer\Transformation $transformation Transformation * that supplies the meta-data for this writer. * * @return void */ public function transform(\DOMDocument $structure, \phpDocumentor\Transformer\Transformation $transformation) { // NOTE: the -V flag sends output using STDERR and STDOUT exec('dot -V 2>&1', $output, $error); if ($error != 0) { $this->log('Unable to find the `dot` command of the GraphViz package. ' . 'Is GraphViz correctly installed and present in your path?', \phpDocumentor\Plugin\Core\Log::ERR); return; } $this->node_font = $transformation->getParameter('font', 'Courier'); // add to classes $xpath = new \DOMXPath($structure); $qry = $xpath->query('//class[full_name]/..'); $class_paths = array(); /** @var \DOMElement $element */ foreach ($qry as $element) { $path = $element->getAttribute('generated-path'); foreach ($element->getElementsByTagName('class') as $class) { $class_paths[$class->getElementsByTagName('full_name')->item(0)->nodeValue] = $path; } } // add to interfaces $qry = $xpath->query('//interface[full_name]/..'); /** @var \DOMElement $element */ foreach ($qry as $element) { $path = $element->getAttribute('generated-path'); foreach ($element->getElementsByTagName('interface') as $class) { $class_paths[$class->getElementsByTagName('full_name')->item(0)->nodeValue] = $path; } } $this->class_paths = $class_paths; $type_method = 'process' . ucfirst($transformation->getSource()); $this->{$type_method}($structure, $transformation); }
/** * This method combines the structure.xml and the given target template * and creates a static html page at the artifact location. * * @param \DOMDocument $structure XML source. * @param \phpDocumentor\Transformer\Transformation $transformation Transformation. * * @throws \Exception * * @return void */ public function transform(\DOMDocument $structure, \phpDocumentor\Transformer\Transformation $transformation) { if (!class_exists('XSLTProcessor')) { throw new \phpDocumentor\Plugin\Core\Exception('The XSL writer was unable to find your XSLTProcessor; ' . 'please check if you have installed the PHP XSL extension'); } $artifact = $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getArtifact(); $xsl = new \DOMDocument(); $xsl->load($transformation->getSourceAsPath()); $proc = new \XSLTProcessor(); $proc->importStyleSheet($xsl); if (empty($structure->documentElement)) { throw new \phpDocumentor\Plugin\Core\Exception('Specified DOMDocument lacks documentElement, cannot transform'); } $proc->setParameter('', 'title', $structure->documentElement->getAttribute('title')); $proc->setParameter('', 'root', str_repeat('../', substr_count($transformation->getArtifact(), '/'))); $proc->setParameter('', 'search_template', $transformation->getParameter('search', 'none')); $proc->setParameter('', 'version', \phpDocumentor\Application::VERSION); $proc->setParameter('', 'generated_datetime', date('r')); // check parameters for variables and add them when found $this->setProcessorParameters($transformation, $proc); // if a query is given, then apply a transformation to the artifact // location by replacing ($<var>} with the sluggified node-value of the // search result if ($transformation->getQuery() !== '') { $xpath = new \DOMXPath($transformation->getTransformer()->getSource()); /** @var \DOMNodeList $qry */ $qry = $xpath->query($transformation->getQuery()); $count = $qry->length; foreach ($qry as $key => $element) { \phpDocumentor\Plugin\EventDispatcher::getInstance()->dispatch('transformer.writer.xsl.pre', \phpDocumentor\Transformer\Events\PreXslWriterEvent::createInstance($this)->setElement($element)->setProgress(array($key + 1, $count))); $proc->setParameter('', $element->nodeName, $element->nodeValue); $file_name = $transformation->getTransformer()->generateFilename($element->nodeValue); $filename = str_replace('{$' . $element->nodeName . '}', $file_name, $artifact); $this->log('Processing the file: ' . $element->nodeValue . ' as ' . $filename); if (!file_exists(dirname($filename))) { mkdir(dirname($filename), 0755, true); } $proc->transformToURI($structure, $this->getXsltUriFromFilename($filename)); } } else { if (substr($transformation->getArtifact(), 0, 1) == '$') { // not a file, it must become a variable! $variable_name = substr($transformation->getArtifact(), 1); $this->xsl_variables[$variable_name] = $proc->transformToXml($structure); } else { if (!file_exists(dirname($artifact))) { mkdir(dirname($artifact), 0755, true); } $proc->transformToURI($structure, $this->getXsltUriFromFilename($artifact)); } } }
/** * Tries to add any custom extensions that have been defined in the template or the transformation's configuration. * * This method will read the `twig-extension` parameter of the transformation (which inherits the template's * parameter set) and try to add those extensions to the environment. * * @param Transformation $transformation * @param ProjectDescriptor $project * @param \Twig_Environment $twigEnvironment * * @throws \InvalidArgumentException if a twig-extension should be loaded but it could not be found. * * @return void */ protected function addExtensionsFromTemplateConfiguration(Transformation $transformation, ProjectDescriptor $project, \Twig_Environment $twigEnvironment) { /** @var \SimpleXMLElement $extension */ foreach ((array) $transformation->getParameter('twig-extension', array()) as $extension) { $extension = (string) $extension; if (!class_exists($extension)) { throw new \InvalidArgumentException('Unknown twig extension: ' . $extension); } // to support 'normal' Twig extensions we check the interface to determine what instantiation to do. $implementsInterface = in_array('phpDocumentor\\Plugin\\Core\\Twig\\ExtensionInterface', class_implements($extension)); $twigEnvironment->addExtension($implementsInterface ? new $extension($project, $transformation) : new $extension()); } }
/** * Tries to add any custom extensions that have been defined in the * template or the transformation's configuration. * * This method will read the `twig-extension` parameter of the * transformation (which inherits the template's parameter set) and * try to add those extensions to the environment. * * @param Transformation $transformation * @param \SimpleXMLElement $structure * @param \Twig_Environment $env * * @throws \InvalidArgumentException if a twig-extension should be loaded * but it could not be found. * * @return void */ protected function addExtensionsFromTemplateConfiguration($transformation, $structure, $env) { /** @var \SimpleXMLElement $extension */ foreach ((array) $transformation->getParameter('twig-extension', array()) as $extension) { $extension = (string) $extension; if (!class_exists($extension)) { throw new \InvalidArgumentException('Unknown twig extension: ' . $extension); } // to support 'normal' Twig extensions we check the interface // to determine what instantiation to do. $implements_interface = in_array('phpDocumentor\\Plugin\\Twig\\ExtensionInterface', class_implements($extension)); $extension_object = $implements_interface ? new $extension($structure, $transformation) : new $extension(); $env->addExtension($extension_object); } }
/** * Tries to add any custom extensions that have been defined in the template or the transformation's configuration. * * This method will read the `twig-extension` parameter of the transformation (which inherits the template's * parameter set) and try to add those extensions to the environment. * * @param Transformation $transformation * @param ProjectDescriptor $project * @param \Twig_Environment $twigEnvironment * * @throws \InvalidArgumentException if a twig-extension should be loaded but it could not be found. * * @return void */ protected function addExtensionsFromTemplateConfiguration(Transformation $transformation, ProjectDescriptor $project, \Twig_Environment $twigEnvironment) { $isDebug = $transformation->getParameter('twig-debug') ? $transformation->getParameter('twig-debug')->getValue() : false; if ($isDebug == 'true') { $twigEnvironment->enableDebug(); $twigEnvironment->enableAutoReload(); $twigEnvironment->addExtension(new \Twig_Extension_Debug()); } /** @var Template\Parameter $extension */ foreach ($transformation->getParametersWithKey('twig-extension') as $extension) { $extensionValue = $extension->getValue(); if (!class_exists($extensionValue)) { throw new \InvalidArgumentException('Unknown twig extension: ' . $extensionValue); } // to support 'normal' Twig extensions we check the interface to determine what instantiation to do. $implementsInterface = in_array('phpDocumentor\\Plugin\\Twig\\ExtensionInterface', class_implements($extensionValue)); $twigEnvironment->addExtension($implementsInterface ? new $extensionValue($project, $transformation) : new $extensionValue()); } }
/** * @param Transformation $transformation * @param $proc * @param $structure */ private function registerDefaultVariables(Transformation $transformation, $proc, $structure) { $proc->setParameter('', 'title', $structure->documentElement->getAttribute('title')); if ($transformation->getParameter('search') !== null && $transformation->getParameter('search')->getValue()) { $proc->setParameter('', 'search_template', $transformation->getParameter('search')->getValue()); } else { $proc->setParameter('', 'search_template', 'none'); } $proc->setParameter('', 'version', Application::$VERSION); $proc->setParameter('', 'generated_datetime', date('r')); }
/** * This method combines the structure.xml and the given target template * and creates a static html page at the artifact location. * * @param ProjectDescriptor $project Document containing the structure. * @param Transformation $transformation Transformation to execute. * * @return void */ public function transform(ProjectDescriptor $project, Transformation $transformation) { if (!class_exists('XSLTProcessor')) { throw new Exception('The XSL writer was unable to find your XSLTProcessor; ' . 'please check if you have installed the PHP XSL extension'); } $artifact = $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getArtifact(); $xsl = new \DOMDocument(); $xsl->load($transformation->getSourceAsPath()); $structureFilename = $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . 'structure.xml'; if (!is_readable($structureFilename)) { throw new RuntimeException('Structure.xml file was not found in the target directory, is the XML writer missing from the ' . 'template definition?'); } // load the structure file (ast) $structure = new \DOMDocument('1.0', 'utf-8'); libxml_use_internal_errors(true); $structure->load($structureFilename); $proc = new \XSLTProcessor(); $proc->importStyleSheet($xsl); if (empty($structure->documentElement)) { $message = 'Specified DOMDocument lacks documentElement, cannot transform.'; if (libxml_get_last_error()) { $message .= PHP_EOL . 'Apparently an error occurred with reading the structure.xml file, the reported ' . 'error was "' . trim(libxml_get_last_error()->message) . '" on line ' . libxml_get_last_error()->line; } throw new Exception($message); } $proc->setParameter('', 'title', $structure->documentElement->getAttribute('title')); $proc->setParameter('', 'root', str_repeat('../', substr_count($transformation->getArtifact(), '/'))); $proc->setParameter('', 'search_template', $transformation->getParameter('search', 'none')); $proc->setParameter('', 'version', \phpDocumentor\Application::$VERSION); $proc->setParameter('', 'generated_datetime', date('r')); // check parameters for variables and add them when found $this->setProcessorParameters($transformation, $proc); // if a query is given, then apply a transformation to the artifact // location by replacing ($<var>} with the sluggified node-value of the // search result if ($transformation->getQuery() !== '') { $xpath = new \DOMXPath($structure); /** @var \DOMNodeList $qry */ $qry = $xpath->query($transformation->getQuery()); $count = $qry->length; foreach ($qry as $key => $element) { \phpDocumentor\Event\Dispatcher::getInstance()->dispatch('transformer.writer.xsl.pre', \phpDocumentor\Transformer\Event\PreXslWriterEvent::createInstance($this)->setElement($element)->setProgress(array($key + 1, $count))); $proc->setParameter('', $element->nodeName, $element->nodeValue); $file_name = $transformation->getTransformer()->generateFilename($element->nodeValue); $filename = str_replace('{$' . $element->nodeName . '}', $file_name, $artifact); if (!file_exists(dirname($filename))) { mkdir(dirname($filename), 0755, true); } $proc->transformToURI($structure, $this->getXsltUriFromFilename($filename)); } } else { if (substr($transformation->getArtifact(), 0, 1) == '$') { // not a file, it must become a variable! $variable_name = substr($transformation->getArtifact(), 1); $this->xsl_variables[$variable_name] = $proc->transformToXml($structure); } else { if (!file_exists(dirname($artifact))) { mkdir(dirname($artifact), 0755, true); } $proc->transformToURI($structure, $this->getXsltUriFromFilename($artifact)); } } }