[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/core/ -> graphviz_api.php (source)

   1  <?php
   2  # MantisBT - A PHP based bugtracking system
   3  
   4  # MantisBT is free software: you can redistribute it and/or modify
   5  # it under the terms of the GNU General Public License as published by
   6  # the Free Software Foundation, either version 2 of the License, or
   7  # (at your option) any later version.
   8  #
   9  # MantisBT is distributed in the hope that it will be useful,
  10  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  # GNU General Public License for more details.
  13  #
  14  # You should have received a copy of the GNU General Public License
  15  # along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * GraphViz API
  19   *
  20   * Wrapper classes around GraphViz utilities (dot and neato) for
  21   * directed and undirected graph generation. These wrappers are enhanced
  22   * enough just to support relationship_graph_api.php. They don't
  23   * support subgraphs yet.
  24   *
  25   * The original Graphviz package is available at:
  26   *     - http://www.graphviz.org/
  27   * Additional documentation can be found at:
  28   *     - http://www.graphviz.org/Documentation.html
  29   *
  30   * @package CoreAPI
  31   * @subpackage GraphVizAPI
  32   * @author Juliano Ravasi Ferraz <jferraz at users sourceforge net>
  33   * @copyright Copyright (C) 2002 - 2011  MantisBT Team - mantisbt-dev@lists.sourceforge.net
  34   * @link http://www.mantisbt.org
  35   *
  36   * @uses constant_inc.php
  37   * @uses utility_api.php
  38   */
  39  
  40  require_api( 'constant_inc.php' );
  41  require_api( 'utility_api.php' );
  42  
  43  /**
  44   * constant(s) defining the output formats supported by dot and neato.
  45   */
  46  define( 'GRAPHVIZ_ATTRIBUTED_DOT', 0 );
  47  define( 'GRAPHVIZ_PS', 1 );
  48  define( 'GRAPHVIZ_HPGL', 2 );
  49  define( 'GRAPHVIZ_PCL', 3 );
  50  define( 'GRAPHVIZ_MIF', 4 );
  51  define( 'GRAPHVIZ_PLAIN', 6 );
  52  define( 'GRAPHVIZ_PLAIN_EXT', 7 );
  53  define( 'GRAPHVIZ_GIF', 11 );
  54  define( 'GRAPHVIZ_JPEG', 12 );
  55  define( 'GRAPHVIZ_PNG', 13 );
  56  define( 'GRAPHVIZ_WBMP', 14 );
  57  define( 'GRAPHVIZ_XBM', 15 );
  58  define( 'GRAPHVIZ_ISMAP', 16 );
  59  define( 'GRAPHVIZ_IMAP', 17 );
  60  define( 'GRAPHVIZ_CMAP', 18 );
  61  define( 'GRAPHVIZ_CMAPX', 19 );
  62  define( 'GRAPHVIZ_VRML', 20 );
  63  define( 'GRAPHVIZ_SVG', 25 );
  64  define( 'GRAPHVIZ_SVGZ', 26 );
  65  define( 'GRAPHVIZ_CANONICAL_DOT', 27 );
  66  define( 'GRAPHVIZ_PDF', 28 );
  67  
  68  /**
  69   * Base class for graph creation and manipulation. By default,
  70   * undirected graphs are generated. For directed graphs, use Digraph
  71   * class.
  72   * @package MantisBT
  73   * @subpackage classes
  74   */
  75  class Graph {
  76      var $name = 'G';
  77      var $attributes = array();
  78      var $default_node = null;
  79      var $default_edge = null;
  80      var $nodes = array();
  81      var $edges = array();
  82  
  83      var $graphviz_tool;
  84      var $graphviz_com_module;
  85  
  86      var $formats = array(
  87          'dot' => array(
  88              'binary' => false,
  89              'type' => GRAPHVIZ_ATTRIBUTED_DOT,
  90              'mime' => 'text/x-graphviz',
  91          ),
  92          'ps' => array(
  93              'binary' => false,
  94              'type' => GRAPHVIZ_PS,
  95              'mime' => 'application/postscript',
  96          ),
  97          'hpgl' => array(
  98              'binary' => true,
  99              'type' => GRAPHVIZ_HPGL,
 100              'mime' => 'application/vnd.hp-HPGL',
 101          ),
 102          'pcl' => array(
 103              'binary' => true,
 104              'type' => GRAPHVIZ_PCL,
 105              'mime' => 'application/vnd.hp-PCL',
 106          ),
 107          'mif' => array(
 108              'binary' => true,
 109              'type' => GRAPHVIZ_MIF,
 110              'mime' => 'application/vnd.mif',
 111          ),
 112          'gif' => array(
 113              'binary' => true,
 114              'type' => GRAPHVIZ_GIF,
 115              'mime' => 'image/gif',
 116          ),
 117          'jpg' => array(
 118              'binary' => false,
 119              'type' => GRAPHVIZ_JPEG,
 120              'mime' => 'image/jpeg',
 121          ),
 122          'jpeg' => array(
 123              'binary' => true,
 124              'type' => GRAPHVIZ_JPEG,
 125              'mime' => 'image/jpeg',
 126          ),
 127          'png' => array(
 128              'binary' => true,
 129              'type' => GRAPHVIZ_PNG,
 130              'mime' => 'image/png',
 131          ),
 132          'wbmp' => array(
 133              'binary' => true,
 134              'type' => GRAPHVIZ_WBMP,
 135              'mime' => 'image/vnd.wap.wbmp',
 136          ),
 137          'xbm' => array(
 138              'binary' => false,
 139              'type' => GRAPHVIZ_XBM,
 140              'mime' => 'image/x-xbitmap',
 141          ),
 142          'ismap' => array(
 143              'binary' => false,
 144              'type' => GRAPHVIZ_ISMAP,
 145              'mime' => 'text/plain',
 146          ),
 147          'imap' => array(
 148              'binary' => false,
 149              'type' => GRAPHVIZ_IMAP,
 150              'mime' => 'application/x-httpd-imap',
 151          ),
 152          'cmap' => array(
 153              'binary' => false,
 154              'type' => GRAPHVIZ_CMAP,
 155              'mime' => 'text/html',
 156          ),
 157          'cmapx' => array(
 158              'binary' => false,
 159              'type' => GRAPHVIZ_CMAPX,
 160              'mime' => 'application/xhtml+xml',
 161          ),
 162          'vrml' => array(
 163              'binary' => true,
 164              'type' => GRAPHVIZ_VRML,
 165              'mime' => 'x-world/x-vrml',
 166          ),
 167          'svg' => array(
 168              'binary' => false,
 169              'type' => GRAPHVIZ_SVG,
 170              'mime' => 'image/svg+xml',
 171          ),
 172          'svgz' => array(
 173              'binary' => true,
 174              'type' => GRAPHVIZ_SVGZ,
 175              'mime' => 'image/svg+xml',
 176          ),
 177          'pdf' => array(
 178              'binary' => true,
 179              'type' => GRAPHVIZ_PDF,
 180              'mime' => 'application/pdf',
 181          ),
 182      );
 183  
 184      /**
 185       * Constructor for Graph objects.
 186       * @param string $p_name
 187       * @param array $p_attributes
 188       * @param string $p_tool
 189       * @param string $p_com_module
 190       * @return null
 191       */
 192  	function Graph( $p_name = 'G', $p_attributes = array(), $p_tool = 'neato', $p_com_module = 'WinGraphviz.NEATO' ) {
 193          if( is_string( $p_name ) ) {
 194              $this->name = $p_name;
 195          }
 196  
 197          $this->set_attributes( $p_attributes );
 198  
 199          $this->graphviz_tool = $p_tool;
 200          $this->graphviz_com_module = $p_com_module;
 201      }
 202  
 203      /**
 204       * Sets graph attributes.
 205       * @param array $p_attributes
 206       * @return null
 207       */
 208  	function set_attributes( $p_attributes ) {
 209          if( is_array( $p_attributes ) ) {
 210              $this->attributes = $p_attributes;
 211          }
 212      }
 213  
 214      /**
 215       * Sets default attributes for all nodes of the graph.
 216       * @param array $p_attributes
 217       * @return null
 218       */
 219  	function set_default_node_attr( $p_attributes ) {
 220          if( is_array( $p_attributes ) ) {
 221              $this->default_node = $p_attributes;
 222          }
 223      }
 224  
 225      /**
 226       * Sets default attributes for all edges of the graph.
 227       * @param array $p_attributes
 228       * @return null
 229       */
 230  	 function set_default_edge_attr( $p_attributes ) {
 231          if( is_array( $p_attributes ) ) {
 232              $this->default_edge = $p_attributes;
 233          }
 234      }
 235  
 236      /**
 237       * Adds a node to the graph.
 238       * @param string $p_name
 239       * @param array $p_attributes
 240       * @return null
 241       */
 242  	 function add_node( $p_name, $p_attributes = array() ) {
 243          if( is_array( $p_attributes ) ) {
 244              $this->nodes[$p_name] = $p_attributes;
 245          }
 246      }
 247  
 248      /**
 249       * Adds an edge to the graph.
 250       * @param string $p_src
 251       * @param string $p_dst
 252       * @param array $p_attributes
 253       * @return null
 254       */
 255  	 function add_edge( $p_src, $p_dst, $p_attributes = array() ) {
 256          if( is_array( $p_attributes ) ) {
 257              $this->edges[] = array(
 258                  'src' => $p_src,
 259                  'dst' => $p_dst,
 260                  'attributes' => $p_attributes,
 261              );
 262          }
 263      }
 264  
 265      /**
 266       * Check if an edge is already present.
 267       * @param string $p_src
 268       * @param string $p_dst
 269       * @return bool
 270       */
 271  	function is_edge_present( $p_src, $p_dst ) {
 272          foreach( $this->edges as $t_edge ) {
 273              if( $t_edge['src'] == $p_src && $t_edge['dst'] == $p_dst ) {
 274                  return true;
 275              }
 276          }
 277          return false;
 278      }
 279  
 280      /**
 281       * Generates an undirected graph representation (suitable for neato).
 282       * @return null
 283       */
 284  	function generate() {
 285          echo 'graph ' . $this->name . ' {' . "\n";
 286  
 287          $this->_print_graph_defaults();
 288  
 289          foreach( $this->nodes as $t_name => $t_attr ) {
 290              $t_name = '"' . addcslashes( $t_name, "\0..\37\"\\" ) . '"';
 291              $t_attr = $this->_build_attribute_list( $t_attr );
 292              echo "\t" . $t_name . ' ' . $t_attr . ";\n";
 293          }
 294  
 295          foreach( $this->edges as $t_edge ) {
 296              $t_src = '"' . addcslashes( $t_edge['src'], "\0..\37\"\\" ) . '"';
 297              $t_dst = '"' . addcslashes( $t_edge['dst'], "\0..\37\"\\" ) . '"';
 298              $t_attr = $t_edge['attributes'];
 299              $t_attr = $this->_build_attribute_list( $t_attr );
 300              echo "\t" . $t_src . ' -- ' . $t_dst . ' ' . $t_attr . ";\n";
 301          }
 302  
 303          echo "};\n";
 304      }
 305  
 306      /**
 307       * Outputs a graph image or map in the specified format.
 308       * @param string $p_format
 309       * @param bool $p_headers
 310       * @return null
 311       */
 312  	function output( $p_format = 'dot', $p_headers = false ) {
 313          # Check if it is a recognized format.
 314          if( !isset( $this->formats[$p_format] ) ) {
 315              trigger_error( ERROR_GENERIC, ERROR );
 316          }
 317  
 318          $t_binary = $this->formats[$p_format]['binary'];
 319          $t_type = $this->formats[$p_format]['type'];
 320          $t_mime = $this->formats[$p_format]['mime'];
 321  
 322          # Send Content-Type header, if requested.
 323          if( $p_headers ) {
 324              header( 'Content-Type: ' . $t_mime );
 325          }
 326          # Retrieve the source dot document into a buffer
 327          ob_start();
 328          $this->generate();
 329          $t_dot_source = ob_get_contents();
 330          ob_end_clean();
 331  
 332          # Start dot process
 333  
 334          $t_command = $this->graphviz_tool . ' -T' . $p_format;
 335          $t_descriptors = array(
 336              0 => array( 'pipe', 'r', ),
 337              1 => array( 'pipe', 'w', ),
 338              2 => array( 'file', 'php://stderr', 'w', ),
 339              );
 340  
 341          $t_pipes = array();
 342          $t_proccess = proc_open( $t_command, $t_descriptors, $t_pipes );
 343  
 344          if( is_resource( $t_proccess ) ) {
 345              # Filter generated output through dot
 346              fwrite( $t_pipes[0], $t_dot_source );
 347              fclose( $t_pipes[0] );
 348  
 349              if( $p_headers ) {
 350                  # Headers were requested, use another output buffer to
 351                  # retrieve the size for Content-Length.
 352                  ob_start();
 353                  while( !feof( $t_pipes[1] ) ) {
 354                      echo fgets( $t_pipes[1], 1024 );
 355                  }
 356                  header( 'Content-Length: ' . ob_get_length() );
 357                  ob_end_flush();
 358              } else {
 359                  # No need for headers, send output directly.
 360                  while( !feof( $t_pipes[1] ) ) {
 361                      print( fgets( $t_pipes[1], 1024 ) );
 362                  }
 363              }
 364  
 365              fclose( $t_pipes[1] );
 366              proc_close( $t_proccess );
 367          }
 368      }
 369  
 370      /**
 371       * PROTECTED function to build a node or edge attribute list.
 372       * @param array $p_attributes
 373       * @return string
 374       */
 375  	function _build_attribute_list( $p_attributes ) {
 376          if( empty( $p_attributes ) ) {
 377              return '';
 378          }
 379  
 380          $t_result = array();
 381  
 382          foreach( $p_attributes as $t_name => $t_value ) {
 383              if( !preg_match( "/[a-zA-Z]+/", $t_name ) ) {
 384                  continue;
 385              }
 386  
 387              if( is_string( $t_value ) ) {
 388                  $t_value = '"' . addcslashes( $t_value, "\0..\37\"\\" ) . '"';
 389              }
 390              else if( is_integer( $t_value ) or is_float( $t_value ) ) {
 391                  $t_value = (string) $t_value;
 392              } else {
 393                  continue;
 394              }
 395  
 396              $t_result[] = $t_name . '=' . $t_value;
 397          }
 398  
 399          return '[ ' . join( ', ', $t_result ) . ' ]';
 400      }
 401  
 402      /**
 403       * PROTECTED function to print graph attributes and defaults.
 404       * @return null
 405       */
 406  	function _print_graph_defaults() {
 407          foreach( $this->attributes as $t_name => $t_value ) {
 408              if( !preg_match( "/[a-zA-Z]+/", $t_name ) ) {
 409                  continue;
 410              }
 411  
 412              if( is_string( $t_value ) ) {
 413                  $t_value = '"' . addcslashes( $t_value, "\0..\37\"\\" ) . '"';
 414              }
 415              else if( is_integer( $t_value ) or is_float( $t_value ) ) {
 416                  $t_value = (string) $t_value;
 417              } else {
 418                  continue;
 419              }
 420  
 421              echo "\t" . $t_name . '=' . $t_value . ";\n";
 422          }
 423  
 424          if( null !== $this->default_node ) {
 425              $t_attr = $this->_build_attribute_list( $this->default_node );
 426              echo "\t" . 'node ' . $t_attr . ";\n";
 427          }
 428  
 429          if( null !== $this->default_edge ) {
 430              $t_attr = $this->_build_attribute_list( $this->default_edge );
 431              echo "\t" . 'edge ' . $t_attr . ";\n";
 432          }
 433      }
 434  }
 435  
 436  /**
 437   * Directed graph creation and manipulation.
 438   * @package MantisBT
 439   * @subpackage classes
 440   */
 441  class Digraph extends Graph {
 442      /**
 443       * Constructor for Digraph objects.
 444       * @param string $p_name
 445       * @param array $p_attributes
 446       * @param string $p_tool
 447       * @param string $p_com_module
 448       * @return null
 449       */
 450  	function Digraph( $p_name = 'G', $p_attributes = array(), $p_tool = 'dot', $p_com_module = 'WinGraphviz.DOT' ) {
 451          parent::Graph( $p_name, $p_attributes, $p_tool, $p_com_module );
 452      }
 453  
 454      /**
 455       * Generates a directed graph representation (suitable for dot).
 456       */
 457  	function generate() {
 458          echo 'digraph ' . $this->name . ' {' . "\n";
 459  
 460          $this->_print_graph_defaults();
 461  
 462          foreach( $this->nodes as $t_name => $t_attr ) {
 463              $t_name = '"' . addcslashes( $t_name, "\0..\37\"\\" ) . '"';
 464              $t_attr = $this->_build_attribute_list( $t_attr );
 465              echo "\t" . $t_name . ' ' . $t_attr . ";\n";
 466          }
 467  
 468          foreach( $this->edges as $t_edge ) {
 469              $t_src = '"' . addcslashes( $t_edge['src'], "\0..\37\"\\" ) . '"';
 470              $t_dst = '"' . addcslashes( $t_edge['dst'], "\0..\37\"\\" ) . '"';
 471              $t_attr = $t_edge['attributes'];
 472              $t_attr = $this->_build_attribute_list( $t_attr );
 473              echo "\t" . $t_src . ' -> ' . $t_dst . ' ' . $t_attr . ";\n";
 474          }
 475  
 476          echo "};\n";
 477      }
 478  }


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7