Anyone diagnosing network latency or path routing knows that running traceroute from the command line is a standard daily task. However, when presenting network routes to non-technical users or client dashboards, raw terminal text is not always intuitive.
To make route inspections clearer, I built a lightweight PHP web utility that executes traceroute, highlights hop latency with dynamic color coding, and renders a visual directed graph illustrating the complete path to the destination.
During testing, I hit a common infrastructure pitfall on minimal Linux servers: the missing system binary required to compile DOT graph files into PNG images.
Here is how the tool was designed, how to diagnose the dependency issue, and the complete PHP code.
1. Application logic and the initial error#
The planned application flow:
- Receive a target IP address or hostname via a
POSTform. - Strictly validate input to avoid command injection vulnerabilities.
- Run the native system
traceroutebinary. - Parse the output line by line: extract hop numbers, hostnames/IPs, and round-trip times (RTT in milliseconds).
- Generate a directed graph descriptor in the DOT language.
- Invoke a system utility to compile the
.dotfile into a.pngbitmap.
The image generation snippet in PHP was written as follows:
// Save the DOT graph structure to disk
file_put_contents($dotFilePath, $dot);
// Compile the DOT graph into a PNG image
exec("dot -Tpng " . escapeshellarg($dotFilePath) . " -o " . escapeshellarg($imageFilePath));
During testing, input validation succeeded, traceroute executed, and the .dot file was written to disk. However, the PNG image was never created, triggering the fallback error:
Erro ao gerar o gráfico.
2. The diagnosis: missing Graphviz binary#
The failure occurred because the dot -Tpng ... command requires a system tool that is not installed by default on minimal Linux server images: Graphviz.
PHP can assemble the DOT syntax natively without any extensions:
digraph G {
rankdir=LR;
"Inicio" -> "gateway.local (1.12 ms)";
"gateway.local (1.12 ms)" -> "10.0.0.1 (4.50 ms)";
"10.0.0.1 (4.50 ms)" -> "yourdomain.com (12.30 ms)";
}
However, compiling that textual representation into an actual image (.png or .svg) requires Graphviz's dot layout engine. Without this package, exec() returns a non-zero exit code and no image file is created.
3. Installing Graphviz on the server#
To enable graph rendering for your web server process (www-data, nginx, or nobody), install Graphviz:
On Debian / Ubuntu distributions:
sudo apt-get update
sudo apt-get install -y graphviz
On RHEL / AlmaLinux / Rocky Linux distributions:
sudo dnf install -y graphviz
Once installed, the dot binary becomes available in the system's global $PATH.
4. Complete PHP implementation#
Below is the complete, secure script that validates user input, captures traceroute output, builds the DOT graph, color-codes response times, and renders the diagram:
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 1. Strict validation of received Hostname or IP
function isValidHostname($hostname) {
return filter_var($hostname, FILTER_VALIDATE_IP) !== false ||
filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
}
$hostname = trim($_POST['hostname'] ?? '');
if (!isValidHostname($hostname)) {
echo "<div class='alert alert-warning'>Please enter a valid IP address or hostname.</div>";
exit;
}
// 2. Safe execution of traceroute
$command = escapeshellcmd('traceroute ' . $hostname);
$output = [];
$return_var = 0;
exec($command . ' 2>&1', $output, $return_var);
if ($return_var !== 0) {
$error_message = htmlspecialchars(implode("\n", $output));
echo "<div class='alert alert-danger'>Error executing traceroute: <pre>$error_message</pre></div>";
} else {
$header = array_shift($output);
$formatted_output = '';
$lines = explode("\n", implode("\n", $output));
// 3. Initialize Graph structure (DOT language)
$dot = "digraph G {\n";
$dot .= "rankdir=LR;\n"; // Left-to-right horizontal layout
$previous_hop = "Start";
// Sanitize string for filenames
$dotname = preg_replace('/[^a-zA-Z0-9_-]/', '_', $hostname);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line) || strpos($line, 'traceroute') !== false) {
continue;
}
$parts = preg_split('/\s+/', $line);
// Check if the line begins with a hop number
if (isset($parts[0]) && is_numeric($parts[0])) {
$hop_number = $parts[0];
$hop_name = $parts[1] ?? "Unknown";
$latencies = [];
// Extract round-trip times in milliseconds
foreach ($parts as $part) {
if (preg_match('/(\d+\.\d+)\s*ms/', $part, $matches)) {
$latencies[] = $matches[1] . " ms";
}
}
$latency_str = implode(", ", $latencies);
$node_label = "{$hop_name} ({$latency_str})";
// Link previous node to current node in the graph
$dot .= "\"{$previous_hop}\" -> \"{$node_label}\";\n";
$previous_hop = $node_label;
unset($parts[0]);
$line_content = implode(' ', $parts);
$formatted_line = "<div class='__traceroute__value'><span class='__traceroute_route'>{$hop_number}</span> {$line_content}</div>";
} else {
$formatted_line = "<div class='__traceroute__value'>{$line}</div>";
}
// Visual highlight based on response time (RTT)
$formatted_line = preg_replace_callback('/(\d+\.\d+)\s+ms/', function($matches) {
$ms = floatval($matches[1]);
$color_class = 'default';
if ($ms < 1) {
$color_class = 'green';
} elseif ($ms < 5) {
$color_class = 'blue';
} else {
$color_class = 'red';
}
return "<span class='latency {$color_class}'>{$matches[0]}</span>";
}, $formatted_line);
$formatted_output .= $formatted_line;
}
$dot .= "}\n";
// 4. Storage paths
$storageDir = '/var/www/html/views/tools/traceroute/';
$dotFilePath = $storageDir . $dotname . '_traceroute_graph.dot';
$imageFilePath = $storageDir . $dotname . '_traceroute.png';
file_put_contents($dotFilePath, $dot);
// 5. Compile PNG image using Graphviz
exec("dot -Tpng " . escapeshellarg($dotFilePath) . " -o " . escapeshellarg($imageFilePath));
// 6. Output to browser
if (file_exists($imageFilePath)) {
echo "<div class='alert alert-info'>" . htmlspecialchars($header) . "</div>";
echo "<div class='__traceroute__container'><div class='__traceroute__content'>{$formatted_output}</div></div>";
echo "<div class='mt-4'><div class='__traceroute__image'><img src='views/tools/traceroute/{$dotname}_traceroute.png' alt='Traceroute Graph for {$dotname}'></div></div>";
} else {
echo "<div class='alert alert-danger'>Error generating graph.</div>";
}
}
} else {
echo "<div class='alert alert-danger'>Invalid request.</div>";
}
?>
Best practices for web-based diagnostic tools#
Integrating command-line network utilities with PHP web interfaces simplifies network triage. When executing shell commands via exec():
- Strict Input Sanitization: Validate hostnames with native filters and escape parameters with
escapeshellarg()to protect against command injection. - Explicit Dependency Management: Document external system binaries (such as Graphviz) in your server provisioning scripts to prevent silent execution failures in production.
Was this article helpful?
Leave a quick reaction to help prioritize future technical guides:
This post is licensed under CC BY-NC.



Comments
Join the discussion below.
0 comments