PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/DocBlox/Plugin/Core/Transformer/Writer/Graph.php

https://github.com/studioskylab/Docblox
PHP | 259 lines | 166 code | 31 blank | 62 comment | 12 complexity | 968c83786211acc317542408e01052bc MD5 | raw file
  1. <?php
  2. /**
  3. * DocBlox
  4. *
  5. * PHP Version 5
  6. *
  7. * @category DocBlox
  8. * @package Transformer
  9. * @subpackage Writers
  10. * @author Mike van Riel <mike.vanriel@naenius.com>
  11. * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
  12. * @license http://www.opensource.org/licenses/mit-license.php MIT
  13. * @link http://docblox-project.org
  14. */
  15. /**
  16. * Class diagram generator.
  17. *
  18. * Checks whether graphviz is enabled and logs an error if not.
  19. *
  20. * @category DocBlox
  21. * @package Transformer
  22. * @subpackage Writers
  23. * @author Mike van Riel <mike.vanriel@naenius.com>
  24. * @license http://www.opensource.org/licenses/mit-license.php MIT
  25. * @link http://docblox-project.org
  26. */
  27. class DocBlox_Plugin_Core_Transformer_Writer_Graph
  28. extends DocBlox_Transformer_Writer_Abstract
  29. {
  30. protected $has_namespaces = false;
  31. /**
  32. * Generates an array containing class to path references and then invokes the Source specific method.
  33. *
  34. * @param DOMDocument $structure
  35. * @param DocBlox_Transformer_Transformation $transformation
  36. *
  37. * @return void
  38. */
  39. public function transform(DOMDocument $structure, DocBlox_Transformer_Transformation $transformation)
  40. {
  41. // NOTE: the -V flag sends output using STDERR and STDOUT
  42. exec('dot -V 2>&1', $output, $error);
  43. if ($error != 0) {
  44. $this->log(
  45. 'Unable to find the `dot` command of the GraphViz package. Is GraphViz correctly installed and present in your path?',
  46. Zend_Log::ERR
  47. );
  48. return;
  49. }
  50. // add to classes
  51. $xpath = new DOMXPath($structure);
  52. $qry = $xpath->query("/namespace[@name and @name != 'default']");
  53. if ($qry->length > 0) {
  54. $this->has_namespaces = true;
  55. }
  56. $qry = $xpath->query('//class[full_name]/..');
  57. $class_paths = array();
  58. /** @var DOMElement $element */
  59. foreach ($qry as $element) {
  60. $path = $element->getAttribute('generated-path');
  61. foreach ($element->getElementsByTagName('class') as $class) {
  62. $class_paths[$class->getElementsByTagName('full_name')->item(0)->nodeValue] = $path;
  63. }
  64. }
  65. // add to interfaces
  66. $qry = $xpath->query('//interface[full_name]/..');
  67. /** @var DOMElement $element */
  68. foreach ($qry as $element) {
  69. $path = $element->getAttribute('generated-path');
  70. foreach ($element->getElementsByTagName('interface') as $class) {
  71. $class_paths[$class->getElementsByTagName('full_name')->item(0)->nodeValue] = $path;
  72. }
  73. }
  74. $this->class_paths = $class_paths;
  75. $type_method = 'process'.ucfirst($transformation->getSource());
  76. $this->$type_method($structure, $transformation);
  77. }
  78. /**
  79. * Builds a tree of namespace subgraphs with their classes associated.
  80. *
  81. * @param DocBlox_GraphViz_Graph $graph
  82. * @param DOMElement $namespace_element
  83. * @param DOMXPath $xpath
  84. * @param string $full_namespace_name
  85. *
  86. * @return void
  87. */
  88. public function buildNamespaceTree(DocBlox_GraphViz_Graph $graph,
  89. DOMElement $namespace_element, DOMXPath $xpath, $full_namespace_name)
  90. {
  91. $namespace = $namespace_element->getAttribute('name');
  92. $full_namespace_name .= '\\'.$namespace;
  93. $full_namespace_name = ltrim($full_namespace_name, '\\');
  94. $sub_graph = DocBlox_GraphViz_Graph::create('cluster_' . str_replace(array('\\', '$'), '_', $full_namespace_name))
  95. ->setLabel($full_namespace_name != 'default' ? $namespace : '')
  96. ->setStyle('rounded')
  97. ->setColor($full_namespace_name != 'default' ? 'gray' : 'none')
  98. ->setFontColor('gray')
  99. ->setFontSize('11')
  100. ->setRankDir('LR');
  101. $sub_qry = $xpath->query(
  102. "/project/file/interface[@namespace='$full_namespace_name']|/project/file/class[@namespace='$full_namespace_name']"
  103. );
  104. /** @var DOMElement $sub_element */
  105. foreach ($sub_qry as $sub_element) {
  106. $node = DocBlox_GraphViz_Node::create(
  107. str_replace(array('\\', '$'), '_', $sub_element->getElementsByTagName('full_name')->item(0)->nodeValue),
  108. $sub_element->getElementsByTagName('name')->item(0)->nodeValue
  109. );
  110. $node->setShape('box');
  111. $node->setFontName('Courier New');
  112. $node->setFontSize('11');
  113. if ($sub_element->getAttribute('abstract') == 'true') {
  114. $node->setLabel('<«abstract»<br/>'. $sub_element->getElementsByTagName('name')->item(0)->nodeValue.'>');
  115. }
  116. if (!isset($this->class_paths[$sub_element->getElementsByTagName('full_name')->item(0)->nodeValue])) {
  117. echo $sub_element->getElementsByTagName('full_name')->item(0)->nodeValue.PHP_EOL;
  118. } else {
  119. $node->setURL($this->class_paths[$sub_element->getElementsByTagName('full_name')->item(0)->nodeValue]);
  120. $node->setTarget('_parent');
  121. }
  122. $sub_graph->setNode($node);
  123. }
  124. $graph->addGraph($sub_graph);
  125. foreach($namespace_element->getElementsByTagName('namespace') as $element) {
  126. $this->buildNamespaceTree($sub_graph, $element, $xpath, $full_namespace_name);
  127. }
  128. }
  129. /**
  130. * Creates a class inheritance diagram.
  131. *
  132. * @param DOMDocument $structure
  133. * @param DocBlox_Transformer_Transformation $transformation
  134. *
  135. * @return void
  136. */
  137. public function processClass(DOMDocument $structure, DocBlox_Transformer_Transformation $transformation)
  138. {
  139. $filename = $transformation->getTransformer()->getTarget() . DIRECTORY_SEPARATOR . $transformation->getArtifact();
  140. $graph = DocBlox_GraphViz_Graph::create()
  141. ->setRankSep('1.0')
  142. ->setCenter('true')
  143. ->setRank('source')
  144. ->setRankDir('RL')
  145. ->setSplines('true')
  146. ->setConcentrate('true');
  147. $xpath = new DOMXPath($structure);
  148. $qry = $xpath->query("/project/namespace");
  149. /** @var DOMElement $element */
  150. foreach($qry as $element) {
  151. $this->buildNamespaceTree($graph, $element, $xpath, '');
  152. }
  153. // link all extended relations
  154. $qry = $xpath->query('/project/file/interface[extends]|/project/file/class[extends]');
  155. /** @var DOMElement $element */
  156. foreach($qry as $element) {
  157. $from_name = $element->getElementsByTagName('full_name')->item(0)->nodeValue;
  158. $to_name = $element->getElementsByTagName('extends')->item(0)->nodeValue;
  159. if (!$to_name) {
  160. continue;
  161. }
  162. $from = $graph->findNode(str_replace(array('\\', '$'), '_', $from_name));
  163. $to = $graph->findNode(str_replace(array('\\', '$'), '_', $to_name));
  164. if ($from === null) {
  165. $from = DocBlox_GraphViz_Node::create(
  166. str_replace(array('\\', '$'), '_', $from_name)
  167. );
  168. $from->setFontColor('gray');
  169. $from->setLabel(addslashes($from_name));
  170. $graph->setNode($from);
  171. }
  172. if ($to === null) {
  173. $to = DocBlox_GraphViz_Node::create(
  174. str_replace(array('\\', '$'), '_', $to_name)
  175. );
  176. $to->setFontColor('gray');
  177. $to->setLabel(addslashes($to_name));
  178. $graph->setNode($to);
  179. }
  180. $edge = DocBlox_GraphViz_Edge::create($from, $to);
  181. $edge->setArrowHead('empty');
  182. $graph->link($edge);
  183. }
  184. // link all implemented relations
  185. $qry = $xpath->query('/project/file/interface[imports]|/project/file/class[implements]');
  186. /** @var DOMElement $element */
  187. foreach($qry as $element) {
  188. $from_name = $element->getElementsByTagName('full_name')->item(0)->nodeValue;
  189. foreach($element->getElementsByTagName('implements') as $implements) {
  190. $to_name = $implements->nodeValue;
  191. if (!$to_name) {
  192. continue;
  193. }
  194. $from = $graph->findNode(str_replace(array('\\', '$'), '_', $from_name));
  195. $to = $graph->findNode(str_replace(array('\\', '$'), '_', $to_name));
  196. if ($from === null)
  197. {
  198. $from = DocBlox_GraphViz_Node::create(str_replace(array('\\', '$'), '_', $from_name));
  199. $from->setFontColor('gray');
  200. $from->setLabel(addslashes($from_name));
  201. $graph->setNode($from);
  202. }
  203. if ($to === null) {
  204. $to = DocBlox_GraphViz_Node::create(str_replace(array('\\', '$'), '_', $to_name));
  205. $to->setFontColor('gray');
  206. $to->setLabel(addslashes($to_name));
  207. $graph->setNode($to);
  208. }
  209. $edge = DocBlox_GraphViz_Edge::create($from, $to);
  210. $edge->setStyle('dotted');
  211. $edge->setArrowHead('empty');
  212. $graph->link($edge);
  213. }
  214. }
  215. $graph->export('svg', $filename);
  216. $svg = simplexml_load_file($filename);
  217. $script = $svg->addChild('script');
  218. $script->addAttribute('xlink:href', 'js/SVGPan.js', 'http://www.w3.org/1999/xlink');
  219. // for the SVGPan file to work no viewBox may be defined and the id of the first <g> node must be renamed to 'viewport'
  220. unset($svg['viewBox']);
  221. $svg->g['id'] = 'viewport';
  222. $svg->asXML($filename);
  223. }
  224. }