Aprende a leer, entender y corregir código real. Dos lenguajes, en paralelo, desde las primeras instrucciones hasta las librerías profesionales. Pulsa ▶ en cualquier bloque para ver la ejecución paso a paso.
Una variable es una caja con nombre donde guardamos un valor. En Python el tipo se deduce solo; en PHP toda variable empieza con $. Ambos son tipado dinámico: la caja cambia de tipo según lo que metes.
| Concepto | Python | PHP |
|---|---|---|
| Declarar variable | nombre = "Ana" | $nombre = "Ana"; |
| Entero | edad = 25 | $edad = 25; |
| Decimal | precio = 3.14 | $precio = 3.14; |
| Booleano | activo = True | $activo = true; |
| Sin valor | x = None | $x = null; |
| Saber el tipo | type(x) | gettype($x) |
| Imprimir | print(x) | echo $x; |
| Fin de línea | — (salto de línea) | ; obligatorio |
Condicionales y bucles son el esqueleto de cualquier programa. En Python el bloque se define por indentación (espacios); en PHP por llaves { }. Ambos funcionan igual en lógica, distinto en forma.
if tiene 4 espacios, todo su contenido debe tenerlos. Un espacio de más o de menos y el código falla. Es el error más común para quien llega del HTML.
| Estructura | Python | PHP |
|---|---|---|
| if / sino | if x > 0: / else: | if ($x > 0) { / } else { |
| else if | elif | elseif |
| Bucle contado | for i in range(5): | for ($i=0; $i<5; $i++) { |
| Bucle mientras | while condicion: | while ($cond) { |
| Recorrer lista | for item in lista: | foreach ($arr as $item) { |
| Bloque de código | indentación (4 espacios) | llaves { } |
Una función es un bloque de código con nombre que puedes reutilizar. Recibe parámetros (entradas) y puede devolver un valor (salida). El principio DRY: Don't Repeat Yourself — si copias código, convierte eso en función.
def (Python) o function (PHP) · nombre · paréntesis con parámetros · bloque de código · return opcional. Si no hay return, devuelve None / null.
Las listas (Python) y arrays (PHP) son colecciones de valores bajo un mismo nombre. Se accede a cada elemento por su índice, que empieza siempre en 0. Son la estructura de datos más usada junto con los diccionarios/arrays asociativos.
-1 accede al último elemento: truco muy útil.
| Operación | Python (lista) | PHP (array) |
|---|---|---|
| Crear | nums = [1, 2, 3] | $nums = [1, 2, 3]; |
| Acceder | nums[0] | $nums[0] |
| Añadir al final | nums.append(4) | $nums[] = 4; |
| Longitud | len(nums) | count($nums) |
| Eliminar último | nums.pop() | array_pop($nums) |
| ¿Contiene? | 3 in nums | in_array(3, $nums) |
| Ordenar | nums.sort() | sort($nums) |
| Clave → valor | {"a": 1} dict | ["a" => 1] assoc array |
Una clase es una plantilla. Un objeto es una instancia de esa plantilla. Si "Perro" es la clase, "Laika" y "Rex" son objetos. Cada objeto tiene sus propios atributos (datos) y métodos (funciones). Este paradigma se llama POO — Programación Orientada a Objetos.
self / $this es la forma de referirse al objeto actual. Es como decir "yo mismo". Sin él no puedes acceder a los atributos del objeto desde dentro.
Una librería es código escrito por otros que importas para no reinventar la rueda. NumPy trabaja con números y matrices; Pandas con tablas de datos (piensa en Excel programable). En PHP las librerías más usadas son las propias del lenguaje para web: base de datos, JSON, fechas.
import numpy as np carga la librería y le da un alias corto. En PHP require incluye otro fichero PHP (una librería, una clase) en el programa actual. El concepto es el mismo: traer código externo al tuyo.
NumPy — cálculo numérico
NumPy no tiene equivalente en PHP
PHP no se usa para cálculo científico. Para ese trabajo existen Python + NumPy, R, o MATLAB.
PHP sí tiene array_sum(), array_map() y funciones matemáticas básicas — pero no operaciones vectorizadas.
Pandas — tablas de datos
Mapa rápido de librerías por uso:
| Uso | Python | PHP |
|---|---|---|
| Cálculo numérico | numpy | — (no aplica) |
| Tablas de datos | pandas | arrays asociativos |
| Gráficas | matplotlib / seaborn | — (JS en frontend) |
| Web / API | flask / fastapi | nativo + slim |
| Base de datos | sqlalchemy / pymysql | PDO / mysqli |
| JSON | json.loads() | json_decode() |
| Fechas | datetime | DateTime / date() |
| IA / ML | scikit-learn / torch | — (no aplica) |
| Peticiones HTTP | requests | curl / file_get_contents |
Buscar un elemento, comprobar si existe y filtrar por condición son las operaciones más frecuentes cuando un LLM te devuelve datos en forma de lista. Python tiene sintaxis muy expresiva para esto; PHP usa funciones de librería estándar.
[x for x in lista if condicion] es la forma pythonica de filtrar en una línea. Equivale a un bucle for + if + append. Cuando veas corchetes con for dentro, es esto.| Operación | Python | PHP |
|---|---|---|
| ¿Contiene? | x in lista | in_array($x, $arr) |
| Buscar índice | lista.index(x) | array_search($x, $arr) |
| Filtrar | [x for x in l if cond] | array_filter($arr, fn) |
| Transformar | [f(x) for x in l] | array_map(fn, $arr) |
| Contar coincidencias | lista.count(x) | array_count_values($arr) |
El texto es el tipo de dato más frecuente cuando trabajas con LLMs. Limpiar, buscar, dividir y recomponer strings es imprescindible. Python trata los strings como objetos con métodos; PHP usa funciones con prefijo str_.
f"Hola {nombre}" inserta variables en Python. En PHP las comillas dobles también interpolan: "Hola $nombre". Para concatenar, PHP usa el punto .. Ambas formas aparecen constantemente en código generado por IA.| Operación | Python | PHP |
|---|---|---|
| Longitud | len(s) | strlen($s) |
| Mayúsculas / Minúsc. | s.upper() / s.lower() | strtoupper($s) / strtolower($s) |
| ¿Contiene? | "sub" in s | str_contains($s, "sub") |
| Reemplazar | s.replace("a","b") | str_replace("a","b",$s) |
| Dividir | s.split(",") | explode(",", $s) |
| Unir lista | ",".join(lista) | implode(",", $arr) |
| Quitar espacios | s.strip() | trim($s) |
| Interpolación | f"Hola {var}" | "Hola $var" |
| Subcadena | s[2:5] | substr($s, 2, 3) |
Cuando algo falla, el programa lanza una excepción. Si no la capturas, el programa se detiene. El bloque try/except (Python) o try/catch (PHP) intercepta el error y permite continuar. El código que generan los LLMs casi siempre incluye esto.
try va el código que puede fallar. En except/catch va qué hacer si falla. finally se ejecuta siempre — ideal para cerrar archivos o conexiones de base de datos.| Concepto | Python | PHP |
|---|---|---|
| Bloque protegido | try: | try { |
| Capturar error | except Exception as e: | catch (Exception $e) { |
| Siempre ejecutar | finally: | finally { |
| Lanzar error | raise ValueError("msg") | throw new Exception("msg") |
| Mensaje del error | str(e) | $e->getMessage() |
| División por cero | ZeroDivisionError | Exception (manual) |
| Tipo incorrecto | TypeError | TypeError |
JSON es el formato universal de intercambio de datos en la web. Cuando un LLM te genera código que llama a una API — o cuando tú llamas a un LLM por API — los datos viajan en JSON. Entender cómo convertir entre JSON y estructuras nativas es esencial.
json.dumps / json_encode). El proceso inverso — texto JSON a estructura de datos — es deserializar (json.loads / json_decode). Cuando ves estos nombres en código de IA, ya sabes qué hacen.| Operación | Python | PHP |
|---|---|---|
| JSON → datos | json.loads(texto) | json_decode($texto) |
| Datos → JSON | json.dumps(datos) | json_encode($datos) |
| JSON bonito | json.dumps(d, indent=2) | json_encode($d, JSON_PRETTY_PRINT) |
| Petición GET | requests.get(url) | file_get_contents($url) |
| Petición con cabeceras | requests.get(url, headers={}) | curl + CURLOPT_HTTPHEADER |
| Leer respuesta | r.json() | json_decode($respuesta) |
Las expresiones regulares son patrones para buscar, validar y extraer texto. Un LLM las genera con frecuencia para limpiar datos o validar formularios. No hace falta memorizarlas — sí hace falta reconocerlas y saber qué buscar.
/patron/flags. El patrón describe qué buscar: . cualquier carácter, * cero o más, + uno o más, ^ inicio, $ fin, [abc] uno de esos, \d dígito, \w letra/número, \s espacio. Los flags más comunes: i ignora mayúsculas, g busca todas las ocurrencias.| Operación | Python | PHP |
|---|---|---|
| ¿Coincide? | re.search(pat, s) | preg_match($pat, $s) |
| Todas las coincidencias | re.findall(pat, s) | preg_match_all($pat, $s, $m) |
| Reemplazar | re.sub(pat, repl, s) | preg_replace($pat, $repl, $s) |
| Dividir | re.split(pat, s) | preg_split($pat, $s) |
| Formato patrón | r"patron" (raw string) | "/patron/flags" |
Cuando un LLM genera código de machine learning con Keras o TensorFlow, aparecen siempre los mismos bloques. No necesitas ejecutarlo — sí necesitas reconocer qué hace cada parte: dónde están los datos, dónde se define la red, dónde se entrena y dónde se predice.
| Bloque de código | Qué hace | Palabras clave |
|---|---|---|
| Importar librerías | Cargar herramientas | import keras, from tensorflow, import sklearn |
| Preparar datos | Cargar, limpiar, dividir train/test | train_test_split, fit_transform, X_train, y_train |
| Definir modelo | Arquitectura de la red | Sequential(), Dense(), layers, activation |
| Compilar | Configurar aprendizaje | model.compile(), optimizer, loss, metrics |
| Entrenar | Aprender de los datos | model.fit(), epochs, batch_size |
| Evaluar / Predecir | Comprobar y usar el modelo | model.predict(), model.evaluate(), score() |
Leer y escribir archivos es una tarea básica en cualquier backend. Python usa el gestor de contexto with open(); PHP tiene fopen/fclose y funciones de alto nivel. Además veremos el buffer de salida, control de errores, locale y zona horaria — y las cookies en PHP.
with lo hace automáticamente al salir. En PHP llama a fclose() o usa file_put_contents() que no deja nada abierto.
fopen() / open():
| Modo | Descripción | Python | PHP |
|---|---|---|---|
| r | Solo lectura. El fichero debe existir. | open(f,"r") | fopen($f,"r") |
| w | Solo escritura. Crea o vacía el fichero. | open(f,"w") | fopen($f,"w") |
| a | Añadir al final. Crea si no existe. | open(f,"a") | fopen($f,"a") |
| r+ | Lectura y escritura. El fichero debe existir. | open(f,"r+") | fopen($f,"r+") |
| w+ | Lectura y escritura. Crea o vacía. | open(f,"w+") | fopen($f,"w+") |
| x | Crea exclusivo. Falla si el fichero ya existe. | open(f,"x") | fopen($f,"x") |
| rb/wb | Binario. Añade b a cualquier modo. | open(f,"rb") | fopen($f,"rb") |
io.StringIO() — fichero en memoria, sin disco. Útil para tests y parseo.try / except / finally — captura FileNotFoundError, PermissionError…import locale
locale.setlocale(locale.LC_TIME, 'es_ES.UTF-8')from datetime import datetime
from zoneinfo import ZoneInfo
dt = datetime.now(ZoneInfo('Europe/Madrid'))
print(dt.strftime('%A %d de %B de %Y'))
ob_start() — activa buffer de salida (todo echo queda en RAM).ob_get_clean() — obtiene el buffer y lo vacía.ob_end_flush() — vacía y desactiva el buffer.error_reporting(E_ALL);
ini_set('display_errors', '1'); — muestra todos los errores (solo en desarrollo).setlocale(LC_TIME, 'es_ES.UTF-8');
date_default_timezone_set('Europe/Madrid');
echo strftime('%A %d de %B de %Y');setcookie('usuario', 'Ana', time()+86400, '/');echo $_COOKIE['usuario'] ?? 'sin cookie';| Tarea | Python | PHP |
|---|---|---|
| Leer fichero completo | open(f).read() | file_get_contents($f) |
| Escribir fichero completo | open(f,'w').write(txt) | file_put_contents($f, $txt) |
| Añadir al final | open(f,'a').write(txt) | file_put_contents($f,$txt,FILE_APPEND) |
| Línea por línea | for linea in f: | while($l=fgets($f)) |
| Buffer en memoria | io.StringIO() | ob_start() … ob_get_clean() |
| Zona horaria | ZoneInfo('Europe/Madrid') | date_default_timezone_set('Europe/Madrid') |
| Locale de fechas | locale.setlocale(LC_TIME,'es_ES.UTF-8') | setlocale(LC_TIME,'es_ES.UTF-8') |
| Leer cookie | no aplica (server-side) | $_COOKIE['clave'] |
| Escribir cookie | — | setcookie('c','v',time()+86400,'/') |
En PHP, $_POST y $_GET son arrays superglobales que permiten acceder a los datos enviados a través de los métodos HTTP POST y GET, respectivamente.
$_GET: Los datos se envían a través de la URL (ej: pagina.php?nombre=Sergio). Son visibles y se usan para consultas simples.
$_POST: Los datos se envían en el cuerpo de la petición HTTP (no visibles en la URL). Se usan para formularios con información sensible (ej: contraseñas).
$_GET y $_POST con funciones como htmlspecialchars() o filter_var() para evitar ataques como XSS o inyección SQL.
// Ejemplo de $_GET
if (isset($_GET['nombre'])) {
echo "Hola, " . htmlspecialchars($_GET['nombre']);
} else {
echo "No se recibió el parámetro 'nombre' en la URL.";
}
// Ejemplo de $_POST (simulado para el editor)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edad'])) {
echo "Tu edad es: " . intval($_POST['edad']);
} else {
echo "No se recibió el parámetro 'edad' por POST.";
}
TensorFlow.js es una librería de JavaScript que permite ejecutar modelos de Machine Learning directamente en el navegador. Aquí tienes un ejemplo sencillo con tensores locales (sin necesidad de descargar modelos externos).
// Ejemplo de TensorFlow.js con tensores locales
async function runTFJSExample() {
const outputDiv = document.getElementById('out-tfjs');
outputDiv.innerHTML = 'Cargando TensorFlow.js...';
try {
// Crear un tensor simple
const tensor = tf.tensor2d([[1, 2], [3, 4]]);
const result = tensor.mul(2); // Multiplicar por 2
// Mostrar resultado en el HTML
outputDiv.innerHTML = `
salida
Tensor original: [[1, 2], [3, 4]]
Resultado (x2): [[${result.get(0, 0)}, ${result.get(0, 1)}], [${result.get(1, 0)}, ${result.get(1, 1)}]]
`;
} catch (error) {
outputDiv.innerHTML = `Error${error.message}`;
}
}
Con pandas, puedes leer y manipular archivos CSV directamente en el navegador usando Pyodide.
Aquí tienes un ejemplo con datos de ejemplo generados en el código.
StringIO para simular un archivo CSV sin necesidad de subir ficheros.
import pandas as pd
from io import StringIO
data = "nombre,edad,ciudad\nAna,25,Madrid\nLuis,30,Barcelona"
df = pd.read_csv(StringIO(data))
repr(df)
# Esto devuelve una cadena con la representación del DataFrame
Hasta ahora has ejecutado Python y PHP directamente en tu navegador, sin servidor. Vamos a hacer lo mismo con una base de datos relacional: aquí corre SQLite real, compilado a WebAssembly (sql.js), igual que Pyodide hace con Python. No hay servidor MySQL/MariaDB detrás, no se toca infodocencia.net para nada: tu base de datos vive solo en tu navegador.
localStorage de tu navegador tras cada CREATE/INSERT/UPDATE/DELETE. Si cierras y vuelves, sigue ahí. Usa ↺ Reiniciar para volver a los datos de ejemplo, o ⬇ Descargar .sqlite para guardarte una copia real del fichero.
Para practicar usaremos una mini librería online, con 5 tablas ya cargadas con datos de ejemplo:
| Tabla | Columnas |
|---|---|
autores | id, nombre, pais |
libros | id, titulo, autor_id → autores(id), precio, anio, stock |
clientes | id, nombre, email, ciudad |
pedidos | id, cliente_id → clientes(id), fecha, total |
pedido_detalle | id, pedido_id → pedidos(id), libro_id → libros(id), cantidad |
DDL (Data Definition Language) es el SQL que define la estructura: qué tablas existen, qué columnas tiene cada una, de qué tipo, y cómo se relacionan entre sí mediante claves. Es el "plano" de la base de datos.
libros.autor_id apunta a autores.id, y decimos "cada libro pertenece a un autor". Sin foreign keys tendrías que repetir el nombre del autor en cada fila de libro; con ellas, lo escribes una vez y lo referencias.
| Operación | SQL |
|---|---|
| Crear tabla | CREATE TABLE t (id INTEGER PRIMARY KEY, ...) |
| Clave primaria | id INTEGER PRIMARY KEY |
| Clave foránea | autor_id INTEGER REFERENCES autores(id) |
| Obligatorio | nombre TEXT NOT NULL |
| Único | email TEXT UNIQUE |
| Valor por defecto | stock INTEGER DEFAULT 0 |
| Añadir columna | ALTER TABLE t ADD COLUMN col TEXT |
| Borrar tabla | DROP TABLE t |
Con la estructura ya creada, el DML (Data Manipulation Language) mete y modifica los datos que viven dentro: INSERT, UPDATE y DELETE.
WHERE. Un UPDATE o DELETE sin WHERE afecta a todas las filas de la tabla, no solo a una. Es el error más caro y más común en SQL — ejecuta siempre primero un SELECT con el mismo WHERE para comprobar qué filas vas a tocar.
| Operación | SQL |
|---|---|
| Insertar fila | INSERT INTO t (col1,col2) VALUES (a,b) |
| Actualizar | UPDATE t SET col=valor WHERE id=1 |
| Borrar filas | DELETE FROM t WHERE id=1 |
| Vaciar tabla entera | DELETE FROM t (¡sin WHERE!) |
El DQL (Data Query Language) es, en la práctica, un único comando con muchas piezas: SELECT. Filtra con WHERE, ordena con ORDER BY, limita con LIMIT, y busca patrones de texto con LIKE.
LIKE y sus comodines: % significa "cero o más caracteres cualquiera", _ significa "exactamente un carácter". LIKE '%García%' encuentra "García" en cualquier posición del texto. Esto es búsqueda literal (con acentos y mayúsculas según configuración) — no es búsqueda fonética. Para encontrar palabras que suenan parecido aunque estén mal escritas (Gonzalez / Gonzales), MySQL/MariaDB traen la función SOUNDEX(), que no existe en SQLite; la cubrimos en el manual de MySQL al final del curso.
| Operación | SQL |
|---|---|
| Todo | SELECT * FROM t |
| Columnas concretas | SELECT col1, col2 FROM t |
| Filtrar | WHERE precio > 10 |
| Rango | WHERE anio BETWEEN 1960 AND 1980 |
| Lista de valores | WHERE pais IN ('Chile','Argentina') |
| Patrón de texto | WHERE titulo LIKE '%amor%' |
| Sin valor | WHERE autor_id IS NULL |
| Ordenar | ORDER BY precio DESC |
| Limitar filas | LIMIT 5 / LIMIT 5 OFFSET 10 |
Los datos están repartidos en varias tablas a propósito (para no repetirlos). JOIN las vuelve a juntar en el momento de consultar. Una subconsulta es un SELECT completo metido dentro de otro, para usar su resultado como filtro.
INNER JOIN devuelve solo las filas que coinciden en ambas tablas (un libro sin autor asignado desaparecería). LEFT JOIN devuelve todas las filas de la tabla izquierda, aunque no haya coincidencia a la derecha (rellenando con NULL). Regla práctica: si la pregunta es "¿qué libros no tienen pedidos?", necesitas LEFT JOIN + WHERE ... IS NULL, o una subconsulta con NOT IN.
| Operación | SQL |
|---|---|
| Unir por coincidencia | FROM libros l JOIN autores a ON l.autor_id=a.id |
| Unir conservando la izquierda | FROM libros l LEFT JOIN pedido_detalle d ON d.libro_id=l.id |
| Subconsulta como valor | WHERE precio > (SELECT AVG(precio) FROM libros) |
| Subconsulta como lista | WHERE id IN (SELECT libro_id FROM pedido_detalle) |
| Negación de lista | WHERE id NOT IN (SELECT ...) |
Las funciones de agregación resumen muchas filas en una: COUNT, SUM, AVG, MIN, MAX. GROUP BY las aplica por grupo (por autor, por cliente, por ciudad...) en vez de a toda la tabla.
WHERE se aplica antes de agrupar (fila a fila); HAVING se aplica después de agrupar, sobre el resultado ya resumido — por eso HAVING COUNT(*) > 2 funciona y WHERE COUNT(*) > 2 da error. Con esto, más JOIN, LIKE y subconsultas de las secciones anteriores, cubres el 20% de SQL que resuelve el 80% de las consultas del día a día — el resto (índices, transacciones, procedimientos...) está en el manual final, para cuando pases a MySQL/MariaDB en un hosting real.
| Operación | SQL |
|---|---|
| Contar filas | COUNT(*) |
| Sumar / Media | SUM(col) / AVG(col) |
| Mínimo / Máximo | MIN(col) / MAX(col) |
| Agrupar | GROUP BY columna |
| Filtrar grupos | HAVING SUM(col) > 20 |
| Texto sin repetidos | SELECT DISTINCT pais FROM autores |
Has completado todas las secciones. Escribe tu nombre para generar tu certificado verificable.
Pega el código base64 de cualquier diploma para comprobar su autenticidad y ver los datos originales.