Programación en red con PHP: de sockets a web scraping

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: por qué la programación en red con PHP no da miedo, sino que es interesante

\n\n

Cuando escuchas «programación en red», enseguida te vienen a la mente imágenes de desarrolladores de C++ manejando sockets de bajo nivel, o administradores escribiendo scripts en Python para monitorear la red. ¿Y PHP? ¿Acaso su único propósito no es mostrar un «¡Hola, mundo!» en un sitio web?

\n\n

En realidad, PHP es una herramienta potente para la comunicación en red. Puede abrir sockets, navegar por HTTP, trabajar con WebSocket e incluso extraer datos de otros sitios web. Y todo esto, sin necesidad de instalar extensiones adicionales (bueno, casi).

\n\n

En este artículo, exploraremos las técnicas clave de programación en red con PHP. Aprenderás a escribir un cliente TCP simple, automatizar peticiones a APIs, hacer scraping de sitios web y crear tu propio chat con WebSocket. ¡Vamos allá!

\n\n

1. Fundamentos de sockets en PHP

\n\n

Los sockets son una forma de bajo nivel para que los programas se comuniquen a través de la red. En PHP, el trabajo con sockets se realiza mediante las funciones de la familia socket_*. Estas están incluidas en la instalación estándar, pero a veces requieren habilitar la extensión sockets en el php.ini.

\n\n

Creación de un cliente TCP

\n\n

Escribamos un cliente simple que se conecte a un servidor (por ejemplo, a time.nist.gov, un servidor de hora exacta) y lea la respuesta:

\n\n
<?php\n$host = 'time.nist.gov';\n$port = 37;\n\n// Creamos el socket\n$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nif ($socket === false) {\n    echo "No se pudo crear el socket: " . socket_strerror(socket_last_error()) . "\\n";\n    exit;\n}\n\n// Nos conectamos\n$result = socket_connect($socket, $host, $port);\nif ($result === false) {\n    echo "No se pudo conectar: " . socket_strerror(socket_last_error($socket)) . "\\n";\n    exit;\n}\n\n// Leemos 4 bytes (hora en formato NTP)\n$bytes = socket_read($socket, 4);\nif ($bytes === false) {\n    echo "Error de lectura: " . socket_strerror(socket_last_error($socket)) . "\\n";\n    exit;\n}\n\n// Cerramos el socket\nsocket_close($socket);\n\n// Convertimos los datos binarios a un número\n$time = unpack('N', $bytes)[1];\n// La hora NTP se cuenta desde 1900, restamos 70 años (en segundos)\n$unix_time = $time - 2208988800;\necho "Hora actual (UNIX): " . date('Y-m-d H:i:s', $unix_time) . "\\n";\n?>
\n\n

Este código se conecta al servidor de hora exacta, lee 4 bytes (la hora en formato NTP) y los convierte en una fecha legible. Fíjate en el manejo de errores: en programación en red, esto es fundamental.

\n\n

Creación de un servidor TCP simple

\n\n

Ahora, creemos nuestro propio miniservidor que acepte conexiones y responda «¡Hola, cliente!»:

\n\n
<?php\n$host = '127.0.0.1';\n$port = 8080;\n\n$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);\nsocket_bind($server, $host, $port);\nsocket_listen($server, 5);\n\necho "Servidor iniciado en $host:$port\\n";\n\nwhile (true) {\n    $client = socket_accept($server);\n    if ($client === false) {\n        continue;\n    }\n    $msg = "¡Hola, cliente!\\n";\n    socket_write($client, $msg, strlen($msg));\n    socket_close($client);\n}\nsocket_close($server);\n?>
\n\n

Ejecuta el script en la consola (php server.php) y luego conéctate con telnet: telnet 127.0.0.1 8080. Verás el saludo.

\n\n

Consejo: Para proyectos reales, usa librerías como ReactPHP o Amp — ofrecen asincronía y una API más amigable.

\n\n

2. Peticiones HTTP con cURL

\n\n

cURL es la navaja suiza para trabajar con HTTP en PHP. Permite hacer peticiones GET, POST, manejar cookies, cabeceras, proxies y certificados. La extensión curl suele venir preinstalada.

\n\n

Petición GET básica

\n\n
<?php\n$ch = curl_init('https://api.github.com/repos/php/php-src');\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_USERAGENT, 'PHP Script');\n$response = curl_exec($ch);\n$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n\nif ($http_code === 200) {\n    $data = json_decode($response, true);\n    echo "Repositorio: " . $data['full_name'] . "\\n";\n    echo "Estrellas: " . $data['stargazers_count'] . "\\n";\n} else {\n    echo "Error HTTP: $http_code\\n";\n}\n?>
\n\n

Este ejemplo consulta la API de GitHub para obtener información del repositorio oficial de PHP. Configuramos CURLOPT_RETURNTRANSFER para que la respuesta se devuelva como string, y CURLOPT_USERAGENT es obligatorio en la API de GitHub.

\n\n

Petición POST con datos

