0byt3m1n1
Path:
/
data
/
applications
/
aps
/
mantis
/
1.2.7-0
/
standard
/
htdocs
/
core
/
[
Home
]
File: graphviz_api.php
<?php # MantisBT - a php based bugtracking system # MantisBT is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # MantisBT is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with MantisBT. If not, see <http://www.gnu.org/licenses/>. /** * Wrapper classes around GraphViz utilities (dot and neato) for * directed and undirected graph generation. Under Windows, the COM * API provided by WinGraphviz is used. These wrappers are enhanced * enough just to support relationship_graph_api.php. They don't * support subgraphs yet. * * The original Graphviz package is available at: * - http://www.research.att.com/sw/tools/graphviz/ * WinGraphviz can be installed from: * - http://home.so-net.net.tw/oodtsen/wingraphviz/ * Additional documentation can be found at: * - http://www.graphviz.org/Documentation.html * @package CoreAPI * @subpackage GraphVizAPI * @author Juliano Ravasi Ferraz <jferraz at users sourceforge net> * @copyright Copyright (C) 2002 - 2011 MantisBT Team - mantisbt-dev@lists.sourceforge.net * @link http://www.mantisbt.org */ /** * constant(s) defining the output formats supported by dot and neato. */ define( 'GRAPHVIZ_ATTRIBUTED_DOT', 0 ); define( 'GRAPHVIZ_PS', 1 ); define( 'GRAPHVIZ_HPGL', 2 ); define( 'GRAPHVIZ_PCL', 3 ); define( 'GRAPHVIZ_MIF', 4 ); define( 'GRAPHVIZ_PLAIN', 6 ); define( 'GRAPHVIZ_PLAIN_EXT', 7 ); define( 'GRAPHVIZ_GIF', 11 ); define( 'GRAPHVIZ_JPEG', 12 ); define( 'GRAPHVIZ_PNG', 13 ); define( 'GRAPHVIZ_WBMP', 14 ); define( 'GRAPHVIZ_XBM', 15 ); define( 'GRAPHVIZ_ISMAP', 16 ); define( 'GRAPHVIZ_IMAP', 17 ); define( 'GRAPHVIZ_CMAP', 18 ); define( 'GRAPHVIZ_CMAPX', 19 ); define( 'GRAPHVIZ_VRML', 20 ); define( 'GRAPHVIZ_SVG', 25 ); define( 'GRAPHVIZ_SVGZ', 26 ); define( 'GRAPHVIZ_CANONICAL_DOT', 27 ); define( 'GRAPHVIZ_PDF', 28 ); /** * Base class for graph creation and manipulation. By default, * undirected graphs are generated. For directed graphs, use Digraph * class. * @package MantisBT * @subpackage classes */ class Graph { var $name = 'G'; var $attributes = array(); var $default_node = null; var $default_edge = null; var $nodes = array(); var $edges = array(); var $graphviz_tool; var $graphviz_com_module; var $formats = array( 'dot' => array( 'binary' => false, 'type' => GRAPHVIZ_ATTRIBUTED_DOT, 'mime' => 'text/x-graphviz', ), 'ps' => array( 'binary' => false, 'type' => GRAPHVIZ_PS, 'mime' => 'application/postscript', ), 'hpgl' => array( 'binary' => true, 'type' => GRAPHVIZ_HPGL, 'mime' => 'application/vnd.hp-HPGL', ), 'pcl' => array( 'binary' => true, 'type' => GRAPHVIZ_PCL, 'mime' => 'application/vnd.hp-PCL', ), 'mif' => array( 'binary' => true, 'type' => GRAPHVIZ_MIF, 'mime' => 'application/vnd.mif', ), 'gif' => array( 'binary' => true, 'type' => GRAPHVIZ_GIF, 'mime' => 'image/gif', ), 'jpg' => array( 'binary' => false, 'type' => GRAPHVIZ_JPEG, 'mime' => 'image/jpeg', ), 'jpeg' => array( 'binary' => true, 'type' => GRAPHVIZ_JPEG, 'mime' => 'image/jpeg', ), 'png' => array( 'binary' => true, 'type' => GRAPHVIZ_PNG, 'mime' => 'image/png', ), 'wbmp' => array( 'binary' => true, 'type' => GRAPHVIZ_WBMP, 'mime' => 'image/vnd.wap.wbmp', ), 'xbm' => array( 'binary' => false, 'type' => GRAPHVIZ_XBM, 'mime' => 'image/x-xbitmap', ), 'ismap' => array( 'binary' => false, 'type' => GRAPHVIZ_ISMAP, 'mime' => 'text/plain', ), 'imap' => array( 'binary' => false, 'type' => GRAPHVIZ_IMAP, 'mime' => 'application/x-httpd-imap', ), 'cmap' => array( 'binary' => false, 'type' => GRAPHVIZ_CMAP, 'mime' => 'text/html', ), 'cmapx' => array( 'binary' => false, 'type' => GRAPHVIZ_CMAPX, 'mime' => 'application/xhtml+xml', ), 'vrml' => array( 'binary' => true, 'type' => GRAPHVIZ_VRML, 'mime' => 'x-world/x-vrml', ), 'svg' => array( 'binary' => false, 'type' => GRAPHVIZ_SVG, 'mime' => 'image/svg+xml', ), 'svgz' => array( 'binary' => true, 'type' => GRAPHVIZ_SVGZ, 'mime' => 'image/svg+xml', ), 'pdf' => array( 'binary' => true, 'type' => GRAPHVIZ_PDF, 'mime' => 'application/pdf', ), ); /** * Constructor for Graph objects. * @param string $p_name * @param array $p_attributes * @param string $p_tool * @param string $p_com_module * @return null */ function Graph( $p_name = 'G', $p_attributes = array(), $p_tool = 'neato', $p_com_module = 'WinGraphviz.NEATO' ) { if( is_string( $p_name ) ) { $this->name = $p_name; } $this->set_attributes( $p_attributes ); $this->graphviz_tool = $p_tool; $this->graphviz_com_module = $p_com_module; } /** * Sets graph attributes. * @param array $p_attributes * @return null */ function set_attributes( $p_attributes ) { if( is_array( $p_attributes ) ) { $this->attributes = $p_attributes; } } /** * Sets default attributes for all nodes of the graph. * @param array $p_attributes * @return null */ function set_default_node_attr( $p_attributes ) { if( is_array( $p_attributes ) ) { $this->default_node = $p_attributes; } } /** * Sets default attributes for all edges of the graph. * @param array $p_attributes * @return null */ function set_default_edge_attr( $p_attributes ) { if( is_array( $p_attributes ) ) { $this->default_edge = $p_attributes; } } /** * Adds a node to the graph. * @param string $p_name * @param array $p_attributes * @return null */ function add_node( $p_name, $p_attributes = array() ) { if( is_array( $p_attributes ) ) { $this->nodes[$p_name] = $p_attributes; } } /** * Adds an edge to the graph. * @param string $p_src * @param string $p_dst * @param array $p_attributes * @return null */ function add_edge( $p_src, $p_dst, $p_attributes = array() ) { if( is_array( $p_attributes ) ) { $this->edges[] = array( 'src' => $p_src, 'dst' => $p_dst, 'attributes' => $p_attributes, ); } } /** * Check if an edge is already present. * @param string $p_src * @param string $p_dst * @return bool */ function is_edge_present( $p_src, $p_dst ) { foreach( $this->edges as $t_edge ) { if( $t_edge['src'] == $p_src && $t_edge['dst'] == $p_dst ) { return true; } } return false; } /** * Generates an undirected graph representation (suitable for neato). * @return null */ function generate() { echo 'graph ' . $this->name . ' {' . "\n"; $this->_print_graph_defaults(); foreach( $this->nodes as $t_name => $t_attr ) { $t_name = '"' . addcslashes( $t_name, "\0..\37\"\\" ) . '"'; $t_attr = $this->_build_attribute_list( $t_attr ); echo "\t" . $t_name . ' ' . $t_attr . ";\n"; } foreach( $this->edges as $t_edge ) { $t_src = '"' . addcslashes( $t_edge['src'], "\0..\37\"\\" ) . '"'; $t_dst = '"' . addcslashes( $t_edge['dst'], "\0..\37\"\\" ) . '"'; $t_attr = $t_edge['attributes']; $t_attr = $this->_build_attribute_list( $t_attr ); echo "\t" . $t_src . ' -- ' . $t_dst . ' ' . $t_attr . ";\n"; } echo "};\n"; } /** * Outputs a graph image or map in the specified format. * @param string $p_format * @param bool $p_headers * @return null */ function output( $p_format = 'dot', $p_headers = false ) { # Check if it is a recognized format. if( !isset( $this->formats[$p_format] ) ) { trigger_error( ERROR_GENERIC, ERROR ); } $t_binary = $this->formats[$p_format]['binary']; $t_type = $this->formats[$p_format]['type']; $t_mime = $this->formats[$p_format]['mime']; # Send Content-Type header, if requested. if( $p_headers ) { header( 'Content-Type: ' . $t_mime ); } # Retrieve the source dot document into a buffer ob_start(); $this->generate(); $t_dot_source = ob_get_contents(); ob_end_clean(); # There are three different ways to generate the output depending # on the operating system and PHP version. if( is_windows_server() ) { # If we are under Windows, we use the COM interface provided # by WinGraphviz. Thanks Paul! # Issue #4625: Work around WinGraphviz bug that fails with # graphs with zero or one node. It is probably too much to # generate a graphic output just to explain it to the user, # so we just return a null content. if( count( $this->nodes ) <= 1 ) { return; } $t_graphviz = new COM( $this->graphviz_com_module ); # Check if we managed to instantiate the COM object. if( is_null( $t_graphviz ) ) { # We can't display any message or trigger an error on # failure, since we may have already sent a Content-type # header potentially incompatible with the any html output. return; } if( $t_binary ) { # Image formats $t_dot_output = $t_graphviz->ToBinaryGraph( $t_dot_source, $t_type ); if( $p_headers ) { # Headers were requested, use another output buffer # to retrieve the size for Content-Length. ob_start(); echo base64_decode( $t_dot_output->ToBase64String() ); header( 'Content-Length: ' . ob_get_length() ); ob_end_flush(); } else { # No need for headers, send output directly. echo base64_decode( $ret->ToBase64String() ); } } else { # Text formats $t_dot_output = $t_graphviz->ToTextGraph( $t_dot_source, $t_type ); if( $p_headers ) { header( 'Content-Length: ' . utf8_strlen( $t_dot_output ) ); } echo $t_dot_output; } unset( $t_graphviz ); } else { # If we are not under Windows, use proc_open, # since it avoids the need of temporary files. # Start dot process $t_command = $this->graphviz_tool . ' -T' . $p_format; $t_descriptors = array( 0 => array( 'pipe', 'r', ), 1 => array( 'pipe', 'w', ), 2 => array( 'file', 'php://stderr', 'w', ), ); $t_pipes = array(); $t_proccess = proc_open( $t_command, $t_descriptors, $t_pipes ); if( is_resource( $t_proccess ) ) { # Filter generated output through dot fwrite( $t_pipes[0], $t_dot_source ); fclose( $t_pipes[0] ); if( $p_headers ) { # Headers were requested, use another output buffer to # retrieve the size for Content-Length. ob_start(); while( !feof( $t_pipes[1] ) ) { echo fgets( $t_pipes[1], 1024 ); } header( 'Content-Length: ' . ob_get_length() ); ob_end_flush(); } else { # No need for headers, send output directly. while( !feof( $t_pipes[1] ) ) { print( fgets( $t_pipes[1], 1024 ) ); } } fclose( $t_pipes[1] ); proc_close( $t_proccess ); } } } /** * PROTECTED function to build a node or edge attribute list. * @param array $p_attributes * @return string */ function _build_attribute_list( $p_attributes ) { if( empty( $p_attributes ) ) { return ''; } $t_result = array(); foreach( $p_attributes as $t_name => $t_value ) { if( !preg_match( "/[a-zA-Z]+/", $t_name ) ) { continue; } if( is_string( $t_value ) ) { $t_value = '"' . addcslashes( $t_value, "\0..\37\"\\" ) . '"'; } else if( is_integer( $t_value ) or is_float( $t_value ) ) { $t_value = (string) $t_value; } else { continue; } $t_result[] = $t_name . '=' . $t_value; } return '[ ' . join( ', ', $t_result ) . ' ]'; } /** * PROTECTED function to print graph attributes and defaults. * @return null */ function _print_graph_defaults() { foreach( $this->attributes as $t_name => $t_value ) { if( !preg_match( "/[a-zA-Z]+/", $t_name ) ) { continue; } if( is_string( $t_value ) ) { $t_value = '"' . addcslashes( $t_value, "\0..\37\"\\" ) . '"'; } else if( is_integer( $t_value ) or is_float( $t_value ) ) { $t_value = (string) $t_value; } else { continue; } echo "\t" . $t_name . '=' . $t_value . ";\n"; } if( null !== $this->default_node ) { $t_attr = $this->_build_attribute_list( $this->default_node ); echo "\t" . 'node ' . $t_attr . ";\n"; } if( null !== $this->default_edge ) { $t_attr = $this->_build_attribute_list( $this->default_edge ); echo "\t" . 'edge ' . $t_attr . ";\n"; } } } /** * Directed graph creation and manipulation. * @package MantisBT * @subpackage classes */ class Digraph extends Graph { /** * Constructor for Digraph objects. * @param string $p_name * @param array $p_attributes * @param string $p_tool * @param string $p_com_module * @return null */ function Digraph( $p_name = 'G', $p_attributes = array(), $p_tool = 'dot', $p_com_module = 'WinGraphviz.DOT' ) { parent::Graph( $p_name, $p_attributes, $p_tool, $p_com_module ); } /** * Generates a directed graph representation (suitable for dot). */ function generate() { echo 'digraph ' . $this->name . ' {' . "\n"; $this->_print_graph_defaults(); foreach( $this->nodes as $t_name => $t_attr ) { $t_name = '"' . addcslashes( $t_name, "\0..\37\"\\" ) . '"'; $t_attr = $this->_build_attribute_list( $t_attr ); echo "\t" . $t_name . ' ' . $t_attr . ";\n"; } foreach( $this->edges as $t_edge ) { $t_src = '"' . addcslashes( $t_edge['src'], "\0..\37\"\\" ) . '"'; $t_dst = '"' . addcslashes( $t_edge['dst'], "\0..\37\"\\" ) . '"'; $t_attr = $t_edge['attributes']; $t_attr = $this->_build_attribute_list( $t_attr ); echo "\t" . $t_src . ' -> ' . $t_dst . ' ' . $t_attr . ";\n"; } echo "};\n"; } }