Minecraft Status is a PHP 8.1+ library for reading Minecraft server information without running a Minecraft client.
Use it when you want to show server availability, MOTD, player counts, version, protocol, favicon, player samples or Query data in a website, panel, bot, monitor or backend job.
The library supports:
- Java Edition status ping, used by the Minecraft multiplayer server list;
- Java Edition GameSpy4 Query, when
enable-query=trueis enabled on the server; - Bedrock Edition RakNet unconnected ping/pong;
- legacy Java ping for servers before Minecraft 1.7.
Install the package with Composer:
composer require dev-lancer/minecraft-statusRequirements:
- PHP
^8.1; ext-json;ext-iconv;ext-mbstring.
Every client follows the same shape: instantiate it for a host, call fetch(), then read the typed result with getResult(). Use raw() or getInfo() only when you need the parsed protocol array.
use DevLancer\MinecraftStatus\MinecraftJavaStatus;
$status = new MinecraftJavaStatus('mc.example.com');
$result = $status->fetch()->getResult();
echo $result->motd();
echo $result->onlinePlayers() . '/' . $result->maxPlayers();fetch() returns the same client instance, so chaining is safe:
$result = (new MinecraftJavaStatus('mc.example.com'))
->fetch()
->getResult();Most Java Edition servers should be queried with MinecraftJavaStatus. It uses the same TCP status ping that the Minecraft multiplayer screen uses.
<?php declare(strict_types=1);
use DevLancer\MinecraftStatus\Exception\ConnectionException;
use DevLancer\MinecraftStatus\Exception\InvalidResponseException;
use DevLancer\MinecraftStatus\Exception\ProtocolException;
use DevLancer\MinecraftStatus\Exception\ReceiveStatusException;
use DevLancer\MinecraftStatus\Exception\TimeoutException;
use DevLancer\MinecraftStatus\MinecraftJavaStatus;
require_once __DIR__ . '/vendor/autoload.php';
$status = new MinecraftJavaStatus('mc.example.com');
try {
$result = $status->fetch()->getResult();
echo "Server is online\n";
echo "MOTD: " . $result->motd() . "\n";
echo "Players: " . $result->onlinePlayers() . '/' . $result->maxPlayers() . "\n";
echo "Version: " . ($result->versionName ?? 'unknown') . "\n";
echo "Protocol: " . $result->protocol . "\n";
} catch (ConnectionException $exception) {
echo "Connection failed: " . $exception->getMessage() . "\n";
} catch (TimeoutException $exception) {
echo "Server read timed out: " . $exception->getMessage() . "\n";
} catch (ProtocolException | InvalidResponseException $exception) {
echo "Invalid server response: " . $exception->getMessage() . "\n";
} catch (ReceiveStatusException $exception) {
echo "Status could not be received: " . $exception->getMessage() . "\n";
}The repository also contains a fuller Java example in examples/ping.php.
Pick the client by protocol, not only by server edition:
| Client | Use when | Transport | Default port |
|---|---|---|---|
MinecraftJavaStatus |
You want normal Java Edition server list status | TCP | 25565 |
MinecraftJavaQuery |
You know GameSpy4 Query is enabled | UDP | 25565 |
MinecraftBedrockStatus |
You query a Bedrock Edition server | UDP | 19132 |
MinecraftJavaPreOld17Status |
You query a pre-1.7 Java server | TCP | 25565 |
Start with MinecraftJavaStatus for Java servers. Query is optional server functionality and is commonly disabled on public servers.
getResult() returns a result object. Every result object has these methods:
raw(): array
motd(): string
onlinePlayers(): int
maxPlayers(): intThe common methods are enough for many dashboards:
$result = $status->fetch()->getResult();
$isFull = $result->onlinePlayers() >= $result->maxPlayers();
echo $result->motd();
echo $isFull ? 'Server is full' : 'Slots are available';Each protocol also exposes extra readonly properties:
| Result | Extra data |
|---|---|
JavaStatusResult |
protocol, versionName, favicon, delay, players |
JavaQueryResult |
hostIp, players |
BedrockStatusResult |
protocol, version, gameMode, map, serverId, ipv4Port, ipv6Port |
LegacyJavaStatusResult |
protocol, versionName |
For Java status, the players property is the sample returned by the server. Servers are allowed to return no sample even when players are online.
foreach ($result->players as $player) {
print_r($player);
}If a server sends a favicon, Java status exposes it as a data URI string:
if ($result->favicon !== '') {
file_put_contents('favicon.txt', $result->favicon);
}Use raw data when you need protocol fields that are not represented by the typed result yet.
$status->fetch();
print_r($status->getResult()->raw());
print_r($status->getInfo());getInfo() is kept for compatibility. New code should prefer getResult() for common fields and getResult()->raw() for raw protocol data.
MinecraftJavaQuery uses the GameSpy4 Query protocol. It is not the same thing as normal Java status ping.
The Minecraft server must enable Query in server.properties:
enable-query=true
query.port=25565Then query it:
<?php declare(strict_types=1);
use DevLancer\MinecraftStatus\MinecraftJavaQuery;
require_once __DIR__ . '/vendor/autoload.php';
$query = new MinecraftJavaQuery('mc.example.com');
$result = $query->fetch()->getResult();
echo "MOTD: " . $result->motd() . "\n";
echo "Players: " . $result->onlinePlayers() . '/' . $result->maxPlayers() . "\n";
echo "Host IP: " . $result->hostIp . "\n";
foreach ($result->players as $player) {
echo $player . "\n";
}Example file: examples/query.php
If Java status works but Query times out, the most likely reason is that Query is disabled or blocked by firewall rules.
Bedrock Edition uses UDP and usually listens on port 19132:
<?php declare(strict_types=1);
use DevLancer\MinecraftStatus\MinecraftBedrockStatus;
require_once __DIR__ . '/vendor/autoload.php';
$status = new MinecraftBedrockStatus('bedrock.example.com', 19132);
$result = $status->fetch()->getResult();
echo "MOTD: " . $result->motd() . "\n";
echo "Players: " . $result->onlinePlayers() . '/' . $result->maxPlayers() . "\n";
echo "Version: " . ($result->version ?? 'unknown') . "\n";
echo "Game mode: " . ($result->gameMode ?? 'unknown') . "\n";Example file: examples/bedrock.php
Bedrock status data is a length-prefixed, semicolon-delimited RakNet status string. The parser treats ; as a field separator and requires the expected Bedrock field count.
Vanilla Bedrock Dedicated Server treats server-name as a string without semicolons. This library does not try to guess or rebuild names that contain ;, because doing so could shift protocol, version, player count and port fields. Payloads with an unexpected field count are rejected as invalid responses.
Use MinecraftJavaPreOld17Status only for old Java servers that still implement the legacy ping used before Minecraft 1.7:
<?php declare(strict_types=1);
use DevLancer\MinecraftStatus\MinecraftJavaPreOld17Status;
require_once __DIR__ . '/vendor/autoload.php';
$status = new MinecraftJavaPreOld17Status('legacy.example.com');
$result = $status->fetch()->getResult();
echo "MOTD: " . $result->motd() . "\n";
echo "Players: " . $result->onlinePlayers() . '/' . $result->maxPlayers() . "\n";
echo "Version: " . ($result->versionName ?? 'unknown') . "\n";Modern Java servers should use MinecraftJavaStatus instead.
All clients accept host, port, timeout and SRV resolving flag:
$status = new MinecraftJavaStatus(
host: 'mc.example.com',
port: 25565,
timeout: 0.5,
resolveSRV: true
);Timeouts are seconds and accept int|float. The value must be greater than zero.
$status->setTimeout(1.5);Set encoding when you need raw strings converted to a specific encoding:
$status->setEncoding('UTF-8');
$status->fetch();SRV lookup is enabled by default. Pass false as the fourth constructor argument when you want to query the exact host and port:
$status = new MinecraftJavaStatus('mc.example.com', 25565, 3, false);SRV resolution ignores literal IPv4 and IPv6 hosts, validates SRV records, normalizes trailing dots and sorts candidates by priority ascending and weight descending.
The lifecycle state describes the last fetch result:
use DevLancer\MinecraftStatus\StatusState;
$status = new MinecraftJavaStatus('mc.example.com');
$status->status(); // StatusState::Idle
$status->fetch();
$status->status(); // StatusState::Fetched
$status->disconnect();
$status->status(); // StatusState::Fetcheddisconnect() closes only the socket. It does not clear a successfully fetched result, so getResult() and getInfo() still work after disconnecting.
If a later fetch() fails, the previous result is cleared and status() becomes StatusState::Failed.
isConnected() only reports whether the socket is currently open. Use status() when you need to know whether parsed data is available.
Handle the specific exceptions when you want precise error messages:
use DevLancer\MinecraftStatus\Exception\ConnectionException;
use DevLancer\MinecraftStatus\Exception\InvalidResponseException;
use DevLancer\MinecraftStatus\Exception\NotConnectedException;
use DevLancer\MinecraftStatus\Exception\ProtocolException;
use DevLancer\MinecraftStatus\Exception\ReceiveStatusException;
use DevLancer\MinecraftStatus\Exception\TimeoutException;
try {
$result = $status->fetch()->getResult();
} catch (ConnectionException $exception) {
// DNS failed, socket could not be opened, or the endpoint refused connection.
} catch (TimeoutException $exception) {
// The server did not answer before the configured timeout.
} catch (ProtocolException $exception) {
// The response packet did not match the expected Minecraft protocol.
} catch (InvalidResponseException $exception) {
// The packet was received, but the payload could not be parsed as valid status data.
} catch (ReceiveStatusException $exception) {
// Compatibility catch for receive, timeout, protocol and invalid response failures.
} catch (NotConnectedException $exception) {
// Data was requested before a successful fetch().
}TimeoutException, ProtocolException and InvalidResponseException extend ReceiveStatusException, so older catch (ReceiveStatusException $exception) blocks still work.
Typed results are the preferred API, but clients also expose convenience getters:
getInfo(): array
getResult(): StatusResultInterface
getCountPlayers(): int
getMaxPlayers(): int
getMotd(): stringAdditional client-specific getters:
| Method | Java Status | Java Query | Bedrock Status | Legacy Java |
|---|---|---|---|---|
getPlayers() |
Yes | Yes | No | No |
getFavicon() |
Yes | No | No | No |
getDelay() |
Yes | No | No | No |
getProtocol() |
Yes | No | Yes | Yes |
connect() remains available as an alias of fetch() for older code:
$status->connect();The deprecated class aliases Ping, Query, QueryBedrock and PingPreOld17 still exist. New code should use MinecraftJavaStatus, MinecraftJavaQuery, MinecraftBedrockStatus and MinecraftJavaPreOld17Status.
See UPGRADE-4.0.md for migration notes from V3.
Run the default checks:
composer qaReal public server smoke tests are opt-in. Create an ignored .servers-list.php file in the repository root, then run:
composer test-realThe real server config should return sections for java_status, java_query, bedrock, legacy_java and srv_java_status. Public servers are not reliable enough for the default test suite, so these tests are intentionally separate from composer test.