\n\n
<?php\n$ch = curl_init('https://jsonplaceholder.typicode.com/posts');\ncurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);\ncurl_setopt($ch, CURLOPT_POST, true);\ncurl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([\n    'title' => 'Mi artículo',\n    'body' => 'Contenido del artículo',\n    'userId' => 1\n]));\ncurl_setopt($ch, CURLOPT_HTTPHEADER, [\n    'Content-Type: application/json'\n]);\n\n$response = curl_exec($ch);\n$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);\ncurl_close($ch);\n\necho "Código HTTP: $http_code\\n";\necho "Respuesta: $response\\n";\n?>
\n\n

Aquí enviamos datos en formato JSON a una API de prueba. Es la base para integrarte con servicios REST como Stripe, Twilio o cualquier API moderna.

\n\n

3. WebSocket en PHP

\n\n

WebSocket permite comunicación bidireccional en tiempo real entre cliente y servidor. A diferencia de HTTP, la conexión permanece abierta y ambos lados pueden enviar mensajes en cualquier momento.

\n\n

PHP no tiene soporte nativo para WebSocket, pero podemos usar la librería Ratchet (basada en ReactPHP) para implementarlo fácilmente.

\n\n

Ejemplo: servidor de chat simple con Ratchet

\n\n

Primero, instala Ratchet vía Composer:

\n\n
composer require cboden/ratchet
\n\n

Luego, crea el servidor:

\n\n
<?php\nuse Ratchet\\MessageComponentInterface;\nuse Ratchet\\ConnectionInterface;\nuse Ratchet\\Server\\IoServer;\nuse Ratchet\\Http\\HttpServer;\nuse Ratchet\\WebSocket\\WsServer;\n\nclass Chat implements MessageComponentInterface {\n    protected $clients;\n\n    public function __construct() {\n        $this->clients = new \\SplObjectStorage;\n    }\n\n    public function onOpen(ConnectionInterface $conn) {\n        $this->clients->attach($conn);\n        echo "Nueva conexión: {$conn->resourceId}\\n";\n    }\n\n    public function onMessage(ConnectionInterface $from, $msg) {\n        foreach ($this->clients as $client) {\n            if ($from !== $client) {\n                $client->send($msg);\n            }\n        }\n    }\n\n    public function onClose(ConnectionInterface $conn) {\n        $this->clients->detach($conn);\n        echo "Conexión cerrada: {$conn->resourceId}\\n";\n    }\n\n    public function onError(ConnectionInterface $conn, \\Exception $e) {\n        echo "Error: {$e->getMessage()}\\n";\n        $conn->close();\n    }\n}\n\n$server = IoServer::factory(\n    new HttpServer(\n        new WsServer(\n            new Chat()\n        )\n    ),\n    8080\n);\n\necho "Servidor WebSocket iniciado en el puerto 8080\\n";\n$server->run();\n?>
\n\n

Este servidor de chat reenvía cualquier mensaje recibido a todos los clientes conectados. Puedes probarlo con un cliente JavaScript básico:

\n\n
<script>\nconst ws = new WebSocket('ws://localhost:8080');\nws.onmessage = function(event) {\n    console.log('Mensaje recibido:', event.data);\n};\nws.onopen = function() {\n    ws.send('¡Hola a todos!');\n};\n</script>
\n\n

4. Web scraping con PHP

\n\n

El web scraping consiste en extraer datos de sitios web de forma automatizada. PHP ofrece varias herramientas para ello, desde funciones básicas como file_get_contents hasta librerías especializadas como Goutte o Symfony Panther.

\n\n

Scraping básico con DOMDocument

\n\n
<?php\n$html = file_get_contents('https://example.com');\n$dom = new DOMDocument();\n@$dom->loadHTML($html);\n\n$xpath = new DOMXPath($dom);\n$titulos = $xpath->query('//h1');\n\nforeach ($titulos as $titulo) {\n    echo "Título: " . $titulo->nodeValue . "\\n";\n}\n?>
\n\n

Este código descarga el HTML de una página y extrae todos los encabezados <h1>. Es un enfoque simple pero funcional para páginas estáticas.

\n\n

Scraping avanzado con Goutte

\n\n

Goutte es una librería que combina Symfony BrowserKit y DomCrawler, facilitando la navegación y extracción de datos:

\n\n
composer require fabpot/goutte
\n\n
<?php\nrequire 'vendor/autoload.php';\n\nuse Goutte\\Client;\n\n$client = new Client();\n$crawler = $client->request('GET', 'https://news.ycombinator.com');\n\n$crawler->filter('.titleline a')->each(function ($node) {\n    echo $node->text() . \"\\n\";\n});\n?>
\n\n

Este ejemplo extrae los títulos de las noticias de Hacker News. Goutte maneja formularios, cookies y navegación entre páginas, ideal para scraping más complejo.

\n\n

Conclusión

\n\n

La programación en red con PHP es mucho más accesible de lo que parece. Desde sockets básicos hasta WebSocket y scraping, PHP ofrece herramientas maduras y bien documentadas para cualquier tarea de comunicación en red.

\n\n

¿Quieres profundizar? Te recomiendo explorar ReactPHP para aplicaciones asíncronas, Symfony HttpClient para peticiones HTTP avanzadas, y PHP WebSockets para proyectos en tiempo real. El límite está en tu imaginación (y en el tiempo de ejecución de PHP, claro).

\n\n

¡Feliz codificación!

Blogs

Book Recommendations