Módulo 03: Operadores
Objetivos del módulo
- Realizar cálculos con los operadores aritméticos, incluidos
**,%,intdiv()yfdiv() - Usar la asignación compuesta (
+=,.=,??=…) y el incremento/decremento - Comparar valores de forma segura y entender la diferencia entre
==y=== - Aplicar el operador nave espacial
<=> - Combinar condiciones con operadores lógicos y conocer la trampa de
and/or - Escribir expresiones concisas con el ternario,
?:,??y?-> - Concatenar cadenas con
.y.= - Conocer los operadores de bits,
@einstanceof - Dar los primeros pasos con el operador pipe
|>de PHP 8.5 - Saber leer la precedencia de operadores
1. ¿Qué es un operador?
📘 Concepto: Un operador es un símbolo que realiza una operación sobre uno o más valores (operandos) y produce un resultado. En
$total = $precio * 3, el*es un operador que multiplica los operandos$precioy3, y=es otro operador que guarda el resultado en$total.
Según el número de operandos:
| Tipo | Operandos | Ejemplos |
|---|---|---|
| Unario | Uno | -$x, !$activo, $i++ |
| Binario | Dos | $a + $b, $a === $b, $a . $b |
| Ternario | Tres | $condicion ? $si : $no |
📘 Concepto: Una expresión es cualquier fragmento de código que produce un valor:
5,$precio,$precio * 1.21,strlen('hola') > 3. Los operadores combinan expresiones para formar expresiones más grandes.
2. Operadores aritméticos
| Operador | Nombre | Ejemplo | Resultado |
|---|---|---|---|
+ | Suma | 7 + 2 | 9 |
- | Resta | 7 - 2 | 5 |
* | Multiplicación | 7 * 2 | 14 |
/ | División | 7 / 2 | 3.5 |
% | Módulo (resto de la división entera) | 7 % 2 | 1 |
** | Potencia | 7 ** 2 | 49 |
-$a | Negación (cambio de signo) | -7 | -7 |
<?php
declare(strict_types=1);
$a = 17;
$b = 5;
echo "Suma: " . ($a + $b) . PHP_EOL; // 22
echo "Resta: " . ($a - $b) . PHP_EOL; // 12
echo "Multiplicación: " . ($a * $b) . PHP_EOL; // 85
echo "División: " . ($a / $b) . PHP_EOL; // 3.4
echo "Resto: " . ($a % $b) . PHP_EOL; // 2
echo "Potencia: " . ($a ** 2) . PHP_EOL; // 289
La división /
La división devuelve un int si el resultado es exacto, y un float si no lo es:
<?php
var_dump(10 / 2); // int(5)
var_dump(10 / 4); // float(2.5)
var_dump(1 / 3); // float(0.3333333333333333)
División entera: intdiv()
Si necesitas el cociente entero (sin decimales), usa intdiv():
<?php
$minutosTotales = 135;
$horas = intdiv($minutosTotales, 60); // 2
$minutos = $minutosTotales % 60; // 15
echo "{$horas} h {$minutos} min" . PHP_EOL; // 2 h 15 min
💡 Consejo:
intdiv()trunca hacia cero:intdiv(-7, 2)da-3, mientras quefloor(-7 / 2)da-4.0. Elige según lo que necesites.
División por cero y fdiv()
<?php
// echo 10 / 0; // DivisionByZeroError: Division by zero
// echo 10 % 0; // DivisionByZeroError: Modulo by zero
// echo intdiv(10, 0); // DivisionByZeroError: Division by zero
var_dump(fdiv(10, 0)); // float(INF) - infinito
var_dump(fdiv(-10, 0)); // float(-INF)
var_dump(fdiv(0, 0)); // float(NAN) - "no es un número"
📘 Concepto: Desde PHP 8, dividir entre cero con
/o%lanza una excepciónDivisionByZeroErrorque detiene el script (salvo que la captures, módulo 14).fdiv()sigue la norma IEEE 754 de los números en coma flotante y devuelveINF,-INFoNAN. Puedes comprobarlos conis_infinite()yis_nan().
El módulo %
El resto es muy útil para saber si un número es par, si es múltiplo de otro o para hacer cosas cíclicas:
<?php
$numero = 14;
echo $numero % 2 === 0 ? 'Par' : 'Impar'; // Par
echo $numero % 7 === 0 ? 'Múltiplo de 7' : 'No'; // Múltiplo de 7
// Colores alternos en filas de una tabla
for ($fila = 0; $fila < 4; $fila++) {
$clase = $fila % 2 === 0 ? 'clara' : 'oscura';
echo "<tr class=\"$clase\">...</tr>" . PHP_EOL;
}
⚠️ Importante:
%trabaja con enteros:7.5 % 2convierte7.5en7(y PHP avisa de que se pierde precisión). Para el resto de números decimales usafmod(7.5, 2)→1.5. Además, el signo del resultado es el del dividendo:-10 % 3da-1.
La potencia **
<?php
var_dump(2 ** 10); // int(1024)
var_dump(2 ** -1); // float(0.5)
var_dump(2 ** 0.5); // float(1.4142135623730951) - raíz cuadrada
var_dump(2 ** 3 ** 2); // int(512) - es asociativo por la derecha: 2 ** (3 ** 2)
var_dump(-2 ** 2); // int(-4) - ¡la potencia se hace antes que el signo!
var_dump((-2) ** 2); // int(4)
Funciones matemáticas útiles
| Función | Ejemplo | Resultado |
|---|---|---|
abs() | abs(-5) | 5 |
round() | round(3.14159, 2) | 3.14 |
round() | round(2.5) | 3.0 (redondea “alejándose del cero”) |
ceil() | ceil(4.1) | 5.0 (hacia arriba) |
floor() | floor(4.9) | 4.0 (hacia abajo) |
sqrt() | sqrt(16) | 4.0 |
max() / min() | max(1, 5, 3) / min([4, 2, 8]) | 5 / 2 |
random_int() | random_int(1, 6) | Entero aleatorio seguro entre 1 y 6 |
pi() / M_PI | M_PI | 3.141592653589793 |
3. Operadores de asignación
Asignación simple
El operador = guarda el valor de la derecha en la variable de la izquierda. No significa “igual” (eso es == o ===):
<?php
$precio = 25; // Asigna 25 a $precio
$total = $precio * 2; // Calcula a la derecha y guarda el resultado
// La asignación también es una expresión que devuelve el valor asignado
$a = $b = 0; // $b = 0 y después $a = 0
Asignación compuesta
Combinan una operación y una asignación. $x += 5 es una forma abreviada de $x = $x + 5:
| Operador | Equivale a | Ejemplo ($x = 10) | Resultado |
|---|---|---|---|
+= | $x = $x + n | $x += 5 | 15 |
-= | $x = $x - n | $x -= 3 | 7 |
*= | $x = $x * n | $x *= 2 | 20 |
/= | $x = $x / n | $x /= 4 | 2.5 |
%= | $x = $x % n | $x %= 3 | 1 |
**= | $x = $x ** n | $x **= 2 | 100 |
.= | $x = $x . texto | $x .= '€' | '10€' |
??= | $x = $x ?? valor | $x ??= 99 | 10 (ya tenía valor) |
<?php
declare(strict_types=1);
$saldo = 1000;
$saldo -= 250; // Pago con tarjeta
$saldo += 1200; // Ingreso de la nómina
$saldo *= 1.02; // Intereses del 2 %
echo "Saldo: $saldo €" . PHP_EOL; // Saldo: 1989 €
Asignación de fusión de null: ??=
??= asigna un valor solo si la variable no existe o vale null. Es perfecto para valores por defecto:
<?php
$config = ['idioma' => 'es'];
$config['idioma'] ??= 'en'; // Ya existe: no cambia ('es')
$config['tema'] ??= 'claro'; // No existe: se asigna 'claro'
$config['por_pagina'] ??= 20; // No existe: se asigna 20
print_r($config);
// Array ( [idioma] => es [tema] => claro [por_pagina] => 20 )
4. Incremento y decremento
| Operador | Nombre | Efecto |
|---|---|---|
++$x | Pre-incremento | Suma 1 a $x y después devuelve el nuevo valor |
$x++ | Post-incremento | Devuelve el valor actual de $x y después le suma 1 |
--$x | Pre-decremento | Resta 1 y devuelve el nuevo valor |
$x-- | Post-decremento | Devuelve el valor actual y después resta 1 |
<?php
$a = 5;
echo $a++ . PHP_EOL; // 5 (devuelve 5, luego $a pasa a 6)
echo $a . PHP_EOL; // 6
$b = 5;
echo ++$b . PHP_EOL; // 6 (primero incrementa, luego devuelve)
echo $b . PHP_EOL; // 6
💡 Consejo: Cuando el incremento va en su propia línea (
$contador++;), pre y post dan exactamente igual. La diferencia solo importa si usas el valor en la misma expresión. Para que el código sea claro, evita expresiones como$i++ + ++$i(vale 12 si$iera 5, pero nadie debería tener que pensarlo).
Casos especiales
<?php
$n = null;
$n++; // $n vale 1
$m = null;
$m--; // ⚠️ Sigue valiendo null (y PHP 8.5 avisa: no tiene efecto)
$letra = 'a';
$letra++; // 'b' - PHP incrementa letras... pero está obsoleto
⚠️ Importante: Incrementar cadenas no numéricas (
'a'→'b','Az'→'Ba') está obsoleto en PHP moderno y genera un aviso Deprecated. Si de verdad lo necesitas, usa la funciónstr_increment('Az')(PHP 8.3), que deja clara la intención.
5. Operadores de comparación
Devuelven siempre un booleano (true o false):
| Operador | Nombre | Es true si… |
|---|---|---|
$a == $b | Igualdad (débil) | Son iguales tras convertir tipos |
$a === $b | Identidad (estricta) | Son iguales y del mismo tipo |
$a != $b | Desigualdad | No son iguales tras convertir tipos |
$a <> $b | Desigualdad | Igual que != (forma alternativa, poco usada) |
$a !== $b | No idénticos | Distintos en valor o en tipo |
$a < $b | Menor que | $a es menor que $b |
$a > $b | Mayor que | $a es mayor que $b |
$a <= $b | Menor o igual | $a es menor o igual que $b |
$a >= $b | Mayor o igual | $a es mayor o igual que $b |
$a <=> $b | Nave espacial | Devuelve -1, 0 o 1 (no un booleano) |
== frente a ===: la regla de oro
<?php
var_dump(5 == '5'); // bool(true) - convierte '5' en 5
var_dump(5 === '5'); // bool(false) - int frente a string
var_dump(0 == false); // bool(true)
var_dump(0 === false); // bool(false)
var_dump(null == ''); // bool(true)
var_dump(null === ''); // bool(false)
⚠️ Importante: Usa siempre
===y!==salvo que tengas un motivo concreto para querer la conversión de tipos. Con==es muy fácil que dos valores “distintos” se consideren iguales, y eso, en una comprobación de contraseñas, permisos o importes, es un error grave.
Tabla de comparaciones peligrosas con ==
Todos estos resultados son reales en PHP 8.5:
| Expresión | == | === | Explicación |
|---|---|---|---|
'1' == '01' | true | false | Dos cadenas numéricas se comparan como números |
'10' == '1e1' | true | false | '1e1' es notación científica: 10 |
100 == '1e2' | true | false | Ídem: 1e2 = 100 |
'1' == ' 1' | true | false | Los espacios alrededor de un número se permiten |
null == false | true | false | Ambos se convierten en false |
[] == false | true | false | El array vacío es “falso” |
'0' == false | true | false | La cadena '0' es “falsa” |
'' == null | true | false | Ambos “vacíos” |
null == 0 | true | false | null se convierte en 0 |
true == 'hola' | true | false | Cualquier cadena no vacía es “verdadera” |
null < -1 | true | — | null se convierte a false y -1 a true |
PHP_INT_MAX + 1 == PHP_INT_MAX | true | false | Al comparar con un float se pierde precisión |
'abc' == 0 | false | false | En PHP 7 era true. PHP 8 corrigió este caso |
0 == '' | false | false | En PHP 7 era true. También corregido en PHP 8 |
📘 Concepto: PHP 8 introdujo las “comparaciones cadena-número más sensatas”: si la cadena no es numérica, el número se convierte en cadena y se comparan como texto. Por eso
'abc' == 0dejó de sertrue. Aun así, como ves en la tabla,==sigue escondiendo muchas sorpresas.
Comparar cadenas
Las cadenas se comparan carácter a carácter según su código (orden “ASCII”), salvo que ambas sean numéricas:
<?php
var_dump('abc' < 'abd'); // bool(true)
var_dump('apple' < 'banana'); // bool(true)
var_dump('Z' < 'a'); // bool(true) - ¡las mayúsculas van antes!
var_dump('10' < '9'); // bool(false) - ambas numéricas: 10 < 9 es falso
var_dump('10' < '9a'); // bool(true) - '9a' no es numérica: compara texto
💡 Consejo: Para ordenar texto de forma natural o sin distinguir mayúsculas existen
strcmp(),strcasecmp(),strnatcmp()y la claseCollatorde la extensión intl (módulo 06).
Comparar arrays
<?php
$a = ['nombre' => 'Ana', 'edad' => 30];
$b = ['edad' => 30, 'nombre' => 'Ana'];
var_dump($a == $b); // bool(true) - mismos pares clave/valor
var_dump($a === $b); // bool(false) - además exige mismo orden y tipos
var_dump([1, 2] == ['1', '2']); // bool(true)
var_dump([1, 2] === ['1', '2']); // bool(false)
El operador nave espacial <=>
$a <=> $b devuelve:
-1si$aes menor que$b0si son iguales1si$aes mayor que$b
Su nombre viene de su forma, que recuerda a una nave de Star Wars: <=>.
<?php
var_dump(5 <=> 3); // int(1)
var_dump(3 <=> 5); // int(-1)
var_dump(5 <=> 5); // int(0)
var_dump('b' <=> 'a'); // int(1)
var_dump([1, 2, 3] <=> [1, 2, 4]); // int(-1)
Su gran utilidad es ordenar. Las funciones de ordenación personalizada como usort() (módulo 05) esperan justo ese -1/0/1:
<?php
declare(strict_types=1);
$productos = [
['nombre' => 'Ratón', 'precio' => 19.90],
['nombre' => 'Monitor', 'precio' => 189.00],
['nombre' => 'Teclado', 'precio' => 45.50],
];
// Ordenar por precio de menor a mayor
usort($productos, fn(array $a, array $b): int => $a['precio'] <=> $b['precio']);
foreach ($productos as $p) {
echo "{$p['nombre']}: {$p['precio']} €" . PHP_EOL;
}
// Ratón: 19.9 €
// Teclado: 45.5 €
// Monitor: 189 €
💡 Consejo: Para ordenar de mayor a menor, simplemente intercambia los operandos:
$b['precio'] <=> $a['precio'].
6. Operadores lógicos
Combinan condiciones booleanas:
| Operador | Nombre | Es true si… |
|---|---|---|
$a && $b | Y (AND) | Ambos son true |
$a \|\| $b | O (OR) | Al menos uno es true |
!$a | NO (NOT) | $a es false |
$a xor $b | O exclusivo | Exactamente uno de los dos es true |
$a and $b | Y | Como &&, pero con precedencia muy baja |
$a or $b | O | Como \|\|, pero con precedencia muy baja |
Tablas de verdad
$a | $b | $a && $b | $a \|\| $b | $a xor $b | !$a |
|---|---|---|---|---|---|
true | true | true | true | false | false |
true | false | false | true | true | false |
false | true | false | true | true | true |
false | false | false | false | false | true |
<?php
declare(strict_types=1);
$edad = 25;
$tieneCarne = true;
$sancionado = false;
$puedeAlquilar = $edad >= 21 && $tieneCarne && !$sancionado;
var_dump($puedeAlquilar); // bool(true)
$esFinDeSemana = in_array(date('N'), ['6', '7'], true);
$esFestivo = false;
$cerrado = $esFinDeSemana || $esFestivo;
Evaluación en cortocircuito
PHP deja de evaluar en cuanto conoce el resultado:
- En
$a && $b, si$aesfalse, no evalúa$b(el resultado ya esfalse). - En
$a || $b, si$aestrue, no evalúa$b(el resultado ya estrue).
Esto permite escribir comprobaciones seguras:
<?php
declare(strict_types=1);
$usuario = null;
// Si $usuario es null, no se intenta acceder a $usuario['rol']
if ($usuario !== null && $usuario['rol'] === 'admin') {
echo 'Bienvenido, administrador';
}
$divisor = 0;
// Si $divisor es 0, no se llega a dividir
if ($divisor !== 0 && 100 / $divisor > 5) {
echo 'Resultado grande';
}
💡 Consejo: Coloca primero la condición más barata o la que protege a las demás. Por ejemplo, comprueba que una variable no es
nullantes de usarla, o comprueba un valor en memoria antes de consultar la base de datos.
La trampa de and y or
and y or tienen menor precedencia que el operador de asignación =. Esto produce resultados inesperados:
<?php
$a = true && false; // $a = (true && false) → false
$b = true and false; // ($b = true) and false → ¡$b vale true!
var_dump($a); // bool(false)
var_dump($b); // bool(true)
⚠️ Importante: Usa siempre
&&y||. Las formasandyorsolo aparecen en código antiguo con el patrón$conexion = conectar() or die('Error');, que hoy se sustituye por excepciones.
7. Operador ternario y ternario corto
Ternario ? :
Es un if/else en una sola expresión: condición ? valorSiTrue : valorSiFalse.
<?php
declare(strict_types=1);
$nota = 6.5;
$resultado = $nota >= 5 ? 'Aprobado' : 'Suspenso';
echo $resultado . PHP_EOL; // Aprobado
$articulos = 1;
echo "Tienes $articulos " . ($articulos === 1 ? 'artículo' : 'artículos') . PHP_EOL;
// Tienes 1 artículo
En plantillas HTML es muy práctico:
<li class="<?= $esActivo ? 'menu-activo' : '' ?>">Inicio</li>
<p>Stock: <?= $stock > 0 ? $stock . ' unidades' : 'Agotado' ?></p>
⚠️ Importante: Desde PHP 8, anidar ternarios sin paréntesis es un error fatal:
$a ? 1 : $b ? 2 : 3no compila. Si de verdad necesitas anidarlos, usa paréntesis… aunque en ese caso casi siempre es mejor unmatch(módulo 04).
<?php
$nota = 8;
// ❌ Error fatal en PHP 8
// $texto = $nota >= 9 ? 'Sobresaliente' : $nota >= 7 ? 'Notable' : 'Otro';
// ✅ Con paréntesis (correcto pero poco legible)
$texto = $nota >= 9 ? 'Sobresaliente' : ($nota >= 7 ? 'Notable' : 'Otro');
Ternario corto ?: (operador Elvis)
$a ?: $b devuelve $a si es verdadero (truthy) y $b en caso contrario. Equivale a $a ? $a : $b:
<?php
$nombreUsuario = '';
echo $nombreUsuario ?: 'Anónimo'; // Anónimo (la cadena vacía es falsa)
$descuento = 0;
echo $descuento ?: 'Sin descuento'; // Sin descuento (¡0 también es falso!)
💡 Consejo: El nombre “Elvis” viene de que
?:recuerda al tupé y los ojos de Elvis Presley de lado. Ten presente que trata como “falsos” el0,'0',''y[], lo que no siempre es lo que quieres.
8. Operador de fusión de null ??
$a ?? $b devuelve $a si existe y no es null; si no, devuelve $b. Además, no genera avisos si la variable o la clave no existen:
<?php
declare(strict_types=1);
// Leer un parámetro de la URL con valor por defecto
$pagina = $_GET['pagina'] ?? 1;
// Encadenar varias alternativas: se queda con la primera que no sea null
$idioma = $_GET['lang'] ?? $_COOKIE['lang'] ?? 'es';
$usuario = ['nombre' => 'Elena', 'apodo' => null];
echo $usuario['apodo'] ?? $usuario['nombre']; // Elena
?? frente a ?:
Valor de $v | $v ?? 'defecto' | $v ?: 'defecto' |
|---|---|---|
| No definida | 'defecto' (sin aviso) | 'defecto' + Warning |
null | 'defecto' | 'defecto' |
0 | 0 | 'defecto' |
'' | '' | 'defecto' |
'0' | '0' | 'defecto' |
false | false | 'defecto' |
'Ana' | 'Ana' | 'Ana' |
💡 Consejo: Usa
??cuando lo que te preocupa es que el dato no exista (parámetros opcionales, claves de arrays, configuración). Usa?:cuando quieres sustituir cualquier valor vacío. En la mayoría de los casos,??es la opción correcta.
9. Operador nullsafe ?-> (adelanto)
Lo estudiarás con detalle en los módulos de POO, pero conviene que lo reconozcas. ?-> accede a una propiedad o método de un objeto solo si el objeto no es null; si lo es, toda la expresión devuelve null sin error:
<?php
declare(strict_types=1);
$fechaBaja = null; // El cliente sigue activo
$fechaAlta = new DateTimeImmutable('2024-03-15');
echo $fechaAlta?->format('d/m/Y') . PHP_EOL; // 15/03/2024
var_dump($fechaBaja?->format('d/m/Y')); // NULL (sin error)
// Combinado con ?? para mostrar un texto alternativo
echo $fechaBaja?->format('d/m/Y') ?? 'Cliente activo'; // Cliente activo
Sin ?-> tendrías que escribir:
<?php
echo $fechaBaja !== null ? $fechaBaja->format('d/m/Y') : 'Cliente activo';
10. Operadores de cadena: . y .=
El punto une (concatena) cadenas:
<?php
declare(strict_types=1);
$nombre = 'Sergio';
$apellido = 'Ramos';
$completo = $nombre . ' ' . $apellido; // 'Sergio Ramos'
// .= añade al final de una cadena existente
$html = '<ul>';
$html .= '<li>Inicio</li>';
$html .= '<li>Contacto</li>';
$html .= '</ul>';
echo $html; // <ul><li>Inicio</li><li>Contacto</li></ul>
⚠️ Importante: No confundas
+con.. En JavaScript+concatena, pero en PHP'5' + '5'es10(suma) y'5' . '5'es'55'(concatenación).
Concatenación y precedencia
Desde PHP 8, + y - tienen más prioridad que .:
<?php
echo 'Total: ' . 10 + 5; // Total: 15 (en PHP 8: 'Total: ' . (10 + 5))
echo 'Total: ' . (10 + 5); // Total: 15 ✅ Mejor así: explícito
💡 Consejo: Pon paréntesis alrededor de las operaciones matemáticas dentro de una concatenación. No depende de recordar la precedencia y cualquiera lo entiende a la primera.
11. Operadores de bits (breve)
Trabajan con la representación binaria de los enteros. No los usarás a menudo, pero aparecen en configuraciones de PHP y en opciones de funciones:
| Operador | Nombre | Ejemplo | Binario | Resultado |
|---|---|---|---|---|
& | Y bit a bit | 6 & 3 | 110 & 011 | 2 (010) |
\| | O bit a bit | 6 \| 3 | 110 \| 011 | 7 (111) |
^ | O exclusivo | 6 ^ 3 | 110 ^ 011 | 5 (101) |
~ | Negación | ~6 | invierte todos los bits | -7 |
<< | Desplazamiento izquierda | 1 << 3 | 1 → 1000 | 8 |
>> | Desplazamiento derecha | 16 >> 2 | 10000 → 100 | 4 |
Uso real: combinar opciones (flags):
<?php
// En php.ini o con error_reporting(): todos los errores excepto los avisos de obsoleto
error_reporting(E_ALL & ~E_DEPRECATED);
// Varias opciones en una sola llamada con |
echo json_encode(
['ciudad' => 'Cádiz', 'url' => 'https://ejemplo.es/a/b'],
JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
);
{
"ciudad": "Cádiz",
"url": "https://ejemplo.es/a/b"
}
⚠️ Importante: No confundas
&con&&ni|con||. Los primeros operan bit a bit con números; los segundos son operadores lógicos con booleanos y cortocircuito.
12. Operador de supresión de errores @ (desaconsejado)
Colocado delante de una expresión, @ oculta los avisos y warnings que genere:
<?php
$contenido = @file_get_contents('no-existe.txt'); // Sin Warning en pantalla
var_dump($contenido); // bool(false)
Parece cómodo, pero es una mala práctica:
| Problema | Consecuencia |
|---|---|
| Esconde errores | El fallo sigue ocurriendo, pero no te enteras |
| Dificulta la depuración | Pasas horas buscando un problema que PHP te estaba explicando |
| No evita errores fatales | Desde PHP 8, @ ya no silencia los errores fatales |
La alternativa correcta es comprobar antes o gestionar el error:
<?php
declare(strict_types=1);
$ruta = 'datos.txt';
if (is_readable($ruta)) {
$contenido = file_get_contents($ruta);
} else {
$contenido = '';
error_log("No se puede leer $ruta");
}
13. Operador de tipo instanceof (adelanto)
instanceof comprueba si un objeto es de una clase (o la hereda, o implementa una interfaz):
<?php
$fecha = new DateTimeImmutable();
var_dump($fecha instanceof DateTimeImmutable); // bool(true)
var_dump($fecha instanceof DateTimeInterface); // bool(true) - implementa la interfaz
var_dump($fecha instanceof DateTime); // bool(false)
Lo usarás mucho en el bloque de POO (módulos 11 y 12) y al capturar excepciones (módulo 14).
14. El operador pipe |> (PHP 8.5)
📘 Concepto: El operador pipe (tubería)
|>toma el valor de su izquierda y se lo pasa como único argumento a la función de su derecha. Permite escribir transformaciones encadenadas en el orden en que ocurren, de izquierda a derecha, como una cadena de montaje.
El problema que resuelve
Imagina que quieres convertir el título ' Ofertas de Verano 2026 ' en un slug para la URL (ofertas-de-verano-2026). Sin pipe hay dos opciones, ambas incómodas:
<?php
declare(strict_types=1);
$titulo = ' Ofertas de Verano 2026 ';
// Opción 1: funciones anidadas. Se lee de dentro hacia fuera.
$slug = str_replace(' ', '-', strtolower(trim($titulo)));
// Opción 2: variables intermedias. Más clara pero más ruidosa.
$paso1 = trim($titulo);
$paso2 = strtolower($paso1);
$slug = str_replace(' ', '-', $paso2);
Con el operador pipe
<?php
declare(strict_types=1);
$titulo = ' Ofertas de Verano 2026 ';
$slug = $titulo
|> trim(...)
|> strtolower(...)
|> (fn(string $texto): string => str_replace(' ', '-', $texto));
echo $slug; // ofertas-de-verano-2026
Se lee como una receta: “toma el título, luego quita los espacios, luego pásalo a minúsculas, luego cambia los espacios por guiones”.
Reglas básicas
| Regla | Ejemplo |
|---|---|
| A la derecha va algo invocable que reciba un parámetro | \|> strtoupper(...) |
strtoupper(...) es un first-class callable: una referencia a la función, sin llamarla | \|> strlen(...) |
| Si la función necesita más argumentos, envuélvela en una arrow function | \|> (fn($s) => str_pad($s, 10)) |
Las arrow functions a la derecha de \|> deben ir entre paréntesis | Sin paréntesis: error de compilación |
| Se evalúa de izquierda a derecha | $x \|> f(...) \|> g(...) equivale a g(f($x)) |
<?php
declare(strict_types=1);
$total = [12.5, 8.0, 30.25]
|> array_sum(...)
|> (fn(float $suma): float => $suma * 1.21)
|> (fn(float $conIva): float => round($conIva, 2));
echo $total; // 61.41
💡 Consejo: El operador pipe brilla cuando hay tres o más transformaciones seguidas. Para una sola llamada,
strtoupper($texto)sigue siendo más claro que$texto |> strtoupper(...). En el módulo 07 (Funciones) verás los callables, las arrow functions y patrones más avanzados con|>.
15. Precedencia de operadores
📘 Concepto: La precedencia determina qué operaciones se hacen primero cuando hay varias en la misma expresión, igual que en matemáticas la multiplicación va antes que la suma:
2 + 3 * 4es14, no20. La asociatividad decide el orden cuando los operadores tienen la misma precedencia (izquierda o derecha).
Tabla simplificada de PHP 8.5, de mayor a menor precedencia:
| Nivel | Operadores | Asociatividad | Tipo |
|---|---|---|---|
| 1 | clone, new | — | Objetos |
| 2 | ** | Derecha | Potencia |
| 3 | ++, --, ~, (int) y demás casts, @, + y - unarios | — | Unarios |
| 4 | instanceof | Izquierda | Tipo |
| 5 | ! | — | Lógico |
| 6 | *, /, % | Izquierda | Aritmético |
| 7 | +, - | Izquierda | Aritmético |
| 8 | <<, >> | Izquierda | Bits |
| 9 | . | Izquierda | Cadena |
| 10 | \|> | Izquierda | Pipe (PHP 8.5) |
| 11 | <, <=, >, >= | — | Comparación |
| 12 | ==, !=, ===, !==, <>, <=> | — | Comparación |
| 13 | & | Izquierda | Bits |
| 14 | ^ | Izquierda | Bits |
| 15 | \| | Izquierda | Bits |
| 16 | && | Izquierda | Lógico |
| 17 | \|\| | Izquierda | Lógico |
| 18 | ?? | Derecha | Fusión de null |
| 19 | ? : | No asociativo | Ternario |
| 20 | =, +=, -=, *=, .=, ??=… | Derecha | Asignación |
| 21 | yield from, yield, print | — | Varios |
| 22 | and | Izquierda | Lógico |
| 23 | xor | Izquierda | Lógico |
| 24 | or | Izquierda | Lógico |
Ejemplos
<?php
var_dump(2 + 3 * 4); // int(14) - * antes que +
var_dump((2 + 3) * 4); // int(20) - los paréntesis mandan
var_dump(-3 ** 2); // int(-9) - ** antes que el - unario
var_dump(!true || true); // bool(true) - ! antes que ||: (!true) || true
var_dump(!(true || true)); // bool(false)
var_dump(10 - 4 - 2); // int(4) - izquierda: (10 - 4) - 2
var_dump(2 ** 2 ** 3); // int(256) - derecha: 2 ** (2 ** 3)
var_dump(5 > 3 && 2 > 1); // bool(true) - comparaciones antes que &&
💡 Consejo: No necesitas memorizar la tabla. La regla profesional es: si tienes que pensar en la precedencia, pon paréntesis. El código se escribe una vez y se lee muchas.
16. Ejercicios
Ejercicio 1: Calculadora de tiempo
Dado un número de segundos (por ejemplo, $segundos = 98765;), calcula y muestra cuántos días, horas, minutos y segundos son, usando intdiv() y %. Salida esperada:
98765 segundos = 1 días, 3 horas, 26 minutos y 5 segundos
Ejercicio 2: Factura con asignación compuesta
Partiendo de $total = 0;, añade con += tres productos (19,99 €, 5,50 € y 42,00 €), aplica un descuento del 10 % con *=, suma 4,95 € de envío y añade el 21 % de IVA. Construye con .= una cadena $ticket con una línea por cada paso. Muestra el total final con round($total, 2).
Ejercicio 3: == o ===
Sin ejecutarlo, indica qué devuelve cada comparación y después compruébalo con var_dump():
<?php
'0' == false;
'0' === false;
'abc' == 0;
null == 0;
'1.0' == '1';
'1.0' === '1';
[] == false;
' 5' == 5;
Escribe en un comentario cuál de ellas te parece más peligrosa y por qué.
Ejercicio 4: Ordenar con la nave espacial
Crea un array de 5 alumnos con nombre y nota. Ordénalo con usort() y <=>:
- Por nota de mayor a menor.
- Por nombre alfabéticamente.
- Por nota descendente y, en caso de empate, por nombre ascendente. Pista:
($b['nota'] <=> $a['nota']) ?: ($a['nombre'] <=> $b['nombre']).
Ejercicio 5: Valores por defecto
Simula los parámetros de una búsqueda con $parametros = ['q' => 'portátil', 'orden' => '', 'pagina' => '0'];. Obtén $q, $orden, $pagina y $porPagina (clave inexistente) con valores por defecto '', 'relevancia', 1 y 20. Hazlo una vez con ?? y otra con ?:, y explica qué diferencias aparecen y cuál es más adecuada para cada campo.
Ejercicio 6: Puede entrar
Escribe una expresión lógica que determine si una persona puede entrar a un concierto: debe tener entrada y (ser mayor de 18 o ir acompañada de un adulto), y no estar en la lista de expulsados. Prueba al menos cuatro combinaciones de variables y muestra el resultado con un ternario ('Puede entrar' / 'No puede entrar').
Ejercicio 7: Pipeline de texto con |>
Usando el operador pipe, transforma el texto ' hola, ESTO es un Título MAL escrito ' en 'Hola, esto es un título mal escrito': quita espacios, pasa a minúsculas con mb_strtolower(...) y pon la primera letra en mayúscula con mb_ucfirst(...) (PHP 8.4). Después añade un paso que le agregue un punto final con una arrow function.
Ejercicio 8: Precedencia
Calcula a mano el resultado de cada expresión y verifícalo:
<?php
$a = 10 + 5 * 2 - 8 / 4;
$b = (10 + 5) * (2 - 8) / 4;
$c = 2 ** 3 * 2;
$d = 'Resultado: ' . 3 + 4;
$e = true || false && false;
$f = 7 % 3 * 2 ** 2;
Resumen
| Categoría | Operadores | Recuerda |
|---|---|---|
| Aritméticos | + - * / % ** | / puede dar float; % trabaja con enteros |
| Funciones numéricas | intdiv(), fdiv(), fmod(), round() | intdiv(7, 2) → 3; fdiv(1, 0) → INF |
| Asignación | = += -= *= /= %= **= .= ??= | ??= asigna solo si es null o no existe |
| Incremento | ++$x, $x++, --$x, $x-- | Pre: cambia y devuelve; post: devuelve y cambia |
| Comparación | === !== == != < > <= >= | Usa siempre === y !== |
| Nave espacial | <=> | Devuelve -1, 0 o 1; ideal para usort() |
| Lógicos | && \|\| ! xor | Cortocircuito; evita and/or |
| Ternario | $c ? $a : $b | Anidar sin paréntesis es error en PHP 8 |
| Elvis | $a ?: $b | Sustituye cualquier valor “falso” |
| Fusión de null | $a ?? $b | Sustituye solo null o inexistente, sin avisos |
| Nullsafe | $obj?->metodo() | Devuelve null si $obj es null |
| Cadena | . y .= | + suma, . concatena |
| Bits | & \| ^ ~ << >> | Combinar flags: A \| B |
| Supresión | @ | Desaconsejado: comprueba o gestiona el error |
| Tipo | instanceof | ¿Es este objeto de esta clase? |
| Pipe (8.5) | $x \|> f(...) | Encadena funciones de izquierda a derecha |
| Precedencia | Paréntesis ( ) | Si dudas, pon paréntesis |