PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/DocBlox/GraphViz/Graph.php

http://github.com/mvriel/Docblox
PHP | 364 lines | 136 code | 38 blank | 190 comment | 9 complexity | e5ad60efe6b73c99586cd9a54298984f MD5 | raw file
Possible License(s): LGPL-3.0, BSD-3-Clause, CC-BY-SA-3.0
  1. <?php
  2. /**
  3. * DocBlox
  4. *
  5. * PHP Version 5
  6. *
  7. * @category DocBlox
  8. * @package Core
  9. * @author Mike van Riel <mike.vanriel@naenius.com>
  10. * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
  11. * @license http://www.opensource.org/licenses/mit-license.php MIT
  12. * @link http://docblox-project.org
  13. */
  14. /**
  15. * Class representing a graph; this may be a main graph but also a subgraph.
  16. *
  17. * In case of a subgraph:
  18. * When the name of the subgraph is prefixed with _cluster_ then the contents
  19. * of this graph will be grouped and a border will be added. Otherwise it is
  20. * used as logical container to place defaults in.
  21. *
  22. * @category DocBlox
  23. * @package GraphViz
  24. * @author Mike van Riel <mike.vanriel@naenius.com>
  25. * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
  26. * @license http://www.opensource.org/licenses/mit-license.php MIT
  27. * @link http://docblox-project.org
  28. */
  29. class DocBlox_GraphViz_Graph
  30. {
  31. /** @var string Name of this graph */
  32. protected $name = 'G';
  33. /** @var string Type of this graph; may be digraph, graph or subgraph */
  34. protected $type = 'digraph';
  35. /** @var DocBlox_GraphViz_Attribute[] A list of attributes for this Graph */
  36. protected $attributes = array();
  37. /** @var DocBlox_GraphViz_Graph[] A list of subgraphs for this Graph */
  38. protected $graphs = array();
  39. /** @var DocBlox_GraphViz_Node[] A list of nodes for this Graph */
  40. protected $nodes = array();
  41. /** @var DocBlox_GraphViz_Edge[] A list of edges / arrows for this Graph */
  42. protected $edges = array();
  43. /**
  44. * Factory method to instantiate a Graph so that you can use fluent coding
  45. * to chain everything.
  46. *
  47. * @param string $name The name for this graph.
  48. * @param bool $directional Whether this is a directed or undirected graph.
  49. *
  50. * @return DocBlox_GraphViz_Graph
  51. */
  52. public static function create($name = 'G', $directional = true)
  53. {
  54. $graph = new self();
  55. $graph
  56. ->setName($name)
  57. ->setType($directional ? 'digraph' : 'graph');
  58. return $graph;
  59. }
  60. /**
  61. * Sets the name for this graph.
  62. *
  63. * If this is a subgraph you can prefix the name with _cluster_ to group all
  64. * contained nodes and add a border.
  65. *
  66. * @param string $name The new name for this graph.
  67. *
  68. * @return DocBlox_GraphViz_Graph
  69. */
  70. public function setName($name)
  71. {
  72. $this->name = $name;
  73. return $this;
  74. }
  75. /**
  76. * Returns the name for this Graph.
  77. *
  78. * @return string
  79. */
  80. public function getName()
  81. {
  82. return $this->name;
  83. }
  84. /**
  85. * Sets the type for this graph.
  86. *
  87. * @param string $type Must be either "digraph", "graph" or "subgraph".
  88. *
  89. * @throw InvalidArgumentException if $type is not "digraph", "graph" or
  90. * "subgraph".
  91. *
  92. * @return DocBlox_GraphViz_Graph
  93. */
  94. public function setType($type)
  95. {
  96. if (!in_array($type, array('digraph', 'graph', 'subgraph'))) {
  97. throw new InvalidArgumentException(
  98. 'The type for a graph must be either "digraph", "graph" or '
  99. . '"subgraph"'
  100. );
  101. }
  102. $this->type = $type;
  103. return $this;
  104. }
  105. /**
  106. * Returns the type of this Graph.
  107. *
  108. * @return string
  109. */
  110. public function getType()
  111. {
  112. return $this->type;
  113. }
  114. /**
  115. * Magic method to provide a getter/setter to add attributes on the Graph.
  116. *
  117. * Using this method we make sure that we support any attribute without
  118. * too much hassle. If the name for this method does not start with get
  119. * or set we return null.
  120. *
  121. * Set methods return this graph (fluent interface) whilst get methods
  122. * return the attribute value.
  123. *
  124. * @param string $name Name of the method including get/set
  125. * @param mixed[] $arguments The arguments, should be 1: the value
  126. *
  127. * @return DocBlox_GraphViz_Attribute[]|DocBlox_GraphViz_Graph|null
  128. */
  129. function __call($name, $arguments)
  130. {
  131. $key = strtolower(substr($name, 3));
  132. if (strtolower(substr($name, 0, 3)) == 'set') {
  133. $this->attributes[$key] = new DocBlox_GraphViz_Attribute(
  134. $key, $arguments[0]
  135. );
  136. return $this;
  137. }
  138. if (strtolower(substr($name, 0, 3)) == 'get') {
  139. return $this->attributes[$key];
  140. }
  141. return null;
  142. }
  143. /**
  144. * Adds a subgraph to this graph; automatically changes the type to subgraph.
  145. *
  146. * Please note that an index is maintained using the name of the subgraph.
  147. * Thus if you have 2 subgraphs with the same name that the first will be
  148. * overwritten by the latter.
  149. *
  150. * @param DocBlox_GraphViz_Graph $graph The graph to add onto this graph as
  151. * subgraph.
  152. *
  153. * @see DocBlox_GraphViz_Graph::create()
  154. *
  155. * @return DocBlox_GraphViz_Graph
  156. */
  157. public function addGraph(DocBlox_GraphViz_Graph $graph)
  158. {
  159. $graph->setType('subgraph');
  160. $this->graphs[$graph->getName()] = $graph;
  161. return $this;
  162. }
  163. /**
  164. * Checks whether a graph with a certain name already exists.
  165. *
  166. * @param string $name Name of the graph to find.
  167. *
  168. * @return bool
  169. */
  170. public function hasGraph($name)
  171. {
  172. return isset($this->graphs[$name]);
  173. }
  174. /**
  175. * Returns the subgraph with a given name.
  176. *
  177. * @param string $name Name of the requested graph.
  178. *
  179. * @return DocBlox_GraphViz_Graph
  180. */
  181. public function getGraph($name)
  182. {
  183. return $this->graphs[$name];
  184. }
  185. /**
  186. * Sets a node in the $nodes array; uses the name of the node as index.
  187. *
  188. * Nodes can be retrieved by retrieving the property with the same name.
  189. * Thus 'node1' can be retrieved by invoking: $graph->node1
  190. *
  191. * @param DocBlox_GraphViz_Node $node The node to set onto this Graph.
  192. *
  193. * @see DocBlox_GraphViz_Node::create()
  194. *
  195. * @return DocBlox_GraphViz_Graph
  196. */
  197. public function setNode(DocBlox_GraphViz_Node $node)
  198. {
  199. $this->nodes[$node->getName()] = $node;
  200. return $this;
  201. }
  202. /**
  203. * Finds a node in this graph or any of its subgraphs.
  204. *
  205. * @param string $name Name of the node to find.
  206. *
  207. * @return DocBlox_GraphViz_Node
  208. */
  209. public function findNode($name)
  210. {
  211. if (isset($this->nodes[$name])) {
  212. return $this->nodes[$name];
  213. }
  214. foreach ($this->graphs as $graph) {
  215. $node = $graph->findNode($name);
  216. if ($node) {
  217. return $node;
  218. }
  219. }
  220. return null;
  221. }
  222. /**
  223. * Sets a node using a custom name.
  224. *
  225. * @param string $name Name of the node.
  226. * @param DocBlox_GraphViz_Node $value Node to set on the given name.
  227. *
  228. * @see DocBlox_GraphViz_Graph::setNode()
  229. *
  230. * @return void
  231. */
  232. function __set($name, $value)
  233. {
  234. $this->setNode($name, $value);
  235. }
  236. /**
  237. * Returns the requested node by its name.
  238. *
  239. * @param string $name The name of the node to retrieve.
  240. *
  241. * @see DocBlox_GraphViz_Graph::setNode()
  242. *
  243. * @return DocBlox_GraphViz_Node
  244. */
  245. function __get($name)
  246. {
  247. return isset($this->nodes[$name]) ? $this->nodes[$name] : null;
  248. }
  249. /**
  250. * Links two nodes to eachother and registers the Edge onto this graph.
  251. *
  252. * @param DocBlox_GraphViz_Edge $edge The link between two classes.
  253. *
  254. * @see DocBlox_GraphViz_Edge::create()
  255. *
  256. * @return DocBlox_GraphViz_Graph
  257. */
  258. public function link(DocBlox_GraphViz_Edge $edge)
  259. {
  260. $this->edges[] = $edge;
  261. return $this;
  262. }
  263. /**
  264. * Exports this graph to a generated image.
  265. *
  266. * This is the only method that actually requires GraphViz.
  267. *
  268. * @param string $type The type to export to; see the link above for a
  269. * list of supported types.
  270. * @param string $filename The path to write to.
  271. *
  272. * @uses GraphViz/dot
  273. *
  274. * @link http://www.graphviz.org/content/output-formats
  275. *
  276. * @throws DocBlox_GraphViz_Exception if an error occurred in GraphViz.
  277. *
  278. * @return DocBlox_GraphViz_Graph
  279. */
  280. public function export($type, $filename)
  281. {
  282. $type = escapeshellarg($type);
  283. $filename = escapeshellarg($filename);
  284. // write the dot file to a temporary file
  285. $tmpfile = tempnam(sys_get_temp_dir(), 'gvz');
  286. file_put_contents($tmpfile, (string)$this);
  287. // escape the temp file for use as argument
  288. $tmpfile_arg = escapeshellarg($tmpfile);
  289. // create the dot output
  290. $output = array();
  291. $code = 0;
  292. exec("dot -T$type -o$filename < $tmpfile_arg 2>&1", $output, $code);
  293. unlink($tmpfile);
  294. if ($code != 0) {
  295. throw new DocBlox_GraphViz_Exception(
  296. 'An error occurred while creating the graph; GraphViz returned: '
  297. . implode(PHP_EOL, $output)
  298. );
  299. }
  300. return $this;
  301. }
  302. /**
  303. * Generates a DOT file for use with GraphViz.
  304. *
  305. * GraphViz is not used in this method; it is safe to call it even without
  306. * GraphViz installed.
  307. *
  308. * @return string
  309. */
  310. public function __toString()
  311. {
  312. $elements = array_merge(
  313. $this->graphs, $this->attributes, $this->edges, $this->nodes
  314. );
  315. $attributes = array();
  316. foreach ($elements as $value) {
  317. $attributes[] = (string)$value;
  318. }
  319. $attributes = implode(PHP_EOL, $attributes);
  320. return <<<DOT
  321. {$this->getType()} "{$this->getName()}" {
  322. $attributes
  323. }
  324. DOT;
  325. }
  326. }