pear/net_socket
PEAR Net_Socket is a lightweight object-oriented PHP socket wrapper for opening TCP/UDP connections, configuring timeouts, reading and writing data, and handling common network errors. A legacy PEAR component useful for simple client sockets and older PHP applications.
Net_Socket is a very small, legacy PEAR wrapper around PHP’s stream sockets. It is client-only and its main value today is either (a) writing quick TCP/UDP clients without fiddling with stream_socket_client, or (b) being the transport used underneath other PEAR packages (Net_SMTP, Net_POP3, Net_IMAP).
Minimal path:
pear install Net_Socket, vendoring the single Net_Socket.php file plus PEAR.php, or using a Composer mirror (e.g. pear/net_socket). Whichever you choose, ensure require_once 'Net/Socket.php'; resolves and PEAR.php is available for error handling.Net/Socket.php first. The whole API is ~15 public methods; read the constructor ($proto, $timeout, $blocksize), connect($host, $port, $timeout), write($data), read($size), readLine(), setTimeout(), setBlocking(), and enableCrypt().require_once 'Net/Socket.php';
$socket = new Net_Socket();
$err = $socket->connect('example.com', 80, 5);
if (PEAR::isError($err)) {
throw new RuntimeException($err->getMessage(), $err->getCode());
}
$socket->write("HEAD / HTTP/1.0\r\nHost: example.com\r\n\r\n");
$statusLine = $socket->readLine(); // check for PEAR_Error too
$socket->disconnect();
Check every return value. Almost all methods return PEAR_Error on failure and false/true/int on success. Wrap once:
private function guard($result) {
if (PEAR::isError($result)) {
throw new RuntimeException($result->getMessage(), $result->getCode());
}
return $result;
}
Reconnect wrapper. Net_Socket has no built-in reconnect. Build a thin class that constructs a fresh socket per attempt, applies timeouts, and retries only on connection-level failures (not on protocol errors):
class TcpClient {
public function __construct(private string $host, private int $port, private int $timeout = 5) {}
public function with(callable $fn) {
$s = new Net_Socket();
$this->guard($s->connect($this->host, $this->port, $this->timeout));
try { return $fn($s); }
finally { $s->disconnect(); }
}
}
Framed protocols. For binary or length-prefixed protocols, read($n) may return fewer than $n bytes. Always loop:
function readExactly(Net_Socket $s, int $n): string {
$buf = '';
while (strlen($buf) < $n) {
$chunk = $s->read($n - strlen($buf));
if (PEAR::isError($chunk)) throw new RuntimeException($chunk->getMessage());
if ($chunk === false || $chunk === '') break;
$buf .= $chunk;
}
return $buf;
}
Read fixed headers first, parse the length field, then call readExactly() for the payload. Use readLine() only for genuinely line-delimited protocols (SMTP, POP3, IRC).
TLS upgrades. Call enableCrypt() before connect() — not after. The underlying code wraps the stream in stream_socket_enable_crypto, so it must be set up on the not-yet-connected socket:
$s = new Net_Socket();
$s->enableCrypt(); // do this first
$s->connect('smtp.example.com', 465, 10);
Non-blocking / multiplexing. setBlocking(false) plus the exposed $socket->fp lets you use stream_select() on multiple Net_Socket instances. This is the only way to get concurrency out of the library — there is no async or event-loop integration:
$s->setBlocking(false);
$read = [$s->fp];
$w = $e = null;
stream_select($read, $w, $e, 2);
if ($read) { $line = $s->readLine(); /* may still return '' */ }
Integration with the PEAR stack. If you already depend on Net_SMTP or Net_POP3, you get Net_Socket transitively — subclass or wrap those instead of reimplementing SMTP. Subclass Net_Socket and override read/write/connect for logging, metrics, or request tracing without touching call sites.
Testing. Debug against a local listener (nc -l 9000 or socat) rather than a remote service. Net_Socket provides no built-in tracing; wrap read/write and var_dump the exact bytes.
PEAR_Error is not an exception. false and PEAR_Error are both possible; check with PEAR::isError($x) (or is_a($x, 'PEAR_Error') on old runtimes). A connect() that "succeeds" but returns true may still be silently unusable if you ignored a prior error.write() returns bytes written, not true. Large payloads are chunked by blocksize. Loop until strlen($remaining) === 0 or you'll send truncated messages.setTimeout() affects reads, not connect(). Connect timeout is the third argument to connect(); setTimeout($sec, $usec) configures stream_set_timeout() for subsequent read operations. Timeout-on-read doesn’t throw — inspect the return value and getStatus().readLine() can block indefinitely if the peer never sends \n and no read timeout is set. It also strips the trailing newline; do not assume "\r\n" is present.getStatus() returns useful diagnostics (timed_out, unread_bytes, eof). It's the fastest way to debug stalled connections — check timed_out after a false/PEAR_Error read before assuming the peer closed.enableCrypt() must precede connect(), and requires OpenSSL. Some older builds only accept deprecated STREAM_CRYPTO_METHOD_* constants; pass an explicit method if you need modern TLS 1.2+.Net_Socket to accept connections; use stream_socket_server directly.disconnect() does not reset the object. After a disconnect, either discard the instance or call connect() again; buffer state and error state can leak between uses.PEAR.php (dynamic properties, old constructor style) and be careful with strict types — Net_Socket predates scalar type hints. Pin error_reporting(E_ALL & ~E_DEPRECATED) or, better, fork the single file into your project and modernise it.$socket->fp for external stream ops, or wrap the instance. There is no event emitter, no middleware, no DI hooks.ext-sockets or Guzzle/stream_socket_client directly — you avoid the PEAR_Error layer and gain non-blocking helpers that are actually maintained.How can I help you explore Laravel packages today?