Cheatsheet PHP
Linguagem de programação web server-side
PHP
Básico
Abrir y Cerrar PHP
<?php // Código PHP aquí echo "Hola Mundo"; ?> <!-- Tag corta (echo) --> <?= "Salida direta" ?> <!-- En archivos .php puros, omitir el ?> --> <?php echo "Sem fecho necesario";
Usa <?php para abrir y ?> para cerrar. La tag corta <?= equivale a echo. En archivos 100% PHP, omite el ?> final para evitar output accidental.
Concatenación e Interpolación
$nombre = "Ana";
$edad = 25;
// Concatenación con punto
echo "Hola " . $nombre . "!";
// Interpolación (comillas dobles)
echo "Hola $nombre, tienes $edad anios";
echo "Hola {$nombre}!"; // con llaves
// Comillas simples (literal)
echo 'Hola $nombre'; // muestra: Hola $nombre
// Heredoc (multi-línea)
$texto = <<<TXT
Hola $nombre,
Tienes $edad anios.
TXT;
// Nowdoc (sin interpolación)
$raw = <<<'TXT'
Sem $interpolación aqui
TXT;El operador . concatena strings. Las comillas dobles "$var" interpolan variables. {$var} para expresiones complejas. Heredoc permite multi-línea con interpolación; Nowdoc sin ella.
Operador Ternario y Elvis
// Ternario completo
$status = ($edad >= 18) ? "adulto" : "menor";
// Elvis (si truthy, usa el valor)
$nombre = $input ?: "Anónimo";
// Equivalente a: $input ? $input : "Anónimo"
// Null coalescing (si no-null)
$nombre = $_GET['nombre'] ?? "Visitante";
// Encadenamiento
$nivel = $score > 90 ? "A"
: ($score > 70 ? "B" : "C");
// Ternario en HTML
<?= $activo ? "Activo" : "Inactivo" ?>El ternario ? : es un if/else compacto. El operador Elvis ?: retorna el valor si truthy. ?? solo verifica null. Úsalo con moderación — los ternarios anchos son difíciles de leer.
Constantes Mágicas
__LINE__; // numero de la línea actual __FILE__; // camino completo del archivo __DIR__; // carpeta del archivo __FUNCTION__; // nombre de la función __CLASS__; // nombre de la clase __METHOD__; // Clase::método __NAMESPACE__; // namespace actual // Uso práctico require __DIR__ . '/config.php'; echo "Error em " . __FILE__ . ":" . __LINE__; // PHP 8.1: enum __TRAIT__; // nombre de la trait
Las constantes mágicas empiezan y terminan con __. __DIR__ sustituye dirname(__FILE__). __LINE__ da la línea actual. Útiles para debug, includes y logging.
Variables
$nombre = "Cláudio";
$edad = 30;
$precio = 19.99;
$activo = true;
$nulo = null;
// Variables dinámicas
$campo = "nombre";
echo $$campo; // "Cláudio"
// Constantes
define("MAX", 100);
const PI = 3.14159;
// Verificar existencia
isset($nombre); // true
isset($inexist); // falseLas variables empiezan con $. define() y const crean constantes. isset() verifica si existe y no es null. Tipos: string, int, float, bool, null.
Superglobales
$_GET['id']; // parámetros URL $_POST['nombre']; // datos del form $_REQUEST['x']; // GET + POST + COOKIE $_SESSION['user']; // sesión $_COOKIE['token']; // cookies $_SERVER['HTTP_HOST']; // servidor $_FILES['upload']; // upload $_ENV['APP_KEY']; // entorno // Ejemplo práctico $id = $_GET['id'] ?? 0; $nombre = $_POST['nombre'] ?? '';
Las superglobales son arrays automáticos disponibles en todas partes. $_GET para URL, $_POST para formularios, $_SESSION para sesión. ¡Siempre sanitiza antes de usar!
Incremento y Asignación
$x = 5; $x++; // 6 (post-incremento) ++$x; // 7 (pre-incremento) $x--; // 6 (post-decremento) // Asignación compuesta $x += 10; // $x = $x + 10 $x -= 3; // $x = $x - 3 $x *= 2; // $x = $x * 2 $x /= 4; // $x = $x / 4 $x %= 3; // $x = $x % 3 $x .= "!"; // $x = $x . "!" (string) $x ??= "default"; // solo si null
++$x incrementa antes de usar; $x++ usa y luego incrementa. .= concatena strings. ??= asigna solo si la variable es null. +=, -=, *= son atajos aritméticos.
Tipos de Datos
// Tipos escalares $str = "texto"; // string $num = 42; // int $float = 3.14; // float $bool = true; // bool // Tipos compuestos $arr = [1, 2, 3]; // array $obj = new stdClass; // object // Verificar tipo gettype($num); // "integer" is_string($str); // true is_int($num); // true is_array($arr); // true // Casting $str2 = (string) 42; // "42" $int2 = (int) "42px"; // 42 $bool2 = (bool) ""; // false
Usa gettype() para ver el tipo e is_*() para verificar. (int), (string), (bool) hacen casting. Valores falsy: 0, "", null, false, [].
Include y Require
// Incluye archivo (warning si no existe) include 'header.php'; // Obligatorio (fatal error si no existe) require 'config.php'; // Incluye solo 1 vez require_once 'database.php'; include_once 'helpers.php'; // Camino relativo al archivo actual require __DIR__ . '/partials/nav.php'; // Retornar valores de archivos $config = require 'config.php';
require para archivos esenciales (se detiene si falla). include para opcionales. *_once evita inclusión duplicada. __DIR__ da el camino absoluto de la carpeta actual.
Echo, Print y Output
// echo (más rápido, sin retorno)
echo "Hola", " ", "Mundo"; // múltiples args
echo "Hola" . " Mundo"; // concatenación
// print (retorna 1)
print "Hola Mundo";
// printf (formateado)
printf("%s tem %d anios", "Ana", 25);
// Output buffering
ob_start();
echo "Contenido capturado";
$html = ob_get_clean();
// print_r para debug
print_r($array);echo es más rápido y acepta múltiples argumentos. print retorna 1 (usable en expresiones). printf() formatea con placeholders. ob_start()/ob_get_clean() capturan output en una variable.
Operadores
// Aritméticos $a + $b; // suma $a - $b; // resta $a * $b; // multiplicación $a / $b; // división $a % $b; // resto $a ** $b; // potencia (2**3 = 8) // Comparación $a == $b; // igual (valor) $a === $b; // idéntico (valor + tipo) $a != $b; // diferente $a <=> $b; // spaceship (-1, 0, 1) // Lógicos $a && $b; // AND $a || $b; // OR !$a; // NOT // Null coalescing $val = $_GET['x'] ?? 'default';
=== compara valor Y tipo (prefiérelo siempre). <=> (spaceship) retorna -1, 0 o 1. ?? retorna el primer valor no-null. ** es potencia. % es el resto de la división.
Comentarios
// Comentario de una línea
# También funciona (estilo shell)
/*
* Comentario
* multi-línea
*/
/**
* DocBlock para funciones/clases
* @param string $nombre
* @return string
*/
function saudar(string $nombre): string {
return "Hola $nombre";
}// y # para una línea. /* */ para multi-línea. /** */ (DocBlock) para documentación — los IDEs leen @param, @return, @throws.
Conversión de Tipos (Casting)
// Casting explícito $str = (string) 42; // "42" $int = (int) "42px"; // 42 $float = (float) "3.14"; // 3.14 $bool = (bool) ""; // false $arr = (array) $obj; // objeto → array // settype (cambia la variable) $var = "42"; settype($var, "integer"); // $var ahora es int // Conversión automática (type juggling) "10" + 5; // 15 (int) "10" . 5; // "105" (string) true + true; // 2 // PHP 8: más estricto // "abc" + 1; // TypeError!
Usa (int), (string), (float), (bool) para casting. settype() cambia el tipo de la variable. PHP 8 es más estricto: operaciones con strings no numéricas lanzan TypeError.
Strings
Funciones Básicas de Strings
$str = " Hola Mundo ";
strlen($str); // 13 (longitud)
trim($str); // "Hola Mundo"
strtoupper($str); // " HOLA MUNDO "
strtolower($str); // " hola mundo "
ucfirst("hola mundo"); // "Hola mundo"
ucwords("hola mundo"); // "Hola Mundo"
str_repeat("ha", 3); // "hahaha"
str_pad("5", 3, "0", STR_PAD_LEFT); // "005"
// Contar ocurrencias
substr_count("banana", "na"); // 2trim() elimina espacios en los extremos. strlen() da la longitud. strtoupper()/strtolower() cambian capitalización. str_pad() rellena hasta un tamaño. substr_count() cuenta repeticiones.
Formateo de Strings
// sprintf (formateo)
sprintf("Hola %s, tienes %d anios", "Ana", 25);
sprintf("Precio: %.2f€", 19.9); // "19.90€"
sprintf("ID: %05d", 42); // "ID: 00042"
sprintf("%x", 255); // "ff" (hex)
// number_format
number_format(1234567.891, 2, ",", ".");
// "1.234.567,89"
// money_format alternativo
"R$ " . number_format(99.9, 2);
// nl2br (saltos de línea → <br>)
echo nl2br("Linea 1\nLinha 2");
// htmlspecialchars (escapar HTML)
htmlspecialchars('<script>alert("x")</script>');sprintf() formatea con placeholders: %s (string), %d (int), %f (float). number_format() formatea numeros con separadores. htmlspecialchars() escapa HTML (previene XSS).
Serialización
// serialize / unserialize
$datos = ['nombre' => 'Ana', 'ítems' => [1,2,3]];
$str = serialize($datos);
// 'a:2:{s:4:"nombre";s:3:"Ana";...}'
$restaurado = unserialize($str);
// Cuidado: ¡seguridad!
// NUNCA unserialize() datos del usuario
// Usa json_encode/json_decode en su lugar
// __sleep y __wakeup
class User {
public function __sleep(): array {
return ['nombre', 'email']; // qué serializar
}
public function __wakeup(): void {
// tras unserialize
}
}serialize() convierte a string. unserialize() restaura. Peligroso: nunca uses unserialize() con datos del usuario (RCE). Prefiere json_encode()/json_decode(). __sleep()/__wakeup() controlan la serialización.
Substring y Posición
$str = "Hola Mundo PHP"; // Extraer parte substr($str, 4); // "Mundo PHP" substr($str, 4, 5); // "Mundo" substr($str, -3); // "PHP" // Encontrar posición strpos($str, "Mundo"); // 4 strrpos($str, "P"); // 10 (última) stripos($str, "mundo"); // 4 (case-insensitive) // Verificar si contiene str_contains($str, "Mundo"); // true (PHP 8+) str_starts_with($str, "Hola"); // true str_ends_with($str, "PHP"); // true
substr() extrae parte de la string. strpos() encuentra la posición (o false). PHP 8+: str_contains(), str_starts_with(), str_ends_with() — más legibles que strpos() !== false.
Regex con preg_*
// Testar si corresponde
preg_match('/^\d{9}$/', $telefono);
// Extraer primer match
preg_match('/(\d{4})-(\d{3})/', $str, $m);
$m[0]; // match completo
$m[1]; // 1º grupo
// Todos los matches
preg_match_all('/\d+/', "a1 b22 c333", $all);
$all[0]; // ["1", "22", "333"]
// Sustituir con regex
preg_replace('/\s+/', ' ', $texto);
preg_replace('/\d+/', 'X', "a1b2"); // "aXbX"
// Split por regex
preg_split('/[\s,]+/', "a, b c");preg_match() testa/encuentra el primer match. preg_match_all() encuentra todos. preg_replace() sustituye con patrón. Delimitadores: /, #, ~. Modificadores: i (case-insensitive), s (dotall).
mb_* — Strings Multibyte
$str = "Hola Mundo 🌍";
// strlen cuenta bytes, mb_strlen cuenta caracteres
strlen($str); // 16 (bytes)
mb_strlen($str); // 12 (caracteres)
// Substring multibyte
mb_substr($str, 0, 3); // "Hola"
mb_strtoupper($str); // "HOLA MUNDO 🌍"
mb_strtolower($str); // "hola mundo 🌍"
// Encoding
mb_detect_encoding($str); // "UTF-8"
mb_convert_encoding($str, 'ISO-8859-1');
// Internal encoding
mb_internal_encoding('UTF-8');Las funciones mb_* manejan UTF-8 correctamente. strlen() cuenta bytes, mb_strlen() cuenta caracteres. Usa mb_substr(), mb_strtoupper() para acentos y emojis. Define mb_internal_encoding('UTF-8') al inicio.
Sustitución
// Sustituir texto
str_replace("mundo", "PHP", "Hola mundo");
// "Hola PHP"
// Sustituir con conteo
str_replace("a", "o", "banana", $count);
// "bonono" ($count = 3)
// Sustituir con arrays
$search = ["foo", "bar"];
$replace = ["baz", "qux"];
str_replace($search, $replace, "foo bar");
// Sustituir por posición
substr_replace("Hola Mundo", "PHP", 4, 5);
// "Hola PHP"str_replace() sustituye todas las ocurrencias. El 4º parámetro (por referencia) cuenta cuántas sustituciones hizo. substr_replace() sustituye por posición. Para regex, usa preg_replace().
Comparación y Encoding
// Comparación
strcmp("abc", "abc"); // 0 (iguales)
strcasecmp("ABC", "abc"); // 0 (case-insensitive)
strncmp("abcdef", "abcxyz", 3); // 0 (3 chars)
// Encoding
mb_strlen("café"); // 4 (UTF-8 correcto)
mb_strtoupper("café"); // "CAFÉ"
mb_substr("Hola Mundo", 0, 3); // "Hola"
mb_detect_encoding($str);
// Convertir encoding
mb_convert_encoding($str, "UTF-8", "ISO-8859-1");
utf8_encode($str); // ISO → UTF-8
utf8_decode($str); // UTF-8 → ISOPara strings con acentos/emoji, usa funciones mb_* (multibyte). mb_strlen() cuenta caracteres correctamente. strcmp() compara strings (retorna 0 si iguales). Siempre usa UTF-8 en el proyecto.
sprintf y printf
// printf: output directo
printf("Nombre: %s, Edad: %d", "Ana", 25);
// sprintf: retorna string
$msg = sprintf("Total: %.2f EUR", 19.9);
// "Total: 19.90 EUR"
// Placeholders
sprintf("%05d", 42); // "00042" (padding)
sprintf("%-10s|", "Esq"); // "Esq |" (izquierda)
sprintf("%+d", 42); // "+42" (signo)
sprintf("%x", 255); // "ff" (hex)
sprintf("%b", 10); // "1010" (binario)
// Argumento posicional
sprintf("%2\$s %1\$s", "Mundo", "Hola");
// "Hola Mundo"sprintf() formatea y retorna string. printf() hace output directo. Placeholders: %s string, %d int, %f float, %x hex. Modificadores: %05d padding, %.2f decimales, %-10s alineación.
Split y Join
// Dividir string en array
$parts = explode(",", "a,b,c");
// ["a", "b", "c"]
$words = explode(" ", "Hola Mundo PHP", 2);
// ["Hola", "Mundo PHP"] (límite 2)
// Juntar array en string
$str = implode(", ", ["a", "b", "c"]);
// "a, b, c"
$url = implode("/", ["api", "users", "1"]);
// "api/users/1"
// Dividir en caracteres
$chars = str_split("ABC");
// ["A", "B", "C"]explode() divide por delimitador. El 3º parámetro limita el numero de partes. implode() junta array con separador. str_split() divide en caracteres individuales.
str_replace Avanzado
// Múltiples sustituciones
$búsqueda = ['foo', 'bar', 'baz'];
$troca = ['FOO', 'BAR', 'BAZ'];
str_replace($búsqueda, $troca, 'foo bar baz');
// strtr (más eficiente para múltiples)
strtr('foo bar', ['foo' => 'FOO', 'bar' => 'BAR']);
// Sustituir solo 1ª ocurrencia
$pos = strpos($str, 'needle');
if ($pos !== false) {
$str = substr_replace($str, 'nuevo', $pos, 6);
}
// Eliminar acentos
function removeAccents(string $s): string {
return strtr($s, 'áàãâéêíóôõúüç',
'aaaaeeiooouuc');
}str_replace() acepta arrays para múltiples sustituciones. strtr() es más eficiente para pares clave-valor. substr_replace() sustituye por posición. Para patrones complejos, usa preg_replace().
Trim y Padding Avanzado
$str = " Hola Mundo ";
trim($str); // "Hola Mundo"
ltrim($str); // "Hola Mundo "
rtrim($str); // " Hola Mundo"
// Trim con caracteres personalizados
trim("---texto---", "-"); // "texto"
trim("00042", "0"); // "42"
trim("\t\n texto \r\n"); // "texto"
// Padding
str_pad("5", 3, "0", STR_PAD_LEFT); // "005"
str_pad("Hi", 10, "-", STR_PAD_BOTH); // "----Hi----"
str_pad("OK", 6, ".", STR_PAD_RIGHT); // "OK...."
// Wordwrap
wordwrap("Texto largo aqui", 10, "\n");trim() elimina espacios (o caracteres indicados) de los extremos. ltrim()/rtrim() solo izquierda/derecha. str_pad() rellena hasta el tamaño deseado. wordwrap() quiebra texto en líneas de tamaño fijo.
Arrays
Crear Arrays
// Array indexado
$frutas = ["manzana", "banana", "pera"];
// Array asociativo
$user = [
"nombre" => "Ana",
"edad" => 30,
"activo" => true,
];
// Array multidimensional
$matriz = [
[1, 2, 3],
[4, 5, 6],
];
// Crear con range
$numeros = range(1, 10);
$letras = range('a', 'f');
// Array vacío
$vacio = [];Sintaxis corta [] (PHP 5.4+). Los arrays pueden ser indexados (0, 1, 2...) o asociativos (clave => valor). range() crea secuencias. Los arrays en PHP son mapas ordenados (aceptan cualquier tipo de clave).
array_reduce y array_keys
$numeros = [1, 2, 3, 4, 5];
// reduce: acumular en un valor
$suma = array_reduce($numeros, fn($acc, $n) => $acc + $n, 0);
// 15
$concat = array_reduce(["a","b","c"], fn($acc, $s) => $acc . $s, "");
// "abc"
// keys y values
$arr = ["nombre" => "Ana", "edad" => 30];
array_keys($arr); // ["nombre", "edad"]
array_values($arr); // ["Ana", 30]
// in_array (verificar existencia)
in_array("Ana", $arr); // true
in_array("Ana", $arr, true); // true (strict)
array_key_exists("nombre", $arr); // truearray_reduce() acumula todos los elementos en un único valor. array_keys()/array_values() extraen claves/valores. in_array() verifica si un valor existe. array_key_exists() verifica una clave.
array_column y array_multisort
$users = [
['nombre' => 'Ana', 'edad' => 30],
['nombre' => 'Ray', 'edad' => 25],
['nombre' => 'Eva', 'edad' => 35],
];
// Extraer columna
$nombres = array_column($users, 'nombre');
// ['Ana', 'Ray', 'Eva']
// Reindexar por columna
$byName = array_column($users, null, 'nombre');
// ['Ana' => [...], 'Ray' => [...], ...]
// Ordenar por columna
array_multisort(
array_column($users, 'edad'), SORT_ASC,
$users
);array_column() extrae una columna de un array multidimensional. Con null como 2º argumento, reindexa por la columna. array_multisort() ordena el array original por una columna específica.
array_flip y array_search
// array_flip: intercambia claves ↔ valores
$map = ['a' => 1, 'b' => 2, 'c' => 3];
array_flip($map); // [1 => 'a', 2 => 'b', 3 => 'c']
// array_search: encuentra la clave por valor
$frutas = ['manzana', 'banana', 'cereza'];
array_search('banana', $frutas); // 1
array_search('x', $frutas); // false
// array_key_exists vs isset
$arr = ['nombre' => null];
array_key_exists('nombre', $arr); // true
isset($arr['nombre']); // false (¡es null!)
// array_rand: clave(s) aleatoria(s)
array_rand($frutas); // 0, 1 o 2
array_rand($frutas, 2); // [0, 2] (2 claves)array_flip() intercambia claves y valores. array_search() retorna la clave del valor encontrado (o false). array_key_exists() verifica una clave incluso con valor null (a diferencia de isset()). array_rand() retorna clave(s) aleatoria(s).
Acceder y Modificar
$arr = ["a" => 1, "b" => 2, "c" => 3]; // Acceder $arr["a"]; // 1 $arr["b"] ?? 0; // 2 (con valor por defecto) // Modificar $arr["d"] = 4; // añadir $arr["a"] = 10; // actualizar unset($arr["b"]); // eliminar // Añadir al final/inicio $arr[] = 5; // final (push) array_push($arr, 6, 7); // final (múltiples) array_unshift($arr, 0); // inicio // Eliminar del final/inicio $último = array_pop($arr); $primero = array_shift($arr);
$arr[] añade al final. unset() elimina un elemento. array_push()/array_pop() para el final. array_unshift()/array_shift() para el inicio. ?? evita un error si la clave no existe.
Ordenación
$numeros = [3, 1, 4, 1, 5]; sort($numeros); // [1, 1, 3, 4, 5] (pierde claves) rsort($numeros); // [5, 4, 3, 1, 1] (reverso) // Preservando claves asort($numeros); // ordena por valor arsort($numeros); // reverso por valor ksort($numeros); // ordena por clave krsort($numeros); // reverso por clave // Ordenación personalizada $users = [["nombre" => "B"], ["nombre" => "A"]]; usort($users, fn($a, $b) => strcmp($a["nombre"], $b["nombre"])); // Ordenar por columna array_multisort(array_column($users, "nombre"), SORT_ASC, $users);
sort() ordena valores (pierde claves). asort() preserva claves. usort() acepta un callback personalizado. array_multisort() ordena por una columna de un array multidimensional. Prefijo r = reverso, k = por clave.
array_intersect y array_diff
$a = [1, 2, 3, 4, 5]; $b = [3, 4, 5, 6, 7]; // Intersección (comunes) array_intersect($a, $b); // [3, 4, 5] // Diferencia (en $a pero no en $b) array_diff($a, $b); // [1, 2] // Asociativo $x = ['a' => 1, 'b' => 2]; $y = ['b' => 2, 'c' => 3]; array_intersect_assoc($x, $y); // ['b' => 2] array_diff_key($x, $y); // ['a' => 1] // Combinar con spread $comum = [...array_intersect($a, $b)];
array_intersect() retorna valores comunes. array_diff() retorna valores que están en el 1º pero no en los otros. Sufijos: _key compara claves, _assoc compara ambos. Preservan las claves del 1º array.
Iterar Arrays
$frutas = ["manzana", "banana", "pera"];
// foreach simple
foreach ($frutas as $fruta) {
echo $fruta;
}
// foreach con clave
foreach ($frutas as $i => $fruta) {
echo "$i: $fruta";
}
// for tradicional
for ($i = 0; $i < count($frutas); $i++) {
echo $frutas[$i];
}
// while con each (antiguo)
// array_walk (callback)
array_walk($frutas, function($val, $key) {
echo "$key => $val\n";
});foreach es la forma preferida de iterar. $i => $val da clave y valor. count() retorna el tamaño. array_walk() aplica un callback a cada elemento. Evita modificar el array durante la iteración.
Merge, Slice y Chunk
$a = [1, 2, 3]; $b = [3, 4, 5]; // Merge $c = array_merge($a, $b); // [1,2,3,3,4,5] $d = [...$a, ...$b]; // spread (PHP 7.4+) // Asociativo: la última clave gana $x = ["a" => 1]; $y = ["a" => 2, "b" => 3]; array_merge($x, $y); // ["a" => 2, "b" => 3] // Slice (extraer parte) array_slice([1,2,3,4,5], 1, 3); // [2,3,4] // Chunk (dividir en grupos) array_chunk([1,2,3,4,5], 2); // [[1,2], [3,4], [5]] // Unique array_unique([1, 2, 2, 3, 3]); // [1, 2, 3]
array_merge() combina arrays. Spread [...$a, ...$b] es más moderno. array_slice() extrae una parte. array_chunk() divide en grupos. array_unique() elimina duplicados.
array_walk y array_walk_recursive
$frutas = ['manzana', 'banana', 'cereza'];
// array_walk: modifica por referencia
array_walk($frutas, function (&$item, $key) {
$item = ucfirst($item);
});
// ['Manzana', 'Banana', 'Cereza']
// Con parámetro extra
$prefijo = 'Fruta: ';
array_walk($frutas, function (&$item) use ($prefijo) {
$item = $prefijo . $item;
});
// Recursivo (arrays anidados)
$nested = ['a' => [1, 2], 'b' => [3, 4]];
array_walk_recursive($nested, function (&$val) {
$val *= 2;
});
// ['a' => [2, 4], 'b' => [6, 8]]array_walk() aplica un callback a cada elemento (por referencia). A diferencia de array_map(): modifica el original y pasa la clave como 2º argumento. array_walk_recursive() entra en arrays anidados.
array_map y array_filter
$numeros = [1, 2, 3, 4, 5];
// map: transformar cada elemento
$doble = array_map(fn($n) => $n * 2, $numeros);
// [2, 4, 6, 8, 10]
// filter: mantener los que pasan
$pares = array_filter($numeros, fn($n) => $n % 2 === 0);
// [2 => 2, 4 => 4]
// filter sin callback (elimina falsy)
$limpo = array_filter([0, 1, "", "a", null, 2]);
// [1 => 1, 3 => "a", 5 => 2]
// map con claves preservadas
$upper = array_map('strtoupper', ["ana", "rui"]);
// ["ANA", "RUI"]array_map() transforma cada elemento y retorna un nuevo array. array_filter() mantiene solo los que pasan la prueba. Sin callback, array_filter() elimina valores falsy. Ambos preservan las claves originales.
Destructuring y Spread
// Destructuring (list)
[$a, $b, $c] = [1, 2, 3];
echo $a; // 1
// Con claves
["nombre" => $nombre, "edad" => $edad] = [
"nombre" => "Ana", "edad" => 30
];
// Ignorar elementos
[, , $terceiro] = [1, 2, 3];
// Spread en llamadas
function suma($a, $b, $c) { return $a+$b+$c; }
$args = [1, 2, 3];
suma(...$args); // 6
// Spread para crear arrays
$base = [1, 2];
$extra = [...$base, 3, 4]; // [1,2,3,4]Destructuring [$a, $b] = [1, 2] extrae valores. Con claves: ["nombre" => $n]. Spread ... expande arrays en argumentos o en otro array. Una coma sola [, , $x] ignora posiciones.
array_unique y Conteo
$arr = [1, 2, 2, 3, 3, 3];
// Eliminar duplicados
array_unique($arr); // [1, 2, 3]
array_values(array_unique($arr)); // reindexar
// Conteo
count($arr); // 6
sizeof($arr); // 6 (alias)
// Conteo recursivo
$nested = ['a' => [1, 2], 'b' => [3]];
count($nested, COUNT_RECURSIVE); // 5
// Contar valores
array_count_values(['a','b','a','c','a']);
// ['a' => 3, 'b' => 1, 'c' => 1]
// Verificar existencia
in_array('banana', $frutas); // true/false
in_array('5', [5, 10], true); // false (strict)array_unique() elimina duplicados (preserva claves — usa array_values() para reindexar). count() cuenta elementos. array_count_values() cuenta ocurrencias. in_array() con 3º argumento true hace comparación estricta.
Controlo de Fluxo
If / Else / ElseIf
$edad = 20;
if ($edad >= 18) {
echo "Adulto";
} elseif ($edad >= 13) {
echo "Adolescente";
} else {
echo "Niño";
}
// Sintaxis alternativa (plantillas)
<?php if ($activo): ?>
<p>Activo</p>
<?php else: ?>
<p>Inactivo</p>
<?php endif; ?>
// Cadena de null coalescing
$val = $a ?? $b ?? $c ?? "default";if/elseif/else para condiciones. La sintaxis alternativa if: endif; es ideal en plantillas HTML. ?? encadenado verifica múltiples variables. Usa === en comparaciones para evitar la coerción de tipos.
Operadores de Comparación
// Loose vs Strict 0 == "0"; // true (compara valor) 0 === "0"; // false (compara valor + tipo) null == false; // true null === false; // false // Spaceship 1 <=> 2; // -1 2 <=> 2; // 0 3 <=> 2; // 1 // Útil en usort $nums = [3, 1, 2]; usort($nums, fn($a, $b) => $a <=> $b); // Null coalescing vs ternario $val = $x ?? "default"; // solo null $val = $x ?: "default"; // cualquier falsy
Siempre prefiere === (strict) sobre == (loose). <=> (spaceship) retorna -1, 0 o 1 — ideal para usort(). ?? solo se activa con null; ?: con cualquier valor falsy.
Bucles Anidados y Labels
// Bucle anidado con break/continue por nivel
for ($i = 1; $i <= 3; $i++) {
for ($j = 1; $j <= 3; $j++) {
if ($i === 2 && $j === 2) break 2;
echo "$i,$j ";
}
}
// 1,1 1,2 1,3 2,1
// foreach anidado
foreach ($categorias as $cat) {
foreach ($cat->productos as $prod) {
if ($prod->stock === 0) continue 2;
echo $prod->nombre;
}
}
// while con múltiples condiciones
$x = 0; $y = 10;
while ($x < 5 && $y > 3) {
$x++; $y--;
}break 2 y continue 2 actúan sobre el bucle externo. El numero indica cuántos niveles. En bucles anidados, usa continue 2 para saltar la iteración del bucle padre. Combina condiciones con && o ||.
Switch y Match
// Switch clásico
$dia = "segunda";
switch ($dia) {
case "segunda":
case "martes":
echo "Inicio";
break;
case "sexta":
echo "Fin";
break;
default:
echo "Medio";
}
// Match (PHP 8+) — más seguro
$resultado = match($dia) {
"segunda", "martes" => "Inicio",
"sexta" => "Fin",
default => "Medio",
};switch compara con == (loose). match (PHP 8+) compara con === (strict) y retorna un valor. match no necesita break y lanza un error si ningún caso coincide (sin default).
Sintaxis Alternativa (Plantillas)
<?php if ($loggedIn): ?>
<p>Bienvenido, <?= $nombre ?></p>
<?php else: ?>
<a href="/login">Entrar</a>
<?php endif; ?>
<?php foreach ($items as $item): ?>
<li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
<?php while ($row = $stmt->fetch()): ?>
<p><?= $row['nombre'] ?></p>
<?php endwhile; ?>La sintaxis alternativa if: endif;, foreach: endforeach;, while: endwhile; es ideal en plantillas HTML. Más legible que las llaves cuando se mezcla con HTML. Usa <?= para output escapado.
Expresión Match (PHP 8) 8.0
// Switch: verboso, comparación loose
switch ($status) {
case 'activo':
$label = 'Activo';
break;
case 'inactivo':
$label = 'Inactivo';
break;
default:
$label = 'Desconocido';
}
// Match: expresión, strict, sin break
$label = match($status) {
'activo' => 'Activo',
'inactivo', 'suspendido' => 'Bloqueado',
default => throw new Exception("Status inválido"),
};match (PHP 8+) es una expresión que retorna un valor. Compara con === (strict). No necesita break. Múltiples casos con comas. Sin default, lanza UnhandledMatchError si nada coincide.
For y Foreach
// for tradicional
for ($i = 0; $i < 10; $i++) {
echo $i;
}
// for con múltiples variables
for ($i = 0, $j = 10; $i < $j; $i++, $j--) {
echo "$i $j";
}
// foreach (arrays)
foreach ([1, 2, 3] as $val) {
echo $val;
}
// foreach por referencia
$arr = [1, 2, 3];
foreach ($arr as &$val) {
$val *= 2; // modifica el original
}
unset($val); // ¡importante!for cuando sabes el numero de iteraciones. foreach para arrays/objetos. &$val itera por referencia (modifica el original) — haz unset($val) después para evitar bugs.
Break, Continue y Goto
// break con nivel (bucles anidados)
for ($i = 0; $i < 10; $i++) {
for ($j = 0; $j < 10; $j++) {
if ($i * $j > 20) break 2; // sale de los 2
echo "$i x $j = " . ($i*$j);
}
}
// continue con nivel
for ($i = 0; $i < 5; $i++) {
for ($j = 0; $j < 5; $j++) {
if ($j === 2) continue 2; // salta el for externo
}
}
// goto (¡evitar!)
goto fin;
echo "Isto no executa";
fin:
echo "Saltou para aqui";break 2 sale de 2 bucles anidados. continue 2 salta a la siguiente iteración del bucle externo. goto existe pero evítalo — hace el código difícil de seguir. Prefiere funciones y condiciones claras.
While y Do-While
// while
$n = 0;
while ($n < 5) {
echo $n++;
}
// do-while (se ejecuta al menos 1 vez)
do {
$input = readline("Nombre: ");
} while (empty($input));
// Bucle infinito con break
$i = 0;
while (true) {
if ($i >= 10) break;
echo $i++;
}
// continue (saltar iteración)
for ($i = 0; $i < 10; $i++) {
if ($i % 2 === 0) continue;
echo $i; // solo impares
}while repite mientras la condición sea verdadera. do-while se ejecuta al menos una vez. break sale del bucle. continue salta a la siguiente iteración. ¡Cuidado con los bucles infinitos!
declare y Ticks
// strict_types: fuerza tipos exactos
declare(strict_types=1);
function suma(int $a, int $b): int {
return $a + $b;
}
suma(1, "2"); // ¡TypeError!
// ticks: se ejecuta cada N statements
declare(ticks=1);
register_tick_function(function () {
echo "Tick executado\n";
});
// encoding (PHP 8.3+)
// declare(encoding='UTF-8');declare(strict_types=1) debe ser la 1ª línea del archivo. Fuerza type hints exactos (sin coerción). ticks ejecuta un callback cada N statements (útil para profiling). strict_types es por archivo, no global.
Funções
Definir Funciones
// Función básica
function saudar(string $nombre): string {
return "Hola, $nombre!";
}
// Parámetro con valor por defecto
function potencia(int $base, int $exp = 2): int {
return $base ** $exp;
}
// Sin retorno (void)
function log(string $msg): void {
file_put_contents('app.log', $msg . PHP_EOL, FILE_APPEND);
}
// Retorno nullable
function encontrar(int $id): ?string {
return $id === 1 ? "Ana" : null;
}Usa type hints en parámetros y retorno. ?string permite null. void indica sin retorno. Valores por defecto con = valor. Los nombres de funciones son case-insensitive.
Recursión y Memoización
// Factorial recursivo
function factorial(int $n): int {
return $n <= 1 ? 1 : $n * factorial($n - 1);
}
// Fibonacci con memoización
function fib(int $n, array &$cache = []): int {
if ($n <= 1) return $n;
return $cache[$n] ??= fib($n-1, $cache) + fib($n-2, $cache);
}
// Recursión con array
function achatar(array $arr): array {
$result = [];
foreach ($arr as $item) {
is_array($item)
? $result = [...$result, ...achatar($item)]
: $result[] = $item;
}
return $result;
}Recursión: función que se llama a sí misma. Siempre ten un caso base para parar. &$cache por referencia para memoización. ??= calcula y guarda solo si no existe.
Funciones Variables y Dinámicas
// Función variable
$fn = 'strtoupper';
echo $fn('hola'); // "HOLA"
// Nombre dinámico
$acción = $_GET['acción'] ?? 'listar';
$funciones = ['listar' => 'listarItems', 'crear' => 'crearItem'];
if (isset($funciones[$acción])) {
$funciones[$acción]();
}
// call_user_func
call_user_func('strtoupper', 'hola');
call_user_func_array('array_merge', [[1], [2]]);
// function_exists
if (function_exists('array_is_list')) {
// PHP 8.1+
}Funciones variables: guarda el nombre en una variable y llama con $fn(). call_user_func() llama por nombre. call_user_func_array() pasa args como array. function_exists() verifica disponibilidad. Útil para patrones de dispatch.
Arrow Functions y Closures
// Arrow function (PHP 7.4+)
$doble = fn($x) => $x * 2;
$suma = fn($a, $b) => $a + $b;
// Captura automática del ámbito
$fator = 3;
$multiplicar = fn($x) => $x * $fator;
// Closure tradicional
$contador = function() {
static $count = 0;
return ++$count;
};
// Closure con use
$prefijo = "Dr.";
$saudar = function($nombre) use ($prefijo) {
return "$prefijo $nombre";
};fn() => es una arrow function (captura variables automáticamente). function() use ($var) es una closure que importa variables del ámbito. static preserva el valor entre llamadas.
Type Hints Avanzados
// Union types (PHP 8+)
function formatear(int|float $num): string {
return number_format($num, 2);
}
// Nullable
function buscar(int $id): ?User {
return $id ? new User() : null;
}
// mixed (PHP 8+)
function dump(mixed $val): void {
var_dump($val);
}
// never (PHP 8.1+)
function abort(string $msg): never {
throw new RuntimeException($msg);
}
// strict mode
declare(strict_types=1);
function suma(int $a, int $b): int {
return $a + $b;
}
// suma(1, "2"); // TypeError!declare(strict_types=1) desactiva la coerción automática. Los union types int|float aceptan múltiples tipos. ?Type permite null. mixed acepta cualquier. never indica que nunca retorna.
Named Arguments (PHP 8) 8.0
// Antes: el orden importa
array_slice($arr, 0, 5, true);
// Ahora: por nombre
array_slice(
array: $arr,
offset: 0,
length: 5,
preserve_keys: true
);
// Saltar parámetros con default
htmlspecialchars($input, double_encode: false);
// Combinar positional + named
str_pad('5', length: 3, pad_string: '0');Los named arguments (PHP 8+) permiten pasar parámetros por nombre en cualquier orden. Ideal para funciones con muchos parámetros opcionales. Puedes saltar defaults. Después de un named, todos deben ser named.
Parámetros Variables y Spread
// Variadic (...args)
function suma(...$nums): int {
return array_sum($nums);
}
suma(1, 2, 3); // 6
// Spread para llamar
$args = [1, 2, 3];
suma(...$args); // 6
// Named arguments (PHP 8+)
function criarUser(string $nombre, int $edad = 0, bool $activo = true) {}
criarUser(nombre: "Ana", activo: false);
// First-class callable (PHP 8.1+)
$strlen = strlen(...);
echo $strlen("hola"); // 3...$args acepta un numero variable de argumentos. ... (spread) expande arrays. Los named arguments nombre: "Ana" (PHP 8+) permiten saltar parámetros. strlen(...) crea un callable de primera clase.
Funciones Anónimas y Closures
// Closure con estado
function contador(): Closure {
$count = 0;
return function() use (&$count) {
return ++$count;
};
}
$next = contador();
$next(); // 1
$next(); // 2
// IIFE (Immediately Invoked)
$result = (function() {
$x = calcular();
$y = procesar($x);
return $y;
})();
// Closure::fromCallable (PHP 7.1+)
$upper = Closure::fromCallable('strtoupper');
echo $upper('hola'); // "HOLA"Las closures capturan variables con use. &$var captura por referencia (modifica el original). IIFE se ejecuta inmediatamente. Closure::fromCallable() convierte funciones en closures. Útil para callbacks y factories.
Union Types y Mixed (PHP 8) 8.0
// Union types
function formatear(int|float $num): string {
return number_format($num, 2);
}
// Nullable shorthand
function encontrar(int $id): ?User {
// retorna User o null
}
// mixed (cualquier tipo)
function debug(mixed $valor): void {
var_dump($valor);
}
// never (nunca retorna)
function abortar(string $msg): never {
throw new RuntimeException($msg);
}Los union types int|float aceptan múltiples tipos. ?Type es atajo para Type|null. mixed acepta cualquier tipo. never (PHP 8.1) indica que la función nunca retorna (lanza excepción o termina).
Callbacks y Higher-Order
// Función como argumento
function aplicar(callable $fn, array $arr): array {
return array_map($fn, $arr);
}
$result = aplicar(fn($x) => $x * 2, [1, 2, 3]);
// Retornar función
function multiplicador(int $fator): Closure {
return fn($x) => $x * $fator;
}
$triple = multiplicador(3);
echo $triple(5); // 15
// call_user_func
call_user_func('strtoupper', 'hola'); // "HOLA"
call_user_func_array('max', [1, 5, 3]); // 5callable es el type hint para funciones/callbacks. array_map(), array_filter(), usort() aceptan callbacks. call_user_func() llama dinámicamente. Funciones que retornan funciones = higher-order.
Sintaxis First-Class Callable (PHP 8.1)
// Antes: Closure::fromCallable o string
$fn = Closure::fromCallable('strlen');
$fn2 = 'strtoupper';
// PHP 8.1+: sintaxis (...)
$len = strlen(...);
echo $len('Hola'); // 3
$upper = strtoupper(...);
$nums = array_map(abs(...), [-1, -2, 3]);
// [1, 2, 3]
// Métodos de objetos
$obj = new MyClass();
$method = $obj->doSomething(...);
$method('arg');
// Métodos estáticos
$static = MyClass::create(...);La sintaxis función(...) (PHP 8.1+) crea un Closure a partir de cualquier callable. Sustituye a Closure::fromCallable(). Funciona con funciones, métodos de objeto ($obj->method(...)) y métodos estáticos (Class::method(...)).
Fibers y Generators 8.0
// Generator (lazy evaluation)
function infinite_range(): Generator {
$i = 0;
while (true) {
yield $i++;
}
}
foreach (infinite_range() as $n) {
if ($n > 5) break;
echo $n; // 0, 1, 2, 3, 4, 5
}
// Fiber (PHP 8.1+)
$fiber = new Fiber(function (): void {
$valor = Fiber::suspend('pausa');
echo "Recibí: $valor";
});
$resultado = $fiber->start(); // 'pausa'
$fiber->resume('datos'); // "Recibí: datos"Generator con yield es lazy — solo calcula cuando se pide. Ideal para secuencias infinitas y archivos grandes. Fiber (PHP 8.1+) permite pausar/reanudar ejecución. Base de sistemas asíncronos.
OOP
Clases y Objetos
class User {
public string $nombre;
private int $edad;
protected float $saldo = 0;
public function __construct(
string $nombre,
int $edad
) {
$this->nombre = $nombre;
$this->edad = $edad;
}
public function saudar(): string {
return "Hola, {$this->nombre}";
}
}
$user = new User("Ana", 30);
echo $user->saudar();public accesible desde fuera. private solo dentro de la clase. protected clase + hijas. $this referencia la instancia. __construct() es el constructor. new crea instancias.
Constructor Promotion (PHP 8)
// Antes (PHP 7)
class UserOld {
private string $nombre;
private int $edad;
public function __construct(string $nombre, int $edad) {
$this->nombre = $nombre;
$this->edad = $edad;
}
}
// Ahora (PHP 8+)
class User {
public function __construct(
private string $nombre,
private int $edad,
private bool $activo = true,
) {}
public function getName(): string {
return $this->nombre;
}
}Constructor Promotion (PHP 8+) declara y asigna propiedades directo en el constructor. private string $nombre en el parámetro crea la propiedad automáticamente. Reduce boilerplate significativamente.
Namespaces y Autoload
// src/Models/User.php
namespace App\Models;
class User {
public function __construct(
private string $nombre
) {}
}
// index.php
require 'vendor/autoload.php';
use App\Models\User;
use App\Services\{EmailService, LogService};
$user = new User("Ana");
// Alias
use App\Models\User as AppUser;
use function App\Helpers\formatear;
use const App\Config\MAX_SIZE;namespace organiza clases en paquetes. use importa clases. autoload (Composer) carga clases automáticamente. use function y use const importan funciones/constantes. PSR-4: 1 clase = 1 archivo.
Final, Visibility y Constants
class Base {
final public function fixo(): void {}
// No puede ser overridden
}
final class Inmutable {
// No puede ser extendida
}
class Config {
// Visibility en constantes (PHP 7.1+)
public const VERSION = '2.0';
protected const DB_HOST = 'localhost';
private const SECRET = 'abc123';
// Constante con expresión (PHP 8.3+)
// const TOTAL = self::PRECIO * 1.23;
}
// Acceso
echo Config::VERSION; // "2.0"final impide el override de método o la extensión de clase. Las constantes de clase pueden tener visibilidad (public, protected, private) desde PHP 7.1. Accede con Clase::CONST. PHP 8.3 permite final en constantes.
Intersection Types (PHP 8.1) 8.0
interface HasNome {
public function getName(): string;
}
interface HasEmail {
public function getEmail(): string;
}
class Contacto implements HasNome, HasEmail {
public function getName(): string { return 'Ana'; }
public function getEmail(): string { return 'ana@mail.com'; }
}
// Intersection: debe implementar AMBAS
function notificar(HasNome&HasEmail $c): void {
echo "{$c->getName()}: {$c->getEmail()}";
}
notificar(new Contacto()); // OK
// DNF types (PHP 8.2): combinación de union + intersection
function procesar((HasNome&HasEmail)|null $c): void {}Los intersection types A&B exigen que el valor implemente TODAS las interfaces. A diferencia de union A|B (una u otra). PHP 8.2 introduce DNF types: (A&B)|C combina intersection dentro de union con paréntesis.
Herencia y Polimorfismo
class Animal {
public function __construct(
protected string $nombre
) {}
public function sonido(): string {
return "...";
}
}
class Perro extends Animal {
public function sonido(): string {
return "{$this->nombre}: Au au!";
}
}
class Gato extends Animal {
public function sonido(): string {
return "{$this->nombre}: Miau!";
}
}
// Polimorfismo
$animales = [new Perro("Rex"), new Gato("Felix")];
foreach ($animales as $a) {
echo $a->sonido(); // llama al método correcto
}extends crea herencia. La clase hija hereda métodos y puede sobrescribirlos (override). parent::método() llama al método de la clase padre. Polimorfismo: misma interfaz, comportamientos diferentes.
Enums (PHP 8.1+)
enum Status: string {
case Activo = 'activo';
case Inactivo = 'inactivo';
case Pendiente = 'pendiente';
public function label(): string {
return match($this) {
self::Activo => '✅ Activo',
self::Inactivo => '❌ Inactivo',
self::Pendiente => '⏳ Pendiente',
};
}
}
$status = Status::Activo;
echo $status->value; // "activo"
echo $status->label(); // "✅ Activo"
// Comparación
$status === Status::Activo; // trueenum (PHP 8.1+) define conjuntos fijos de valores. : string o : int para backed enums. ->value da el valor. Pueden tener métodos. Ideales para estados, tipos y categorías fijas.
Dependency Injection
// Sin DI (acoplado)
class OrderService {
private $db = new Database(); // ¡malo!
}
// Con DI (desacoplado)
class OrderService {
public function __construct(
private DatabaseInterface $db,
private LoggerInterface $logger,
) {}
public function crear(Order $order): void {
$this->db->save($order);
$this->logger->info("Order criada");
}
}
// Inyección manual
$service = new OrderService(
new MySQLDatabase(),
new FileLogger()
);Dependency Injection: recibe dependencias en el constructor en vez de crearlas dentro. Programa para interfaces, no implementaciones. Facilita pruebas (mocks) y cambio de implementaciones. Base de SOLID.
Readonly Properties (PHP 8.1+) 8.0
class Point {
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
}
$p = new Point(1.5, 2.5);
echo $p->x; // 1.5
$p->x = 3.0; // ¡Error! Cannot modify readonly
// Readonly class (PHP 8.2+)
readonly class Config {
public function __construct(
public string $host,
public int $port,
) {}
}readonly (PHP 8.1+) permite escribir solo una vez. Tras la asignación, no puede ser modificado. Ideal para Value Objects y DTOs. readonly class (PHP 8.2+) lo aplica a todas las propiedades.
Interfaces y Abstract
interface Pagable {
public function pagar(float $valor): bool;
public function getMetodo(): string;
}
abstract class PagamentoBase implements Pagable {
abstract protected function validar(): bool;
public function pagar(float $valor): bool {
if (!$this->validar()) return false;
// procesar...
return true;
}
}
class CartaoCredito extends PagamentoBase {
protected function validar(): bool {
return true;
}
public function getMetodo(): string {
return "Tarjeta";
}
}interface define un contrato (solo firmas). implements cumple el contrato. abstract class puede tener métodos con cuerpo. abstract method obliga implementación en la hija. Una clase puede implementar múltiples interfaces.
Static y Constants
class Config {
const MAX_UPLOAD = 10_485_760; // 10MB
private static array $cache = [];
public static function get(string $key): mixed {
return self::$cache[$key] ?? null;
}
public static function set(string $key, mixed $val): void {
self::$cache[$key] = $val;
}
}
// Uso sin instancia
Config::set('debug', true);
Config::get('debug'); // true
Config::MAX_UPLOAD; // 10485760static pertenece a la clase (no a la instancia). Se accede con Clase::método(). self:: referencia la propia clase. const define constantes de clase. Útil para singletons, factories y utilitarios.
Late Static Binding
class Animal {
public static function crear(): static {
return new static(); // clase llamada
}
public function tipo(): string {
return static::class; // clase real
}
}
class Perro extends Animal {}
$perro = Perro::crear();
echo get_class($perro); // "Perro" (no "Animal")
echo $perro->tipo(); // "Perro"
// self vs static
// self:: → clase donde está definido
// static:: → clase que fue llamadastatic:: (Late Static Binding) se refiere a la clase llamada, no a la definida. self:: se refiere siempre a la clase donde está el método. new static() crea una instancia de la clase hija. Esencial para factories y herencia.
Attributes (PHP 8) 8.0
// Definir attribute
#[Attribute(Attribute::TARGET_CLASS)]
class Route {
public function __construct(
public string $path,
public string $method = 'GET',
) {}
}
// Usar
#[Route('/users', method: 'POST')]
class UserController {
public function store() {}
}
// Leer vía Reflection
$ref = new ReflectionClass(UserController::class);
$attrs = $ref->getAttributes(Route::class);
$route = $attrs[0]->newInstance();
echo $route->path; // "/users"#[Attribute] (PHP 8+) sustituye las annotations en comentarios. Metadatos nativos legibles vía Reflection. Usados en frameworks (Symfony, Laravel) para rutas, validación, inyección de dependencias.
Traits
trait Loggable {
public function log(string $msg): void {
echo "[" . static::class . "] $msg\n";
}
}
trait Timestampable {
public ?DateTime $criadoEm = null;
public function marcarCriado(): void {
$this->criadoEm = new DateTime();
}
}
class Post {
use Loggable, Timestampable;
public function __construct(
public string $título
) {
$this->marcarCriado();
$this->log("Post creado");
}
}trait comparte código entre clases sin herencia. use TraitName dentro de la clase. Múltiples traits con use A, B. Resuelve la "herencia múltiple" de PHP. static::class da el nombre de la clase actual.
Magic Methods
class Product {
private array $data = [];
public function __get(string $key): mixed {
return $this->data[$key] ?? null;
}
public function __set(string $key, mixed $val): void {
$this->data[$key] = $val;
}
public function __isset(string $key): bool {
return isset($this->data[$key]);
}
public function __toString(): string {
return json_encode($this->data);
}
public function __invoke(): void {
echo "Chamado como funcion";
}
}
$p = new Product();
$p->nombre = "Teclado"; // __set
echo $p->nombre; // __get
echo $p; // __toStringLos magic methods empiezan con __. __get()/__set() para propiedades dinámicas. __toString() cuando se usa como string. __invoke() cuando se llama como función. __construct(), __destruct() para el ciclo de vida.
Object Cloning y __clone
class Carrito {
public array $ítems = [];
public function __construct(
public string $cliente
) {}
public function __clone(): void {
// Deep copy de los ítems
$this->ítems = array_map(
fn($i) => clone $i,
$this->ítems
);
}
}
$original = new Carrito('Ana');
$original->ítems = [new Item('Libro')];
$copia = clone $original;
$copia->cliente = 'Bruno';
// $original->cliente sigue siendo "Ana"clone crea una copia superficial del objeto. __clone() se llama después de la copia — úsalo para deep copy de objetos internos. Sin clone, la asignación $b = $a copia la referencia, no el objeto.
Enums Avanzados (PHP 8.1) 8.0
// Backed enum (con valores)
enum Status: string {
case Activo = 'activo';
case Inactivo = 'inactivo';
case Suspendido = 'suspendido';
public function label(): string {
return match($this) {
self::Activo => 'Activo ✅',
self::Inactivo => 'Inactivo ❌',
self::Suspendido => 'Suspendido ⏸',
};
}
public function podeEditar(): bool {
return $this === self::Activo;
}
}
$status = Status::from('activo');
$status->value; // 'activo'
$status->name; // 'Activo'
$status->label(); // 'Activo ✅'
Status::tryFrom('x'); // null (sin error)Los enums con : string o : int son backed (tienen valor). from() convierte desde un valor (lanza error si es inválido). tryFrom() retorna null. Pueden tener métodos y usar match. Sustituyen las constantes mágicas.
Ficheiros e JSON
Leer y Escribir Archivos
// Leer todo de una vez
$contenido = file_get_contents('datos.txt');
// Escribir (sobrescribe)
file_put_contents('output.txt', 'Hola!');
// Append (añadir al final)
file_put_contents('log.txt', "linea\n", FILE_APPEND);
// Leer como array de líneas
$lineas = file('datos.txt', FILE_IGNORE_NEW_LINES);
// Verificar existencia
if (file_exists('config.ini')) {
$ini = parse_ini_file('config.ini');
}file_get_contents() lee todo. file_put_contents() escribe. FILE_APPEND añade sin sobrescribir. file() retorna un array de líneas. file_exists() verifica antes de leer.
SplFileObject y Streams
// Leer línea a línea (eficiente)
$file = new SplFileObject('grande.csv');
$file->setFlags(SplFileObject::READ_CSV);
foreach ($file as $linea) {
[$nombre, $email] = $linea;
echo "$nombre: $email\n";
}
// fopen/fread/fwrite
$fp = fopen('datos.txt', 'r');
while (!feof($fp)) {
$linea = fgets($fp);
}
fclose($fp);
// php://input (raw body)
$body = file_get_contents('php://input');SplFileObject es orientado a objetos y eficiente para archivos grandes. fopen()/fgets() para lectura manual. php://input lee el body raw de las requests. Siempre cierra con fclose().
File Locking y Atomicidad
// Lock exclusivo (escritura)
$fp = fopen('contador.txt', 'c+');
if (flock($fp, LOCK_EX)) {
$val = (int) stream_get_contents($fp);
ftruncate($fp, 0);
rewind($fp);
fwrite($fp, (string)($val + 1));
fflush($fp);
flock($fp, LOCK_UN);
}
fclose($fp);
// Lock compartido (lectura)
$fp = fopen('datos.txt', 'r');
flock($fp, LOCK_SH);
$contenido = stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);
// Escritura atómica con rename
file_put_contents('tmp_' . $f, $datos);
rename('tmp_' . $f, $f); // atómicoflock() evita condiciones de carrera. LOCK_EX = exclusivo (escritura), LOCK_SH = compartido (lectura). Siempre haz fflush() antes de LOCK_UN. rename() es atómico en el mismo filesystem — ideal para escritura segura.
JSON
// Array → JSON
$datos = ['nombre' => 'Ana', 'edad' => 30];
$json = json_encode($datos, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// JSON → Array
$arr = json_decode($json, true); // true = array
$obj = json_decode($json); // stdClass
// Tratar errores
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception(json_last_error_msg());
}
// PHP 8+: named + flags
json_encode($datos, flags: JSON_THROW_ON_ERROR);json_encode() convierte a JSON. json_decode($json, true) a array asociativo. JSON_PRETTY_PRINT formatea. JSON_UNESCAPED_UNICODE preserva las tildes. JSON_THROW_ON_ERROR lanza excepción en error.
CSV y Archivos Grandes
// Escribir CSV
$fp = fopen('datos.csv', 'w');
fputcsv($fp, ['Nombre', 'Email']);
fputcsv($fp, ['Ana', 'ana@mail.com']);
fclose($fp);
// Leer CSV
$fp = fopen('datos.csv', 'r');
$headers = fgetcsv($fp);
while (($row = fgetcsv($fp)) !== false) {
$datos[] = array_combine($headers, $row);
}
fclose($fp);
// Archivo grande (línea a línea)
$handle = fopen('gigante.log', 'r');
while (($linea = fgets($handle)) !== false) {
procesar($linea); // memoria constante
}
fclose($handle);fputcsv() escribe CSV. fgetcsv() lee línea a línea. array_combine() mapea headers con valores. Para archivos grandes, usa fgets() en bucle — memoria constante independiente del tamaño.
Manipulación de Archivos
// Crear carpeta
mkdir('uploads/2024', 0755, true); // recursivo
// Copiar, mover, borrar
copy('a.txt', 'b.txt');
rename('b.txt', 'c.txt'); // mueve/renombra
unlink('c.txt'); // borra archivo
rmdir('carpeta-vacia'); // borra carpeta
// Información
filesize('datos.txt'); // bytes
filemtime('datos.txt'); // última modificación
pathinfo('foto.jpg', PATHINFO_EXTENSION); // "jpg"
// Listar carpeta
$ficheros = glob('uploads/*.jpg');
$todos = scandir('.');mkdir() con true crea recursivamente. rename() mueve/renombra. unlink() borra archivos. glob() encuentra por patrón. pathinfo() extrae extensión, nombre, carpeta.
Pathinfo y Manipulación de Paths
$path = '/var/www/uploads/foto.jpg';
pathinfo($path, PATHINFO_DIRNAME); // /var/www/uploads
pathinfo($path, PATHINFO_BASENAME); // foto.jpg
pathinfo($path, PATHINFO_FILENAME); // foto
pathinfo($path, PATHINFO_EXTENSION); // jpg
// Construir paths
$destino = __DIR__ . '/uploads/' . $filename;
// Normalizar
realpath('./../fichero.txt');
basename('/camino/para/file.php'); // file.php
dirname('/camino/para/file.php'); // /camino/para
// Temp
$tmp = tempnam(sys_get_temp_dir(), 'prefix_');pathinfo() extrae partes del camino. realpath() resuelve . y ... basename() da el nombre del archivo. dirname() da la carpeta. tempnam() crea un archivo temporal único.
Upload de Archivos
// HTML: <form enctype="multipart/form-data">
// <input type="file" name="foto">
if (isset($_FILES['foto'])) {
$f = $_FILES['foto'];
if ($f['error'] !== UPLOAD_ERR_OK) {
die("Error: " . $f['error']);
}
$ext = pathinfo($f['name'], PATHINFO_EXTENSION);
$permitidas = ['jpg', 'png', 'webp'];
if (!in_array(strtolower($ext), $permitidas)) {
die("Tipo no permitido");
}
$destino = 'uploads/' . uniqid() . '.' . $ext;
move_uploaded_file($f['tmp_name'], $destino);
}$_FILES contiene los datos del upload. move_uploaded_file() lo mueve al destino final. Siempre valida: error, extensión, tamaño. Usa uniqid() para nombres únicos. Nunca confíes en el nombre original.
DirectoryIterator y Glob
// glob: encontrar archivos por patrón
$fotos = glob('uploads/*.{jpg,png,webp}', GLOB_BRACE);
$phpFiles = glob(__DIR__ . '/*.php');
// DirectoryIterator (OOP)
$dir = new DirectoryIterator('/var/www');
foreach ($dir as $item) {
if ($item->isDot()) continue;
echo $item->getFilename();
echo $item->getSize();
echo $item->isDir() ? ' [DIR]' : ' [FILE]';
}
// RecursiveDirectoryIterator
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/src')
);
foreach ($it as $file) {
if ($file->getExtension() === 'php') {
echo $file->getPathname();
}
}glob() encuentra archivos por patrón (soporta *, ?, {a,b}). DirectoryIterator es la forma OOP de listar directorios. RecursiveDirectoryIterator recorre subdirectorios recursivamente.
Base de Dados e Segurança
PDO — Conexión
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=app;charset=utf8mb4',
'root',
'contrasena',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
} catch (PDOException $e) {
die("Error na conexión: " . $e->getMessage());
}PDO soporta MySQL, PostgreSQL, SQLite, etc. ERRMODE_EXCEPTION lanza excepciones en errores. FETCH_ASSOC retorna arrays asociativos. charset=utf8mb4 para Unicode completo.
Sesiones y Cookies
// Iniciar sesión
session_start();
// Guardar/leer datos
$_SESSION['user_id'] = 42;
$_SESSION['nombre'] = 'Ana';
$userId = $_SESSION['user_id'] ?? null;
// Destruir sesión
session_destroy();
$_SESSION = [];
// Cookies
setcookie('token', $valor, [
'expires' => time() + 86400,
'path' => '/',
'httponly' => true,
'secure' => true,
'samesite' => 'Strict',
]);
$token = $_COOKIE['token'] ?? null;session_start() inicia la sesión (antes de cualquier output). $_SESSION guarda datos en el servidor. setcookie() crea cookies en el cliente. httponly impide acceso vía JS. secure solo en HTTPS.
SQLite y PDO Avanzado
// SQLite (sin servidor)
$pdo = new PDO('sqlite:datos.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Último ID insertado
$pdo->prepare("INSERT INTO users (nombre) VALUES (?)")
->execute(['Ana']);
$id = $pdo->lastInsertId();
// Múltiples filas
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch modes
$stmt->fetch(PDO::FETCH_OBJ); // stdClass
$stmt->fetch(PDO::FETCH_COLUMN); // 1ª columna
$stmt->fetchAll(PDO::FETCH_KEY_PAIR); // id => nombre
// quote (evitar — use prepared)
$pdo->quote("O'Brien"); // "O''Brien"sqlite: crea una BD en archivo (sin servidor). lastInsertId() retorna el ID generado. fetchAll() retorna todas las filas. FETCH_KEY_PAIR crea un mapa clave→valor. quote() escapa pero prefiera siempre prepared statements.
Prepared Statements
// INSERT seguro
$stmt = $pdo->prepare(
"INSERT INTO users (nombre, email) VALUES (:nombre, :email)"
);
$stmt->execute([
':nombre' => $nombre,
':email' => $email,
]);
$id = $pdo->lastInsertId();
// SELECT con parámetros
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();
// Múltiples resultados
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();prepare() + execute() previene SQL Injection. Usa placeholders :nombre o ?. fetch() retorna 1 fila. fetchAll() retorna todas. ¡NUNCA concatene variables en la query!
PDO — UPDATE y DELETE
// UPDATE
$stmt = $pdo->prepare(
"UPDATE users SET nombre = :nombre, email = :email WHERE id = :id"
);
$stmt->execute([
':nombre' => $novoNome,
':email' => $novoEmail,
':id' => $userId,
]);
echo $stmt->rowCount(); // filas afectadas
// DELETE
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$userId]);
// Transacción
$pdo->beginTransaction();
try {
$pdo->exec("UPDATE contas SET saldo = saldo - 100 WHERE id = 1");
$pdo->exec("UPDATE contas SET saldo = saldo + 100 WHERE id = 2");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
}rowCount() retorna las filas afectadas. beginTransaction()/commit()/rollBack() para transacciones atómicas. Si algo falla, rollBack() deshace todo. Esencial para operaciones que dependen unas de otras.
Seguridad — Hash y Escape
// Hash de passwords
$hash = password_hash($contrasena, PASSWORD_DEFAULT);
// Verificar password
if (password_verify($input, $hash)) {
echo "Contrasena correta";
}
// Rehash si es necesario
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$novoHash = password_hash($contrasena, PASSWORD_DEFAULT);
}
// Escapar output (XSS)
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// CSRF token
$token = bin2hex(random_bytes(32));
$_SESSION['csrf'] = $token;password_hash() crea un hash seguro (bcrypt). password_verify() compara. htmlspecialchars() previene XSS. random_bytes() genera tokens seguros. ¡Nunca uses md5() o sha1() para passwords!
Headers de Seguridad
// Headers HTTP de seguridad
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Security-Policy: default-src \'self\'');
header('Strict-Transport-Security: max-age=31536000');
header('Referrer-Policy: strict-origin-when-cross-origin');
// Cache
header('Cache-Control: no-store, no-cache');
header('Pragma: no-cache');
// Content type
header('Content-Type: application/json; charset=utf-8');
echo json_encode($datos);Headers de seguridad: X-Frame-Options impide clickjacking. CSP controla los recursos permitidos. HSTS fuerza HTTPS. X-Content-Type-Options impide MIME sniffing. Siempre define el Content-Type correcto.
Sanitización y Validación
// filter_var
$email = filter_var($input, FILTER_SANITIZE_EMAIL);
$url = filter_var($input, FILTER_SANITIZE_URL);
$int = filter_var($input, FILTER_VALIDATE_INT);
$emailOk = filter_var($input, FILTER_VALIDATE_EMAIL);
// filter_input (directo del request)
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
// Validación manual
$nombre = trim($_POST['nombre'] ?? '');
if (mb_strlen($nombre) < 2 || mb_strlen($nombre) > 100) {
$errores[] = "Nombre inválido";
}
// htmlspecialchars en el output
echo htmlspecialchars($nombre, ENT_QUOTES, 'UTF-8');filter_var() sanitiza/valida datos. FILTER_VALIDATE_* retorna false si es inválido. FILTER_SANITIZE_* limpia caracteres. Siempre valida en el servidor — nunca confíes en el cliente.
Transacciones PDO
try {
$pdo->beginTransaction();
$pdo->prepare("UPDATE contas SET saldo = saldo - ? WHERE id = ?")
->execute([100, $deOrigem]);
$pdo->prepare("UPDATE contas SET saldo = saldo + ? WHERE id = ?")
->execute([100, $paraDestino]);
$pdo->prepare("INSERT INTO transferencias (de, para, valor) VALUES (?, ?, ?)")
->execute([$deOrigem, $paraDestino, 100]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}beginTransaction() inicia la transacción. commit() confirma todo. rollBack() deshace todo si algo falla. Esencial para operaciones que dependen unas de otras (transferencias, pedidos). Siempre usa try/catch con rollBack().
Números e Datas
Funciones Matemáticas
abs(-5); // 5 max(1, 5, 3); // 5 min(1, 5, 3); // 1 round(3.7); // 4 floor(3.7); // 3 ceil(3.2); // 4 round(3.14159, 2); // 3.14 rand(1, 100); // aleatorio 1-100 random_int(1, 100); // criptográficamente seguro pow(2, 10); // 1024 sqrt(144); // 12 fmod(10, 3); // 1 (resto float) intdiv(10, 3); // 3 (división entera)
round() redondea. floor() hacia abajo, ceil() hacia arriba. random_int() es criptográficamente seguro (prefiéralo a rand()). intdiv() hace división entera. fmod() es el resto para floats.
Funciones date() y time()
// date() — formateo rápido
echo date('d/m/Y'); // 25/12/2024
echo date('H:i:s'); // 14:30:00
echo date('Y-m-d H:i:s'); // 2024-12-25 14:30:00
echo date('l'); // Wednesday
echo date('F'); // December
// time() — timestamp actual
$ts = time();
echo date('d/m/Y', $ts + 86400); // mañana
// strtotime() — string → timestamp
$ts = strtotime('+1 week');
$ts = strtotime('next monday');
$ts = strtotime('2024-12-25');
// Zona horaria
date_default_timezone_set('Europe/Lisbon');date() formatea timestamps. time() da el timestamp actual. strtotime() convierte strings en timestamps. date_default_timezone_set() define la zona horaria. Formatos: d (día), m (mes), Y (año), H (hora).
Conversión de Bases y Hex
// Decimal → otras bases
decbin(42); // "101010"
decoct(42); // "52"
dechex(255); // "ff"
// Otras bases → decimal
bindec('101010'); // 42
octdec('52'); // 42
hexdec('ff'); // 255
// base_convert (cualquier base 2-36)
base_convert('ff', 16, 2); // "11111111"
base_convert('255', 10, 16); // "ff"
// Literales en código
$bin = 0b1010; // 10
$oct = 0755; // 493
$hex = 0xFF; // 255
// Colores
$color = dechex(16711680); // "ff0000"decbin(), decoct(), dechex() convierten decimal a binario/octal/hex. bindec(), octdec(), hexdec() hacen lo inverso. base_convert() convierte entre bases 2-36. Literales: 0b binario, 0x hex, 0 octal.
Formateo de Números
// number_format
number_format(1234567.891, 2, ',', '.');
// "1.234.567,89"
// sprintf
sprintf("%.2f", 19.9); // "19.90"
sprintf("%05d", 42); // "00042"
sprintf("%x", 255); // "ff" (hex)
sprintf("%b", 10); // "1010" (binario)
// Conversiones
intval("42px"); // 42
floatval("3.14"); // 3.14
(int) "42"; // 42
(float) "3.14"; // 3.14
// Verificar
is_numeric("42"); // true
is_nan(acos(2)); // true
is_infinite(INF); // truenumber_format() formatea con separadores. sprintf() para formateo avanzado. intval()/floatval() convierten. is_numeric() verifica si es numero o string numérica.
Carbon-like sin Dependencias
// Helpers de fecha útiles
function hoy(): string {
return date('Y-m-d');
}
function mañana(): string {
return date('Y-m-d', strtotime('+1 day'));
}
function diasEntre(string $de, string $ate): int {
$d1 = new DateTime($de);
$d2 = new DateTime($ate);
return (int) $d1->diff($d2)->format('%r%a');
}
function formatarPt(DateTime $dt): string {
$meses = ['', 'enero', 'febrero', 'marzo',
'abril', 'mayo', 'junio', 'julio',
'agosto', 'septiembre', 'octubre',
'noviembre', 'diciembre'];
return $dt->format('d') . ' de ' .
$meses[(int)$dt->format('n')] .
' de ' . $dt->format('Y');
}Funciones utilitarias de fecha sin dependencias externas. strtotime() para cálculos rápidos. DateTime::diff() para diferencias precisas. %r%a da días con signo. Para proyectos reales, considera la biblioteca Carbon.
DateTime
// Fecha actual
$ahora = new DateTime();
echo $ahora->format('d/m/Y H:i:s');
// Fecha específica
$natal = new DateTime('2024-12-25');
$custom = DateTime::createFromFormat('d/m/Y', '25/12/2024');
// Modificar
$ahora->modify('+1 week');
$ahora->add(new DateInterval('P1M')); // +1 mes
$ahora->sub(new DateInterval('P2D')); // -2 días
// Comparar
if ($natal > $ahora) {
echo "Natal ainda no chegou";
}
// Timestamp
$ts = $ahora->getTimestamp();
$dt = (new DateTime())->setTimestamp($ts);DateTime es la forma moderna de trabajar con fechas. format() formatea la salida. modify() acepta strings como +1 week. DateInterval para intervalos precisos. P1M = 1 mes, P2D = 2 días.
Números Aleatorios y UUID
// Aleatorios seguros
random_int(1, 100); // int seguro
bin2hex(random_bytes(16)); // hex 32 chars
base64_encode(random_bytes(32));
// UUID v4 (sin dependencias)
function uuid4(): string {
$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s',
str_split(bin2hex($data), 4));
}
echo uuid4();
// "550e8400-e29b-41d4-a716-446655440000"random_int() y random_bytes() son criptográficamente seguros. rand() y mt_rand() NO son seguros para tokens. UUID v4 puede generarse con random_bytes() y manipulación de bits.
Diferencia entre Fechas
$inicio = new DateTime('2024-01-01');
$fin = new DateTime('2024-12-25');
$diff = $inicio->diff($fin);
echo $diff->days; // 359 (total días)
echo $diff->m; // 11 (meses)
echo $diff->format('%m meses e %d días');
// Edad
$nascimento = new DateTime('1994-05-15');
$edad = $nascimento->diff(new DateTime())->y;
// Carbon-like (sin dependencias)
function diasAte(DateTime $alvo): int {
return (int) (new DateTime())->diff($alvo)->format('%r%a');
}diff() retorna un DateInterval. ->days da el total de días. ->y, ->m, ->d dan años, meses, días. %r en el format muestra signo negativo si la fecha ya pasó.
BCMath — Precisión Arbitraria
// Float tiene problemas de precisión
0.1 + 0.2 === 0.3; // false!
// BCMath: strings con precisión exacta
bcadd('0.1', '0.2', 10); // '0.3'
bcsub('10', '3.5', 2); // '6.50'
bcmul('1.5', '2.3', 4); // '3.4500'
bcdiv('10', '3', 20); // '3.33333333333333333333'
bcmod('10', '3'); // '1'
bccomp('1.001', '1.0', 3); // 1 (mayor)
bcpow('2', '10'); // '1024'
bcsqrt('144', 2); // '12.00'
// Escala global
bcscale(4);
echo bcdiv('1', '3'); // '0.3333'BCMath hace aritmética con precisión arbitraria vía strings. Esencial para dinero y cálculos científicos. bcscale() define decimales globales. Nunca uses float para valores monetarios — usa bcadd(), bcmul(), etc.
Erros e Debug
Try / Catch / Finally
try {
$resultado = 10 / 0;
} catch (DivisionByZeroError $e) {
echo "Error: " . $e->getMessage();
} catch (Exception $e) {
echo "Genérico: " . $e->getMessage();
} finally {
echo "Sempre executa";
}
// Múltiples tipos
try {
// ...
} catch (InvalidArgumentException | RuntimeException $e) {
echo $e->getMessage();
}
// PHP 8: non-capturing catch
try {
// ...
} catch (Exception) {
echo "Algo falhou";
}try ejecuta código arriesgado. catch captura excepciones específicas. finally se ejecuta siempre. PHP 8: catch (Exception) sin variable. Ordena del más específico al más genérico.
Logging y Error Reporting
// error_log a archivo
error_log("Error: " . $e->getMessage(), 3, '/var/log/app.log');
// error_log con contexto
error_log(json_encode([
'level' => 'error',
'message' => $msg,
'context' => ['user_id' => $userId],
'time' => date('c'),
]));
// Configuración por entorno
// Desarrollo:
ini_set('display_errors', '1');
error_reporting(E_ALL);
// Producción:
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php-errors.log');error_log() escribe en el log del sistema o archivo. En desarrollo: display_errors = 1. En producción: display_errors = 0 + log_errors = 1. Nunca muestres errores al usuario en producción.
Lanzar Excepciones
// Lanzar excepción
function dividir(int $a, int $b): float {
if ($b === 0) {
throw new InvalidArgumentException(
"Divisor no pode ser zero"
);
}
return $a / $b;
}
// Excepción personalizada
class SaldoInsuficienteException extends Exception {
public function __construct(
private float $saldo,
private float $valor
) {
parent::__construct(
"Saldo {$this->saldo} < {$this->valor}"
);
}
}throw new Exception() lanza excepciones. Crea clases con extends Exception para errores específicos. Incluye mensajes claros. La excepción se propaga hasta encontrar un catch.
Excepciones con Contexto
// Encadenar excepciones (exception chaining)
try {
$datos = $api->fetch($url);
} catch (HttpException $e) {
throw new ServiceException(
"Fallo ao obtener datos da API",
previous: $e // PHP 8: named arg
);
}
// Excepción con datos extra
class ValidationException extends Exception {
public function __construct(
private array $errors,
string $message = "Validación falhou"
) {
parent::__construct($message);
}
public function getErrors(): array {
return $this->errors;
}
}Exception chaining: pasa la excepción original como previous. getPrevious() accede a la causa. Crea excepciones con datos extra (ej: $errors). Facilita el debugging y el logging estructurado.
Error Handling
// Handler global
set_exception_handler(function (Throwable $e) {
error_log($e->getMessage());
echo "Error interno. Tente novamente.";
});
// Error handler personalizado
set_error_handler(function ($severity, $message, $file, $line) {
throw new ErrorException($message, 0, $severity, $file, $line);
});
// Supresión (¡evitar!)
$result = @file_get_contents('inexistente.txt');
// error_reporting
error_reporting(E_ALL);
ini_set('display_errors', '0'); // producciónset_exception_handler() captura excepciones no tratadas. set_error_handler() convierte errores en excepciones. @ suprime errores (¡evítalo!). error_reporting(E_ALL) muestra todos. En producción: log, no mostrar.
Custom Exception Classes
class AppException extends Exception {
public function __construct(
string $message,
private int $httpCode = 500,
?Throwable $previous = null
) {
parent::__construct($message, 0, $previous);
}
public function getHttpCode(): int {
return $this->httpCode;
}
}
class NotFoundException extends AppException {
public function __construct(string $resource, int $id) {
parent::__construct(
"$resource #$id no encontrado",
httpCode: 404
);
}
}
throw new NotFoundException('User', 42);Crea jerarquías de excepciones con extends Exception. Añade contexto (HTTP code, resource, etc.). Facilita el catch selectivo y respuestas HTTP adecuadas. Patrón: una excepción base por módulo + específicas por caso.
Debug y Var Dump
// var_dump (tipo + valor)
var_dump([1, "a", null, true]);
// print_r (legible)
print_r($array);
// var_export (código PHP válido)
echo var_export($config, true);
// debug_backtrace
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
echo $trace[0]['file'] . ':' . $trace[0]['line'];
// error_log (escribe en el log)
error_log("Debug: " . print_r($datos, true));
// dd() (Laravel/Symfony)
// dd($variable); // dump + dievar_dump() muestra tipo y valor. print_r() es más legible para arrays. var_export() genera código PHP válido. debug_backtrace() muestra la call stack. error_log() escribe en el log sin output.
set_error_handler y Shutdown
// Convertir warnings en excepciones
set_error_handler(function (
int $severity,
string $message,
string $file,
int $line
): bool {
throw new ErrorException(
$message, 0, $severity, $file, $line
);
});
// Shutdown: captura errores fatales
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'],
[E_ERROR, E_PARSE, E_CORE_ERROR]
)) {
error_log(json_encode($error));
http_response_code(500);
}
});
// Restaurar handler
restore_error_handler();set_error_handler() convierte warnings/notices en excepciones. register_shutdown_function() se ejecuta al final — captura errores fatales vía error_get_last(). restore_error_handler() restaura el handler anterior.
Truques e Dicas
Operador Null Coalescing (??)
// Básico $nombre = $_GET['nombre'] ?? 'Visitante'; // Encadenamiento $val = $a ?? $b ?? $c ?? 'default'; // Null coalescing assignment (PHP 7.4+) $config['debug'] ??= false; // Solo asigna si no existe o es null // En arrays anidados $ciudad = $user['dirección']['ciudad'] ?? 'N/D'; // ¡Sin error aunque $user['dirección'] no exista! // Combinar con isset $items = $_SESSION['cart'] ?? [];
?? retorna el primer valor no null. No lanza error en claves inexistentes. ??= asigna solo si es null. Sustituye patrones como isset($x) ? $x : default. Encadénalo para múltiples fallbacks.
Operadores Bitwise y Ternarios
// Bitwise
$a & $b; // AND
$a | $b; // OR
$a ^ $b; // XOR
~$a; // NOT
$a << 2; // shift izquierda (×4)
$a >> 1; // shift derecha (÷2)
// Flags con bitwise
define('READ', 1); // 001
define('WRITE', 2); // 010
define('EXEC', 4); // 100
$perms = READ | WRITE; // 3 (011)
$perms & READ; // 1 (tiene READ)
$perms & EXEC; // 0 (no tiene EXEC)
// Ternario anidado (¡evitar!)
$x = $a ?: $b ?? $c; // confusoBitwise: & (AND), | (OR), ^ (XOR), ~ (NOT). << multiplica por 2, >> divide. Útil para flags/permisos. Combina con |, prueba con &.
Operador Null-Safe (?->)
// Sin null-safe: verificaciones en cadena
$city = null;
if ($user !== null) {
$addr = $user->getAddress();
if ($addr !== null) {
$city = $addr->getCity();
}
}
// Con null-safe (PHP 8+)
$city = $user?->getAddress()?->getCity();
// Si cualquier eslabón es null, retorna null
$nombre = $user?->perfil?->nombre ?? 'Anónimo';
// Funciona con métodos y propiedades
$len = $user?->getName()?->length();El operador ?-> (PHP 8+) retorna null si el objeto es null, sin error. Sustituye cadenas de if !== null. Combínalo con ?? para un default. Funciona con propiedades y métodos encadenados.
Spread y Unpacking
// Merge arrays
$a = [1, 2];
$b = [3, 4];
$c = [...$a, ...$b]; // [1, 2, 3, 4]
// Asociativo (PHP 8.1+)
$defaults = ['color' => 'azul', 'size' => 'M'];
$custom = [...$defaults, 'color' => 'rojo'];
// ['color' => 'rojo', 'size' => 'M']
// En llamadas de función
function suma($a, $b, $c) { return $a + $b + $c; }
$args = [1, 2, 3];
suma(...$args); // 6
// array_push con spread
$arr = [1, 2];
array_push($arr, ...[3, 4, 5]);Spread ... expande arrays. En arrays asociativos (PHP 8.1+), la última clave gana. Sustituye a array_merge() con sintaxis más limpia. Funciona en llamadas de función y array_push().
list() y Destructuring Avanzado
// list() clásico
list($a, $b) = [1, 2];
// Short syntax
[$x, $y] = [10, 20];
// En foreach
$users = [['Ana', 30], ['Ray', 25]];
foreach ($users as [$nombre, $edad]) {
echo "$nombre tem $edad";
}
// Ignorar elementos
[, , $terceiro] = [1, 2, 3, 4];
// Asociativo (PHP 7.1+)
['nombre' => $nombre, 'edad' => $edad] = $user;
// Swap
[$a, $b] = [$b, $a];list() o [] para destructuring. Funciona en foreach para arrays multidimensionales. Una coma sola ignora posiciones. Destructuring asociativo con ['clave' => $var]. Swap elegante: [$a, $b] = [$b, $a].
Short Ternary y Elvis Avanzado
// Elvis operator (?:)
$nombre = $_POST['nombre'] ?: 'Anónimo';
// Equivale a: $_POST['nombre'] ? $_POST['nombre'] : 'Anónimo'
// vs Null Coalescing (??)
$nombre = $_POST['nombre'] ?? 'Anónimo';
// ?? solo verifica null; ?: verifica falsy
// Diferencia práctica
$valor = 0;
$valor ?: 10; // 10 (0 es falsy)
$valor ?? 10; // 0 (0 no es null)
// Ternario en expresiones
$clase = $activo ? 'activo' : 'inactivo';
$msg = $errores ? implode(', ', $errores) : 'OK';
// Ternario anidado (¡evitar!)
// $x = $a ? 'a' : ($b ? 'b' : 'c');?: (Elvis) retorna el valor si es truthy, si no el default. ?? solo verifica null. Diferencia: 0 ?: 10 da 10, pero 0 ?? 10 da 0. Usa ?? para valores opcionales, ?: para truthy/falsy.
Trucos con Arrays
// Último elemento $último = end($arr); $último = $arr[array_key_last($arr)]; // Quitar duplicados $unico = array_unique([1, 2, 2, 3, 3]); // Achatar array $flat = array_merge(...[[1,2], [3,4]]); // o $flat = [...[1,2], ...[3,4]]; // array_combine (claves + valores) $keys = ['a', 'b', 'c']; $vals = [1, 2, 3]; $arr = array_combine($keys, $vals); // array_flip (intercambia clave ↔ valor) $arr = ['a' => 1, 'b' => 2]; $flip = array_flip($arr); // [1 => 'a', 2 => 'b']
array_key_last() da la última clave. array_unique() quita duplicados. array_combine() crea un array desde 2 arrays (claves + valores). array_flip() intercambia claves con valores. Spread ... achata arrays.
Operador @ y Supresión
// Supresión de errores (¡evitar!)
$valor = @$arr['clave_inexistente'];
$fp = @fopen('inexistente.txt', 'r');
// Alternativas mejores:
$valor = $arr['clave'] ?? null;
if (file_exists('fichero.txt')) {
$fp = fopen('fichero.txt', 'r');
}
// error_reporting(0) temporal
$old = error_reporting(0);
// ... código que puede dar warning ...
error_reporting($old);El operador @ suprime errores — ¡evítalo! Dificulta el debugging y esconde bugs. Prefiere ?? para claves inexistentes, file_exists() antes de abrir, o try/catch. PHP 8: @ no suprime fatal errors.
Nuevas Funciones PHP 8.x 8.0
// str_contains, starts_with, ends_with
str_contains('Hola Mundo', 'Mundo');
str_starts_with('Hola', 'Ol');
str_ends_with('prueba.php', '.php');
// array_is_list (PHP 8.1)
array_is_list([1, 2, 3]); // true
array_is_list(['a' => 1]); // false
// enum_exists (PHP 8.1)
enum_exists(Status::class);
// fdiv (división sin warning)
fdiv(1, 0); // INF (¡sin error!)
// get_debug_type (PHP 8)
get_debug_type(null); // "null"
get_debug_type([]); // "array"
get_debug_type(1.5); // "float"PHP 8+: str_contains(), str_starts_with(), str_ends_with(). PHP 8.1: array_is_list(), enum_exists(), fdiv(). PHP 8: get_debug_type() muestra el tipo de forma legible.
Strings — Trucos
// str_contains, starts_with, ends_with (PHP 8+)
str_contains('Hola Mundo', 'Mundo'); // true
str_starts_with('Hola', 'Ol'); // true
str_ends_with('fichero.php', '.php'); // true
// str_pad
str_pad('42', 5, '0', STR_PAD_LEFT); // "00042"
// strtr (múltiples sustituciones)
strtr('Hola Mundo', ['Hola' => 'Hi', 'Mundo' => 'World']);
// sscanf (extraer con formato)
sscanf('2024-12-25', '%d-%d-%d', $y, $m, $d);
// wordwrap
echo wordwrap($texto, 80, "\n");PHP 8+: str_contains(), str_starts_with(), str_ends_with() — más legibles que strpos(). str_pad() rellena. strtr() hace múltiples sustituciones de una vez. sscanf() extrae con formato.
Compact y Extract
// compact: variables → array
$nombre = 'Ana';
$edad = 30;
$email = 'ana@mail.com';
$user = compact('nombre', 'edad', 'email');
// ['nombre' => 'Ana', 'edad' => 30, ...]
// extract: array → variables (¡cuidado!)
$datos = ['título' => 'Post', 'autor' => 'Ana'];
extract($datos);
echo $título; // 'Post'
echo $autor; // 'Ana'
// extract con prefijo (más seguro)
extract($datos, EXTR_PREFIX_ALL, 'd');
echo $d_título; // 'Post'compact() crea un array a partir de variables. extract() hace lo inverso — ¡cuidado: sobrescribe variables existentes! Usa EXTR_PREFIX_ALL por seguridad. Prefiere destructuring con [...] a extract().
Novedades PHP 8.2 y 8.3 8.0
// PHP 8.2: readonly classes
readonly class Point {
public function __construct(
public float $x,
public float $y,
) {}
}
// PHP 8.2: constantes en traits
trait HasId {
public const TABLE = 'items';
}
// PHP 8.3: typed class constants
class Config {
final const int MAX = 100;
final const string APP = 'MyApp';
}
// PHP 8.3: json_validate()
json_validate('{"ok":true}'); // true
json_validate('{invalid}'); // false
// PHP 8.3: Randomizer
$rng = new Random\Randomizer();
$rng->getInt(1, 100);
$rng->shuffleArray([1, 2, 3]);PHP 8.2: readonly class, constantes en traits, DNF types. PHP 8.3: final y tipos en constantes, json_validate() sin decode, Random\Randomizer para aleatoriedad OOP, #[\Override] attribute.