acamposm/ping
A simple Laravel package for pinging hosts and checking connectivity. It wraps ping commands behind a clean API, making it easy to test server availability, measure response, and integrate uptime checks into your Laravel apps.
1. Install
composer require acamposm/ping
The package ships as a Laravel service provider. On Laravel 5.5+ it is auto-discovered; if not (or on older versions), register it in config/app.php:
'providers' => [
// ...
Acamposm\Ping\PingServiceProvider::class,
],
'aliases' => [
'Ping' => Acamposm\Ping\Facades\Ping::class,
],
Confirm the exact namespace/facade name in
composer.json(autoload.psr-4) and the packageREADMEbefore wiring it in — the docs are thin.
2. Run the installer
php artisan ping:install
This is the package's own setup command and the fastest way to get a working config/ping.php. Two things to know:
ping:install stops appearing in php artisan list — that is intentional (fixed in 2.1.3, previously the command lingered and re-ran pointlessly). Its absence is your signal that setup is already done.php artisan vendor:publish --provider="Acamposm\Ping\PingServiceProvider".3. Look here first
config/ping.php → the whole tuning surface (packet count, timeout, size, binary path). Read it before writing any code.src/ → the main Ping class and the PingResult/Response value object. That result object is the API surface you'll use day to day.composer.json → PSR-4 namespace and PHP/Laravel constraints, to confirm it installs cleanly on your app.4. First use case
The canonical use is a simple reachability probe — "is this host up, and how long did it take?":
use Acamposm\Ping\Facades\Ping;
$result = Ping::ping('8.8.8.8');
// Dump once to learn the actual shape of the result object in your version:
dd($result);
Then branch on it:
if ($result->isAlive()) { // or ->isSuccessful(), check the class
$latency = $result->getLatency(); // ms, if exposed
}
Because the surface is small, dump the result object on your first call and build from there rather than trusting the README examples verbatim.
Wrap it behind your own interface. Don't let Ping:: calls leak across the codebase. Define a tiny contract and one adapter:
interface HostProbe
{
public function reachable(string $host): bool;
public function latency(string $host): ?float;
}
final class IcmpHostProbe implements HostProbe
{
public function reachable(string $host): bool
{
try {
return Ping::ping($host)->isAlive();
} catch (\Throwable $e) {
report($e);
return false;
}
}
}
Binding this in a service provider gives you one seam to swap in tests, a stub, or a different library later.
Drive behaviour from config, not from call sites. After ping:install, config/ping.php is the place to set count/timeout. Keep your adapter reading those keys so a slow-network environment can be retuned without a deploy:
final class IcmpHostProbe implements HostProbe
{
public function __construct(private array $options = []) {}
public function reachable(string $host): bool
{
return Ping::ping($host, $this->options)->isAlive(); // adjust to actual signature
}
}
Bind it via fn () => new IcmpHostProbe(config('ping')) and the package config becomes your tuning surface.
Artisan command for scheduled monitoring. The natural home for a ping is a console command, not a controller:
class PingHosts extends Command
{
protected $signature = 'monitor:ping {hosts* : Hosts or IPs to probe}';
public function handle(HostProbe $probe): int
{
foreach ($this->argument('hosts') as $host) {
$up = $probe->reachable($host);
$this->line(sprintf('%-30s %s', $host, $up ? '<info>UP</info>' : '<error>DOWN</error>'));
}
return self::SUCCESS;
}
}
Schedule it with ->everyFiveMinutes() and keep the output structured (JSON) if you intend to ship it to a log aggregator.
Batch probing with isolation. Ping is inherently a network call and will occasionally hang or throw. Never let one bad host abort the loop:
$results = collect($hosts)->mapWithKeys(function (string $host) use ($probe) {
return [$host => $probe->reachable($host)];
});
For many hosts, shard the work across a queued job rather than a single request/command — even a 1-second timeout × 200 hosts is a bad user experience.
Health endpoint pattern. For a /health or readiness check, ping your dependency (database host, upstream API gateway) rather than a public internet address, and cache the result for ~30 seconds so you aren't shelling out on every probe:
$up = Cache::remember('health:db-host', 30, fn () => $probe->reachable(config('database.host')));
Testing. Never hit the network in tests. Swap your HostProbe binding for a fake:
$this->app->bind(HostProbe::class, fn () => new class implements HostProbe {
public function reachable(string $host): bool { return true; }
public function latency(string $host): ?float { return 12.4; }
});
If you must test the package directly, assert on its result object, not on raw command output — output formats vary by platform.
Chaining with notifications. A common workflow: scheduled ping → on state transition (up→down) → Notification::route(...) or a Slack/webhook job. Store last-known state in cache so you only alert on changes, not every failed probe.
ping:install disappearing is by design. Since 2.1.3 the command hides itself once the config has been published. If it's missing in a fresh checkout or CI image, the config was baked in at build time — re-publish with vendor:publish --provider="Acamposm\Ping\PingServiceProvider" --force rather than hunting for a broken command.ping binary must exist on the host. The package shells out to the OS ping; it does not open raw sockets itself. In slim Docker images (alpine without iputils, distroless) this fails outright. Verify with which ping / ping -c 1 127.0.0.1 inside the container.CAP_NET_RAW or a setuid binary. A Permission denied or silent failure is an environment problem, not a package bug.filter_var($host, FILTER_VALIDATE_IP) or a hostname regex before it reaches the package.ping differs across Linux, macOS, and Windows, and can be translated on localized systems. If latency parsing returns null or garbage, that's usually the cause — check by running the exact ping invocation manually.ping() is synchronous and can take seconds on a timeout. Never call it in a loop inside a web request; queue it or move it to a command.config/ping.php. A single echo request plus a short timeout is usually what you want. The default may be multiple packets, inflating every check to several seconds — publish the config via ping:install and set it explicitly.exec/proc_open may be disabled. Shared hosts frequently disable these functions; check php -i | grep disable_functions before blaming the package.ping:install fix) confirms the 2.x line is still alive, but there are no new features landing. If you need IPv6, custom flags, or richer parsing you'll be building it yourself — which is another reason to keep everything behind your HostProbe interface.HostProbe interface so the coupling stays contained.How can I help you explore Laravel packages today?