Módulo 03: Operadores

Objetivos del módulo

  • Realizar cálculos con los operadores aritméticos, incluidos **, %, intdiv() y fdiv()
  • 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, @ e instanceof
  • 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 $precio y 3, 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 que floor(-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ón DivisionByZeroError que detiene el script (salvo que la captures, módulo 14). fdiv() sigue la norma IEEE 754 de los números en coma flotante y devuelve INF, -INF o NAN. Puedes comprobarlos con is_infinite() y is_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 % 2 convierte 7.5 en 7 (y PHP avisa de que se pierde precisión). Para el resto de números decimales usa fmod(7.5, 2) → 1.5. Además, el signo del resultado es el del dividendo: -10 % 3 da -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 $i era 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ón str_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' == 0 dejó de ser true. 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 clase Collator de 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:

  • -1 si $a es menor que $b
  • 0 si son iguales
  • 1 si $a es 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 $a es false, no evalúa $b (el resultado ya es false).
  • En $a || $b, si $a es true, no evalúa $b (el resultado ya es true).

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 null antes 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 formas and y or solo 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 : 3 no compila. Si de verdad necesitas anidarlos, usa paréntesis… aunque en ese caso casi siempre es mejor un match (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” el 0, '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' es 10 (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 * 4 es 14, no 20. 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 <=>:

  1. Por nota de mayor a menor.
  2. Por nombre alfabéticamente.
  3. 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