PHP Classes

How to use a PHP network tools class that can run faster executing tasks in parallel with PHP Swoole using the package Network Tools Queued: Asynchronous diagnostics to test Internet services

Recommend this page to a friend!
     
  Info   Example   View files Files   Install with Composer Install with Composer   Download Download   Reputation   Support forum   Blog    
Last Updated Ratings Unique User Downloads Download Rankings
2025-11-02 (12 hours ago) RSS 2.0 feedNot yet rated by the usersTotal: Not yet counted Not yet ranked
Version License PHP version Categories
networktoolsqueued 1.0MIT/X Consortium ...8.1Networking, Tools, PHP 8
Description 

Author

This package can perform asynchronous diagnostics to test Internet services.

It provides an alternative implementation of the Network Tools package by Thomas Trautner to access multiple types of Internet services, to check their responses, and display the responses on a Web page.

This package uses the PHP Swoole extension to query multiple network services in parallel.

The package also provides score results based on the responses of the network services.

Picture of Henrik Skov
  Performance   Level  
Name: Henrik Skov <contact>
Classes: 1 package by
Country: Denmark Denmark
Age: ???
All time rank: Not yet ranked
Week rank: Not yet ranked
Innovation award
Innovation award
Nominee: 1x

Instructions

This package is inspired by the original Network Tools package by Thomas Trautner.

Please check this example script to see an example of querying network services.

This package requires the PHP Swoole extension.

Example

<?php
require_once __DIR__ . '/vendor/autoload.php';
require_once
__DIR__ . '/NetworkToolsQueued.php';

use
Colors\Color;

use function
Laravel\Prompts\spin;

function
isWSL(): bool {
  
    return
str_contains(strtolower(php_uname('r')), 'microsoft') ||
           
file_exists('/proc/sys/fs/binfmt_misc/WSLInterop');
}

/**
 * Open an URL in system browser
 *
 *
 * @param string $url
 * @return void
 */
function openBrowser(string $url) : void {

    if (
isWSL()) {

        if (
strpos($url, 'file://') === 0) {
           
$filePath = substr($url, 7); // Remove 'file://'
          
            // Copy to accessible location and open with cmd
           
$windowsTempFile = '/mnt/c/Windows/Temp/' . basename($filePath);
           
shell_exec("cp " . escapeshellarg($filePath) . " " . escapeshellarg($windowsTempFile));
          
           
$windowsPath = 'C:\\Windows\\Temp\\' . basename($filePath);
           
shell_exec("cmd.exe /c start \"\" \"" . $windowsPath . "\" 2>/dev/null");
        } else {
           
shell_exec("wslview \"$url\"");
        }
    } else {
       
shell_exec("xdg-open \"$url\"");
    }
}

