Skip to content

XbNz/mtr

Repository files navigation

GitHub Workflow Status

PHP MTR

Framework-agnostic asynchronous MTR library

⚠️ The MTR command requires root privileges for low interval, edit your /etc/sudoers file.

Get up and running

Require the package

composer require xbnz/mtr

Using MTR in your project (Laravel)

// Optionally configure DI (Laravel example)

class AppServiceProvider extends Provider
{
    public function register()
    {
        $this->app->bind(MTR::class, function (Application $app) {
            return MTR::build(Config::get('services.mtr_options'), $app->make(LoggerInterface::class));
        });
    }
}

MTR options

// services.php
return [
    // ...
    'mtr_options' => new \Xbnz\Mtr\MtrOptionsConfigDto(...)
]
⚠️ Important note on configuration options.

You should not disable the 'report wide' or 'json' options. The package will cease to work.

Bulk or single IP with async execution

use Xbnz\Mtr;

public function __construct(private MTR $mtr)
{}

// Or without DI...

public function __construct()
{
    $this->mtr = Mtr::build(new MtrOptionsConfigDto(...), new Logger);
}


public function example()
{

    // Consider a high timeout value for large scans. 
    // Async threads above 50 might cause inaccuracies in RTT statistics.

    $results = $this->mtr->withIp('1.1.1.1', '8.8.8.8')->wrap($consoleTimeout, $concurrentProcesses);
    // OR
    $results = $this->mtr->withIp(...['1.1.1.1', '8.8.8.8'])->wrap($consoleTimeout, $concurrentProcesses);
    // OR
    $results = $this->mtr->withIp('1.1.1.1')->wrap($consoleTimeout, $concurrentProcesses);
    // OR
    $results = $this->mtr->withIp(995738574453)->wrap($consoleTimeout, $concurrentProcesses);
    // OR
    $results = $this->mtr->withIp('google.com')->wrap($consoleTimeout, $concurrentProcesses);
    // OR
    $results = $this->mtr->withIp(0x1294812)->wrap($consoleTimeout, $concurrentProcesses);
    
    
    Assert::containsOnlyInstancesOf(
        MtrResult::class,
        $results
    );
  
    /**
    * If last hop of MTR !== supplied IP, the target is dead. 
    * In most circumstances, you'd want to reject dead targets. 
    * You might still want to see the hops for a dead target, so the default policy is not to reject.
    */
    $aliveTargetHopPairs = $results
        ->reject(fn($result) => $result->targetDown()) 
        ->map(fn($result) => [$result->targetHost => $result->hops]);
        ->each(fn($targetWithHops) => Assert::containsOnlyInstancesOf(MtrHopDto::class, $targetWithHops));
    
    $aliveTargetHopPairs
        ->each(function ($hops, $ip) {
            if ($hops->last()->packetLoss > 5) {
                $this->logger->log("Target {$ip}, hop {$hops->last()->hopPositionCount} has {$hops->last()->packetLoss}% loss")
            }
        });
}

Hooking into the raw() method

The raw() method will return an unprocessed result set from MTR. This will also allow you to hook into each asynchronous process, which is useful for long-running processes.

For example, you might want to keep track of each IP that is processed, like so.

public function handle(MTR $mtr)
{
    // Thousands of IPs
    $mtr->withIp('1.1.1.1/20') 
        ->raw(callable: function (ForkSerializableProcessDto $dto) {
            dump($dto->host . PHP_EOL);
        })
}

About

Framework agnostic package for MTR

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages