PHP предоставляет низкоуровневый доступ к сетевому взаимодействию через потоковые обертки (stream sockets) и расширение Sockets. Это позволяет создавать TCP/UDP клиенты и серверы, работать с HTTP без cURL, управлять DNS и выполнять сетевую диагностику.
stream_socket_client() -- TCP/UDP подключения
<?php
declare(strict_types=1);
// Connect to TCP server
$socket = stream_socket_client(
address: 'tcp://example.com:80',
error_code: $errorCode,
error_message: $errorMessage,
timeout: 10.0,
flags: STREAM_CLIENT_CONNECT,
);
if ($socket === false) {
throw new RuntimeException("Connection failed [$errorCode]: $errorMessage");
}
// Send HTTP request manually
$request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
fwrite($socket, $request);
// Read response
$response = '';
while (!feof($socket)) {
$response .= fread($socket, 8192);
}
fclose($socket);
echo $response;
// Connect with SSL/TLS
$sslSocket = stream_socket_client(
address: 'ssl://example.com:443',
error_code: $errorCode,
error_message: $errorMessage,
timeout: 10.0,
);
if ($sslSocket === false) {
throw new RuntimeException("SSL connection failed: $errorMessage");
}
fwrite($sslSocket, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
$response = stream_get_contents($sslSocket);
fclose($sslSocket);
// Connect with context options
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
],
'socket' => [
'tcp_nodelay' => true, // Disable Nagle's algorithm
],
]);
$socket = stream_socket_client(
'tls://api.example.com:443',
$errorCode,
$errorMessage,
10.0,
STREAM_CLIENT_CONNECT,
$context,
);
// Set stream timeout after connection
stream_set_timeout($socket, 30);
// Non-blocking mode
stream_set_blocking($socket, false);
stream_socket_server() -- создание серверов
<?php
declare(strict_types=1);
// Create a TCP server
$server = stream_socket_server(
address: 'tcp://0.0.0.0:8080',
error_code: $errorCode,
error_message: $errorMessage,
flags: STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
);
if ($server === false) {
throw new RuntimeException("Server creation failed: $errorMessage");
}
echo "Server listening on port 8080...\n";
// Accept connections in a loop
while (true) {
// Block until a client connects (with 60s timeout)
$client = @stream_socket_accept($server, 60, $peerName);
if ($client === false) {
continue; // Timeout or error — try again
}
echo "Connection from: $peerName\n";
// Read request
$request = '';
while (($line = fgets($client)) !== false) {
$request .= $line;
if (trim($line) === '') {
break; // Empty line = end of HTTP headers
}
}
// Send response
$body = "<h1>Hello from PHP!</h1><p>Your IP: $peerName</p>";
$response = "HTTP/1.1 200 OK\r\n"
. "Content-Type: text/html\r\n"
. "Content-Length: " . strlen($body) . "\r\n"
. "Connection: close\r\n"
. "\r\n"
. $body;
fwrite($client, $response);
fclose($client);
}
fclose($server);
// UDP server (connectionless)
$udpServer = stream_socket_server(
'udp://0.0.0.0:9000',
$errorCode,
$errorMessage,
STREAM_SERVER_BIND,
);
while (true) {
$data = stream_socket_recvfrom($udpServer, 65535, 0, $peer);
echo "Received from $peer: $data\n";
stream_socket_sendto($udpServer, "Echo: $data", 0, $peer);
}
file_get_contents() с HTTP контекстом
<?php
declare(strict_types=1);
// Simple GET request (simplest HTTP client in PHP!)
$html = file_get_contents('https://example.com');
// GET with custom headers
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => "Accept: application/json\r\nAuthorization: Bearer my-token\r\n",
'timeout' => 10,
],
]);
$response = file_get_contents('https://api.example.com/users', context: $context);
$data = json_decode($response, true);
// POST with JSON body
$postData = json_encode(['name' => 'John', 'email' => '[email protected]']);
$context = stream_context_create([
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nAccept: application/json\r\n",
'content' => $postData,
'timeout' => 30,
],
]);
$response = file_get_contents('https://api.example.com/users', context: $context);
// Access response headers via $http_response_header (magic variable)
// Populated after file_get_contents() with HTTP wrapper
echo $http_response_header[0]; // 'HTTP/1.1 200 OK'
// Parse status code from response headers
function getStatusCode(array $headers): int
{
// $headers[0] = 'HTTP/1.1 200 OK'
if (preg_match('/HTTP\/[\d.]+\s+(\d+)/', $headers[0], $matches)) {
return (int) $matches[1];
}
return 0;
}
$statusCode = getStatusCode($http_response_header);
// Handle errors — file_get_contents returns false on HTTP errors by default
// Use ignore_errors to get response body even on 4xx/5xx
$context = stream_context_create([
'http' => [
'method' => 'GET',
'ignore_errors' => true, // Don't trigger warning on 4xx/5xx
'timeout' => 10,
],
]);
$response = file_get_contents('https://api.example.com/not-found', context: $context);
$statusCode = getStatusCode($http_response_header);
if ($statusCode === 404) {
echo "Resource not found\n";
}
// PUT request
$context = stream_context_create([
'http' => [
'method' => 'PUT',
'header' => "Content-Type: application/json\r\n",
'content' => json_encode(['status' => 'active']),
],
]);
$response = file_get_contents('https://api.example.com/users/42', context: $context);
stream_context_create() -- полные опции
<?php
declare(strict_types=1);
$context = stream_context_create([
// HTTP/HTTPS options
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\nX-Custom: value\r\n",
'content' => '{"key":"value"}',
'user_agent' => 'MyApp/1.0',
'timeout' => 30, // Read timeout (seconds, float)
'max_redirects' => 5, // Follow redirects (default: 20)
'follow_location' => 1, // Follow redirects (default: 1)
'ignore_errors' => true, // Fetch body on 4xx/5xx
'protocol_version' => '1.1', // HTTP version
'proxy' => 'tcp://proxy.example.com:8080',
'request_fulluri' => true, // Send full URI in request line (for proxies)
],
// SSL/TLS options
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'allow_self_signed' => false,
'cafile' => '/etc/ssl/certs/ca-certificates.crt',
'local_cert' => '/path/to/client-cert.pem', // Client certificate
'local_pk' => '/path/to/client-key.pem', // Client private key
'peer_name' => 'example.com', // Expected CN
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT,
],
// Socket options
'socket' => [
'bindto' => '0.0.0.0:0', // Local address to bind
'tcp_nodelay' => true, // Disable Nagle
],
]);
fsockopen() -- низкоуровневые соединения
<?php
declare(strict_types=1);
// TCP connection
$socket = fsockopen('example.com', 80, $errorCode, $errorMessage, 10);
if ($socket === false) {
throw new RuntimeException("Connection failed: $errorMessage ($errorCode)");
}
// Send raw HTTP
fwrite($socket, "GET /api/data HTTP/1.1\r\n");
fwrite($socket, "Host: example.com\r\n");
fwrite($socket, "Connection: close\r\n");
fwrite($socket, "\r\n");
// Read response
$response = '';
while (!feof($socket)) {
$response .= fgets($socket, 4096);
}
fclose($socket);
// SSL connection via fsockopen
$sslSocket = fsockopen('ssl://example.com', 443, $errorCode, $errorMessage, 10);
// Persistent connection (reused across requests in same process)
$persistent = pfsockopen('example.com', 80, $errorCode, $errorMessage, 10);
// Check if connection is still alive
$meta = stream_get_meta_data($persistent);
if ($meta['timed_out'] || $meta['eof']) {
fclose($persistent);
$persistent = pfsockopen('example.com', 80, $errorCode, $errorMessage, 10);
}
Socket extension -- полный контроль
<?php
declare(strict_types=1);
// Low-level socket API (ext-sockets)
// More control than stream functions, but more complex
// Create TCP socket
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
throw new RuntimeException('Socket creation failed: ' . socket_strerror(socket_last_error()));
}
// Set options
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, ['sec' => 10, 'usec' => 0]);
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, ['sec' => 10, 'usec' => 0]);
socket_set_option($socket, SOL_SOCKET, SO_KEEPALIVE, 1);
// Connect
$result = socket_connect($socket, 'example.com', 80);
if ($result === false) {
throw new RuntimeException('Connection failed: ' . socket_strerror(socket_last_error($socket)));
}
// Send data
$request = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
socket_write($socket, $request, strlen($request));
// Receive data
$response = '';
while ($chunk = socket_read($socket, 8192)) {
$response .= $chunk;
}
socket_close($socket);
// === TCP server with socket extension ===
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, '0.0.0.0', 8080);
socket_listen($server, 5); // Backlog of 5 connections
echo "Listening on :8080\n";
while (true) {
$client = socket_accept($server);
socket_getpeername($client, $clientIp, $clientPort);
echo "Client connected: $clientIp:$clientPort\n";
$data = socket_read($client, 4096);
echo "Received: $data\n";
socket_write($client, "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK");
socket_close($client);
}
socket_close($server);
// === Select — multiplex I/O on multiple sockets ===
$read = [$socket1, $socket2, $socket3];
$write = null;
$except = null;
// Wait for any socket to become readable (up to 5 seconds)
$ready = socket_select($read, $write, $except, 5);
if ($ready > 0) {
foreach ($read as $readySocket) {
$data = socket_read($readySocket, 8192);
// Process data from whichever socket is ready
}
}
DNS функции
<?php
declare(strict_types=1);
// Resolve hostname to IP
$ip = gethostbyname('example.com');
echo $ip; // '93.184.216.34'
// If resolution fails, returns the hostname itself (!)
$result = gethostbyname('nonexistent.invalid');
if ($result === 'nonexistent.invalid') {
echo "DNS resolution failed\n";
}
// Resolve IP to hostname (reverse DNS)
$hostname = gethostbyaddr('8.8.8.8');
echo $hostname; // 'dns.google'
// Get all IPs for hostname
$ips = gethostbynamel('google.com');
// ['142.250.80.14', '142.250.80.46', ...]
// Full DNS records
$records = dns_get_record('example.com', DNS_ALL);
foreach ($records as $record) {
match ($record['type']) {
'A' => printf("A %s -> %s (TTL: %d)\n", $record['host'], $record['ip'], $record['ttl']),
'AAAA' => printf("AAAA %s -> %s\n", $record['host'], $record['ipv6']),
'MX' => printf("MX %s -> %s (pri: %d)\n", $record['host'], $record['target'], $record['pri']),
'CNAME' => printf("CNAME %s -> %s\n", $record['host'], $record['target']),
'TXT' => printf("TXT %s -> %s\n", $record['host'], $record['txt']),
'NS' => printf("NS %s -> %s\n", $record['host'], $record['target']),
'SOA' => printf("SOA %s (mname: %s, serial: %d)\n", $record['host'], $record['mname'], $record['serial']),
default => printf("%s record for %s\n", $record['type'], $record['host']),
};
}
// Query specific record types
$mxRecords = dns_get_record('example.com', DNS_MX);
$txtRecords = dns_get_record('example.com', DNS_TXT);
$nsRecords = dns_get_record('example.com', DNS_NS);
// Check if DNS record exists (useful for email validation)
$hasMx = checkdnsrr('example.com', 'MX');
$hasA = checkdnsrr('example.com', 'A');
// Email domain validation
function isValidEmailDomain(string $email): bool
{
$parts = explode('@', $email);
if (count($parts) !== 2) {
return false;
}
$domain = $parts[1];
return checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A');
}
Сетевые функции
<?php
declare(strict_types=1);
// IP address manipulation
$long = ip2long('192.168.1.1');
echo $long; // 3232235777
$ip = long2ip(3232235777);
echo $ip; // '192.168.1.1'
// IP range check
function isInSubnet(string $ip, string $subnet): bool
{
[$network, $bits] = explode('/', $subnet);
$ipLong = ip2long($ip);
$networkLong = ip2long($network);
$mask = -1 << (32 - (int) $bits);
return ($ipLong & $mask) === ($networkLong & $mask);
}
echo isInSubnet('192.168.1.100', '192.168.1.0/24') ? 'Yes' : 'No'; // Yes
echo isInSubnet('10.0.0.1', '192.168.1.0/24') ? 'Yes' : 'No'; // No
// IPv6 support
$packed = inet_pton('::1'); // Convert to binary format
$readable = inet_ntop($packed); // Convert back to readable
echo $readable; // '::1'
$packedV4 = inet_pton('192.168.1.1'); // Also works with IPv4
$readableV4 = inet_ntop($packedV4); // '192.168.1.1'
// Get network interfaces (PHP 8.3+ socket ext)
// Useful for finding local IP addresses
// Check if port is open
function isPortOpen(string $host, int $port, float $timeout = 2.0): bool
{
$socket = @fsockopen($host, $port, $errorCode, $errorMessage, $timeout);
if ($socket === false) {
return false;
}
fclose($socket);
return true;
}
echo isPortOpen('example.com', 443) ? 'Port 443 open' : 'Port 443 closed';
echo isPortOpen('example.com', 8080) ? 'Port 8080 open' : 'Port 8080 closed';
// Get service name by port
$service = getservbyport(443, 'tcp'); // 'https'
$service = getservbyport(80, 'tcp'); // 'http'
$service = getservbyport(22, 'tcp'); // 'ssh'
// Get port by service name
$port = getservbyname('https', 'tcp'); // 443
$port = getservbyname('ssh', 'tcp'); // 22
Практический пример: HTTP клиент без cURL
<?php
declare(strict_types=1);
final class StreamHttpClient
{
public function __construct(
private readonly int $timeout = 30,
private readonly string $userAgent = 'StreamHTTP/1.0',
) {}
public function get(string $url, array $headers = []): array
{
return $this->request('GET', $url, $headers);
}
public function post(string $url, string $body, array $headers = []): array
{
return $this->request('POST', $url, $headers, $body);
}
private function request(string $method, string $url, array $headers = [], ?string $body = null): array
{
$headerString = '';
foreach ($headers as $name => $value) {
$headerString .= "$name: $value\r\n";
}
$options = [
'http' => [
'method' => $method,
'header' => $headerString . "User-Agent: {$this->userAgent}\r\n",
'timeout' => $this->timeout,
'ignore_errors' => true,
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
];
if ($body !== null) {
$options['http']['content'] = $body;
}
$context = stream_context_create($options);
$responseBody = @file_get_contents($url, false, $context);
if ($responseBody === false) {
$error = error_get_last();
throw new RuntimeException("HTTP request failed: " . ($error['message'] ?? 'unknown'));
}
// Parse status code
$statusLine = $http_response_header[0] ?? '';
preg_match('/HTTP\/[\d.]+\s+(\d+)\s+(.*)/', $statusLine, $matches);
return [
'status' => (int) ($matches[1] ?? 0),
'reason' => $matches[2] ?? '',
'headers' => $this->parseHeaders($http_response_header),
'body' => $responseBody,
];
}
private function parseHeaders(array $rawHeaders): array
{
$headers = [];
foreach ($rawHeaders as $header) {
$parts = explode(':', $header, 2);
if (count($parts) === 2) {
$headers[strtolower(trim($parts[0]))] = trim($parts[1]);
}
}
return $headers;
}
}
// Usage
$client = new StreamHttpClient(timeout: 10);
$response = $client->get('https://api.example.com/users', [
'Accept' => 'application/json',
'Authorization' => 'Bearer my-token',
]);
if ($response['status'] === 200) {
$users = json_decode($response['body'], true);
}
Практический пример: TCP Echo-сервер
<?php
declare(strict_types=1);
function runEchoServer(string $host, int $port): never
{
$server = stream_socket_server(
"tcp://$host:$port",
$errorCode,
$errorMessage,
);
if ($server === false) {
throw new RuntimeException("Cannot start server: $errorMessage");
}
echo "Echo server running on $host:$port\n";
$clients = [$server];
while (true) {
$read = $clients;
$write = null;
$except = null;
// Wait for activity on any socket
$ready = stream_select($read, $write, $except, 1);
if ($ready === false) {
break;
}
foreach ($read as $socket) {
if ($socket === $server) {
// New connection
$client = stream_socket_accept($server, 0, $peerName);
if ($client !== false) {
$clients[] = $client;
stream_set_blocking($client, false);
echo "Connected: $peerName\n";
fwrite($client, "Welcome to Echo Server!\n");
}
} else {
// Data from existing client
$data = fread($socket, 8192);
if ($data === '' || $data === false) {
// Client disconnected
$key = array_search($socket, $clients, true);
unset($clients[$key]);
fclose($socket);
echo "Client disconnected\n";
} else {
// Echo data back
fwrite($socket, "Echo: $data");
}
}
}
}
fclose($server);
}
// runEchoServer('0.0.0.0', 9000);
Практический пример: DNS Lookup утилита
<?php
declare(strict_types=1);
function dnsLookup(string $domain): void
{
echo "=== DNS Lookup: $domain ===\n\n";
// A records (IPv4)
$aRecords = dns_get_record($domain, DNS_A);
echo "A Records:\n";
foreach ($aRecords as $record) {
echo " {$record['ip']} (TTL: {$record['ttl']}s)\n";
}
// AAAA records (IPv6)
$aaaaRecords = dns_get_record($domain, DNS_AAAA);
echo "\nAAAA Records:\n";
foreach ($aaaaRecords as $record) {
echo " {$record['ipv6']} (TTL: {$record['ttl']}s)\n";
}
// MX records
$mxRecords = dns_get_record($domain, DNS_MX);
usort($mxRecords, fn($a, $b) => $a['pri'] <=> $b['pri']);
echo "\nMX Records:\n";
foreach ($mxRecords as $record) {
echo " [{$record['pri']}] {$record['target']}\n";
}
// NS records
$nsRecords = dns_get_record($domain, DNS_NS);
echo "\nNS Records:\n";
foreach ($nsRecords as $record) {
echo " {$record['target']}\n";
}
// TXT records (SPF, DKIM, etc.)
$txtRecords = dns_get_record($domain, DNS_TXT);
echo "\nTXT Records:\n";
foreach ($txtRecords as $record) {
echo " {$record['txt']}\n";
}
// Reverse DNS
foreach ($aRecords as $record) {
$reverse = gethostbyaddr($record['ip']);
echo "\nReverse DNS ({$record['ip']}): $reverse\n";
}
}
// dnsLookup('google.com');