class
StdinHelper {

   
/**
     * Check if there is any data available on STDIN using select()
     * This is the most reliable method for real-time checking
     *
     * @param int $timeout Timeout in seconds (0 for immediate return)
     * @return bool True if data is available, false otherwise
     */
   
public static function hasDataSelect(float $timeout = 0): bool {
       
$read = array(STDIN);
       
$write = null;
       
$except = null;
      
        return
stream_select($read, $write, $except, $timeout) > 0;
    }

   
/**
     * Forward stdin to a subprocess
     *
     *
     * @param string $cmd
     * @return void
     */
   
public static function forwardStdInTo(string $cmd) : void {

       
// Make STDIN non-blocking
       
stream_set_blocking(STDIN, false);

       
// Create a subprocess (example using 'cat', replace with your desired command)
       
$descriptorspec = array(
       
0 => array("pipe", "r"), // stdin
       
1 => array("pipe", "w"), // stdout
       
2 => array("pipe", "w") // stderr
       
);

       
$process = proc_open($cmd, $descriptorspec, $pipes);

        if (
is_resource($process)) {
           
// Make subprocess input/output streams non-blocking
           
stream_set_blocking($pipes[0], false);
           
stream_set_blocking($pipes[1], false);
           
stream_set_blocking($pipes[2], false);

            while (
true) {
               
// Read from STDIN
               
$input = fgets(STDIN);
              
                if (
$input !== false) {
                   
// Write to subprocess stdin
                   
fwrite($pipes[0], $input);
                  
                   
// Flush the pipe to ensure immediate writing
                   
fflush($pipes[0]);
                }
              
               
// Read from subprocess stdout
               
$output = fgets($pipes[1]);
                if (
$output !== false) {
                    echo
$output;
                }
              
               
// Read from subprocess stderr
               
$error = fgets($pipes[2]);
                if (
$error !== false) {
                   
fwrite(STDERR, $error);
                }
              
               
// Small delay to prevent CPU overload
               
usleep(10000); // 10ms delay
              
                // Check if STDIN has closed
               
if (feof(STDIN)) {
                    break;
                }
            }

           
// Clean up
           
fclose($pipes[0]);
           
fclose($pipes[1]);
           
fclose($pipes[2]);
          
           
// Close the process
           
proc_close($process);
        }
    }

   
/**
     * Check if STDIN is connected to a pipe or redirected file
     * Useful for detecting if script is running interactively
     *
     * @return bool True if STDIN is a pipe/file, false if it's a terminal
     */
   
public static function isPiped(): bool {
        return !
posix_isatty(STDIN);
    }

   
/**
     * Check if there is any data using non-blocking read
     * This method might be less reliable but doesn't require stream_select
     *
     * @return bool True if data is available, false otherwise
     */
   
public static function hasDataNonBlocking(): bool {
        static
$buffer = null;

       
// If we previously read a byte, return true
       
if ($buffer !== null) {
            return
true;
        }

       
// Store current blocking state
       
$meta = stream_get_meta_data(STDIN);
       
$blocking = $meta['blocked'] ?? true;

       
// Temporarily set to non-blocking
       
stream_set_blocking(STDIN, false);

       
// Try to read one byte
       
$char = fgetc(STDIN);

       
// Restore original blocking state
       
stream_set_blocking(STDIN, $blocking);

        if (
$char !== false) {
           
// Store the character for later retrieval
           
$buffer = $char;
            return
true;
        }

        return
false;
    }

    public static function
getStdInData(): string {
        static
$buffer = null;
       
$data = '';

       
// If we previously read a byte, prepend it to the read data
       
if ($buffer !== null) {
           
$data .= $buffer;
           
$buffer = null;
        }

       
// Read the rest of the input
       
$data .= stream_get_contents(STDIN);
        return
$data;
    }

   
/**
     * Comprehensive check that combines multiple methods
     *
     * @return array Array with detailed information about STDIN state
     */
   
public static function getStdinInfo(): array {
        return [
           
'has_data' => self::hasDataSelect(),
           
'is_piped' => self::isPiped(),
           
'is_readable' => is_readable("php://stdin"),
           
'stream_type' => stream_get_meta_data(STDIN)['stream_type'],
           
'blocked' => stream_get_meta_data(STDIN)['blocked'],
           
'eof' => feof(STDIN)
        ];
    }
}

//============================================================
// MAIN EXAMPLE CODE
//============================================================

try {

   
define('OPS',[
           
'a',
           
'mx',
           
'txt',
           
'cert',
           
'cname',
           
'soa',
           
'spf',
           
'ptr',
           
'tcp',
           
'smtp',
           
'http',
           
'https',
           
'ping',
           
'simulatedping',
           
'trace',
           
'simulatedtrace',
           
'fulldomain',
           
'fullip',
           
'securityscan',
           
'emailanalysis',
           
'quickcheck',
           
'serverscan',
           
'whois'
   
]);

   
$output = NULL;

    if (
$idx = array_search('--output', $_SERVER['argv'])) {
       
$output = $_SERVER['argv'][$idx+1];
    }

   
$c = new Color(); // See: https://github.com/kevinlebrun/colors.php
  
   
$operation = $_SERVER['argv'][1];
   
$operation = strtolower($operation);
  
   
// Validation
   
if (!in_array($operation, OPS)) {
        throw new
Exception(sprintf('Invalid operation: "%s" - Valid operations: %s', $operation, implode(', ', OPS)));
    }
  
   
$input = $_SERVER['argv'][2];
   
$stdInData = '';

    if (
StdInHelper::hasDataSelect()) {
       
$stdInData = StdinHelper::getStdInData();
    }

   
$inputs = match(TRUE) {
        (!empty(
$input) && is_file($input)) => file($input, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES),
        (!empty(
$stdInData)) => explode(PHP_EOL, trim($stdInData)),
        (!empty(
$input) && !is_file($input)) => [$input],
        default => []
    };

   
// Abort if no inputs
   
if (empty($inputs)) {
        throw new
Exception(sprintf('No inputs for "%s" operation - Use an IP or domain as argument or send a list on STDIN', $operation));
    }

   
$tools = new NetworkToolsQueued();

   
// Enable auto-PTR if flag set
    //if ($this->enableAutoPtr) $tools->enableAutoPTR();

    // Enable problem summary
    //if ($this->enableProblemSummary) $tools->enableProblemSummary();
  
    // Determine if operation requires IP or domain
   
$requiresIP = in_array($operation, ['ping', 'simulatedping', 'ptr']);
   
$requiresDomain = !$requiresIP;

   
// Validate and queue all targets
   
foreach($inputs as $target) {

       
$target = trim($target);
        if (empty(
$target)) continue;
      
       
// Handle special chained operations
       
if (in_array($operation, ['fulldomain', 'securityscan', 'emailanalysis', 'quickcheck', 'whois'])) {
          
           
// Domain operations
           
if (filter_var($target, FILTER_VALIDATE_DOMAIN) === FALSE) {
                throw new
Exception(sprintf('Invalid target for "%s": "%s" - Requires a domain', $operation, $target));
            }

            if (
$operation == 'fulldomain') {
               
$tools->fulldomain($target, in_array('--portscan', $_SERVER['argv']));
            } else {
               
$tools->$operation($target);
            }

        } elseif (
$operation === 'fullip') {
          
           
// IP operation
           
if (filter_var($target, FILTER_VALIDATE_IP) === FALSE) {
                throw new
Exception(sprintf('Invalid target for "%s": "%s" - Requires an IP', $operation, $target));
            }
           
$tools->$operation($target, TRUE, TRUE);
          
        } elseif (
$operation === 'serverscan') {
          
           
// Works with both IP and domain
           
$tools->$operation($target);
          
        } else {

           
// Original single operations
           
if ($requiresIP) {
                if (
filter_var($target, FILTER_VALIDATE_IP) === FALSE) {
                    throw new
Exception(sprintf('Invalid target for "%s": "%s" - Requires an IP', $operation, $target));
                }
            } elseif (
$requiresDomain) {
                if (empty(
$target) || preg_match('/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,}$/i', $target) === 0) {
                    throw new
Exception(sprintf('Invalid target for "%s": "%s" - Requires a domain', $operation, $target));
                }
            }
          
           
$tools->$operation($target);
        }
    }

   
// JSON output mode
   
if (in_array('--json', $_SERVER['argv'])) {
        if (
posix_isatty(STDOUT)) {
           
$m = ($output)
                ?
'<light_yellow>One moment please ...</light_yellow> - <light_cyan>Generating report</light_cyan> - <white>Will write</white> <light_green>'.$this->output.'</light_green> <white>momentarily...</white>'
               
: '<light_yellow>One moment please ...</light_yellow> - <light_cyan>Generating report</light_cyan>';
          
           
$array = spin(function() use($tools) {
               
$tools->exec();
                return
$tools->getResults();
            },
$c->colorize($m));

           
// Group by target
           
$byTarget = [];
            foreach (
$array as $r) {
               
$byTarget[$r['target']][] = $r;
            }
          
           
ksort($byTarget);

           
// Calculate and add scores to output
           
$scores = $tools->getTargetScores();
           
$output = [
               
'targets' => $byTarget,
               
'scores' => $scores
           
];

            if (
$output) {
               
file_put_contents($output, json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
                echo
self::TAB.$c->colorize("<light_green>&#10003;</light_green> Results written to: <light_cyan>{$output}</light_cyan>\n");
            } else {
                echo
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
            }
          
        } else {

           
$tools->exec();
           
$array = $tools->getResults();

           
// Group by target
           
$byTarget = [];
            foreach (
$array as $r) {
               
$byTarget[$r['target']][] = $r;
            }

           
ksort($byTarget);

           
// Calculate and add scores to output
           
$scores = $tools->getTargetScores();
           
$output = [
               
'targets' => $byTarget,
               
'scores' => $scores
           
];

            if (
$output) {
               
file_put_contents($output, json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
            } else {
                echo
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
            }
        }
      
        return;
    }

   
// HTML output mode (default)
   
if (posix_isatty(STDOUT)) {
       
$html = spin(function() use($tools) { // Spin is part of Laravel\Prompts\ package
           
$tools->exec();
           
// Enable grouping by target for better organization
           
return $tools->getReport(TRUE);
        },
$c->colorize('<light_yellow>One moment please ...</light_yellow> - <light_cyan>Generating report</light_cyan> - <white>Will open in</white> <light_green>browser</light_green> <white>momentarily...</white>'));
    } else {
       
$tools->exec();
       
$html = $tools->getReport(TRUE); // Group by target
   
}

   
// Save and open HTML report
   
$file = tempnam(sys_get_temp_dir(), 'nettool_') . '.html';
   
file_put_contents($file, $html);

   
openBrowser('file://'.$file);

} catch(
Exception $ex) {

    echo(
'ERROR :: ' . $ex->getMessage());
    exit(
1);
}


  Files folder image Files (3)  
File Role Description
Accessible without login Plain text file composer.json Conf. composer json
Accessible without login Plain text file example.php Example Short example
Plain text file NetworkToolsQueued.php Class The class

The PHP Classes site has supported package installation using the Composer tool since 2013, as you may verify by reading this instructions page.
Install with Composer Install with Composer
 Version Control Unique User Downloads  
 0%
Total:0
This week:0