Tema
Funciones
Las funciones te permiten empaquetar código con un nombre y reutilizarlo. En esta lección aprenderás a crear funciones con tipos, valores por defecto, parámetros con nombre, funciones variádicas, closures y funciones flecha: todo lo que necesitas para escribir PHP moderno y ordenado.
Concepto
Una función es como una receta: tiene un nombre ("hacer café"), ingredientes de entrada (los parámetros: agua, café) y un resultado (el valor de retorno: una taza de café). Una vez escrita, la usas las veces que quieras sin volver a explicar los pasos.
Beneficios:
- No te repites (principio DRY: Don't Repeat Yourself).
- Si hay un bug, lo corriges en un solo lugar.
- El código se lee como una historia:
calcularTotal(),enviarCorreo(),validarEmail().
Ejemplo básico
php
<?php
declare(strict_types=1);
// Definición
function saludar(string $nombre): string
{
return "Hola, $nombre";
}
// Llamada
echo saludar('Marta'); // Hola, MartaAnatomía:
functionpalabra clave.saludarnombre (encamelCase, idealmente un verbo).string $nombreparámetro con su tipo.: stringtipo de retorno.returndevuelve el valor y termina la función.
Tipado
PHP 8 permite tipar parámetros y retornos con mucha precisión:
php
<?php
declare(strict_types=1);
function calcularIva(float $monto, float $tasa = 0.16): float
{
return round($monto * $tasa, 2);
}
// ?string = puede ser string o null (tipo "nullable")
function obtenerApodo(?string $apodo): string
{
return $apodo ?? 'Sin apodo';
}
// Tipos unión: acepta int O float
function formatearPrecio(int|float $precio): string
{
return '$' . number_format($precio, 2);
}
// void: la función no devuelve nada
function registrarLog(string $mensaje): void
{
file_put_contents(__DIR__ . '/app.log', date('c') . " $mensaje\n", FILE_APPEND);
}
// never: la función nunca termina normalmente (redirige o lanza excepción)
function redirigir(string $url): never
{
header("Location: $url");
exit;
}
// mixed: cualquier tipo (úsalo poco)
function depurar(mixed $valor): void
{
echo '<pre>' . print_r($valor, true) . '</pre>';
}Tipos disponibles: int, float, string, bool, array, callable, iterable, object, mixed, void, never, null, true, false, nombres de clases y uniones (int|string).
Valores por defecto y parámetros con nombre
php
<?php
declare(strict_types=1);
function crearBoton(
string $texto,
string $color = 'azul',
string $tamano = 'mediano',
bool $deshabilitado = false
): string {
$attr = $deshabilitado ? ' disabled' : '';
return "<button class=\"btn btn-$color btn-$tamano\"$attr>$texto</button>";
}
echo crearBoton('Guardar'); // usa todos los valores por defecto
echo crearBoton('Borrar', 'rojo');
// Parámetros con nombre (PHP 8): te saltas los que no te interesan
echo crearBoton('Enviar', deshabilitado: true);
echo crearBoton(texto: 'Ver más', tamano: 'chico');Buena práctica
Los parámetros con valor por defecto van al final. Y cuando una función tiene muchos booleanos, llámala con parámetros con nombre: crearBoton('Enviar', deshabilitado: true) se entiende; crearBoton('Enviar', 'azul', 'mediano', true) no.
Funciones variádicas y spread
... recibe cualquier cantidad de argumentos como un array:
php
<?php
declare(strict_types=1);
function sumar(int|float ...$numeros): int|float
{
return array_sum($numeros);
}
echo sumar(1, 2, 3); // 6
echo sumar(10, 20, 30, 40); // 100
// Spread: "desempaqueta" un array como argumentos
$valores = [5, 5, 5];
echo sumar(...$valores); // 15Ámbito (scope)
Las variables dentro de una función no ven las de fuera, y viceversa.
php
<?php
$iva = 0.16;
function precioConIva(float $precio): float
{
// echo $iva; // Warning: Undefined variable $iva
return $precio * 1.16;
}Existe la palabra global, pero evítala: hace el código difícil de seguir y de probar. Pasa los datos como parámetros o usa constantes.
php
<?php
const IVA = 0.16; // Las constantes sí son visibles en todas partes
function precioConIva(float $precio, float $tasa = IVA): float
{
return $precio * (1 + $tasa);
}Variables estáticas
Conservan su valor entre llamadas:
php
<?php
function siguienteId(): int
{
static $id = 0; // Se inicializa sólo la primera vez
return ++$id;
}
echo siguienteId(); // 1
echo siguienteId(); // 2
echo siguienteId(); // 3Paso por valor y por referencia
php
<?php
function agregarIva(float $precio): float
{
$precio *= 1.16; // Modifica la copia local
return $precio;
}
function agregarIvaRef(float &$precio): void
{
$precio *= 1.16; // Modifica la variable original
}
$p = 100;
agregarIva($p);
echo $p; // 100 (no cambió)
agregarIvaRef($p);
echo $p; // 116 (cambió)Prefiere devolver valores en lugar de modificar por referencia: es más predecible.
Funciones anónimas (closures)
Funciones sin nombre que se guardan en variables o se pasan como argumentos.
php
<?php
$saludar = function (string $nombre): string {
return "Hola, $nombre";
};
echo $saludar('Iván');
// Para usar variables de fuera, se capturan con use
$descuento = 0.2;
$aplicarDescuento = function (float $precio) use ($descuento): float {
return $precio * (1 - $descuento);
};
echo $aplicarDescuento(500); // 400Funciones flecha fn
Versión corta para funciones de una sola expresión. Capturan automáticamente las variables de fuera (por valor).
php
<?php
$descuento = 0.2;
$aplicarDescuento = fn(float $precio): float => $precio * (1 - $descuento);
echo $aplicarDescuento(500); // 400
// Brillan con funciones de arrays
$precios = [100, 250, 80];
$conIva = array_map(fn($p) => $p * 1.16, $precios);
$caros = array_filter($precios, fn($p) => $p > 90);
print_r($conIva); // [116, 290, 92.8]
print_r($caros); // [0 => 100, 1 => 250]Callables y funciones de primera clase
php
<?php
// Pasar el nombre de una función como string
$nombres = ['ana', 'luis'];
print_r(array_map('ucfirst', $nombres)); // ['Ana', 'Luis']
// Sintaxis de primera clase (PHP 8.1): más segura que un string
$mayusculas = strtoupper(...);
echo $mayusculas('hola'); // HOLA
// Una función que recibe otra función
function aplicarATodos(array $items, callable $fn): array
{
$resultado = [];
foreach ($items as $item) {
$resultado[] = $fn($item);
}
return $resultado;
}
print_r(aplicarATodos([1, 2, 3], fn($n) => $n ** 2)); // [1, 4, 9]Ejemplo real: utilidades de un proyecto
Es habitual tener un archivo helpers.php con funciones reutilizables:
php
<?php
declare(strict_types=1);
/**
* Escapa texto para mostrarlo en HTML de forma segura (evita XSS).
*/
function e(?string $texto): string
{
return htmlspecialchars($texto ?? '', ENT_QUOTES, 'UTF-8');
}
/**
* Formatea un monto como moneda mexicana.
*/
function dinero(int|float $monto, string $moneda = 'MXN'): string
{
return '$' . number_format($monto, 2, '.', ',') . " $moneda";
}
/**
* Convierte un título en un "slug" para URLs: "Hola Mundo!" → "hola-mundo"
*/
function slug(string $texto): string
{
$texto = mb_strtolower($texto, 'UTF-8');
// Quita acentos: á → a, ñ → n
$texto = strtr($texto, ['á'=>'a','é'=>'e','í'=>'i','ó'=>'o','ú'=>'u','ü'=>'u','ñ'=>'n']);
$texto = preg_replace('/[^a-z0-9]+/', '-', $texto); // Todo lo raro → guion
return trim($texto, '-');
}
/**
* Redirige y detiene el script.
*/
function redirigir(string $url): never
{
header("Location: $url");
exit;
}
echo e('<b>Hola</b>'); // <b>Hola</b>
echo dinero(1234.5); // $1,234.50 MXN
echo slug('¡Café con Leche y Ñandú!'); // cafe-con-leche-y-nanduError común
Declarar dos veces la misma función (por ejemplo, al hacer include del mismo archivo dos veces) produce Fatal error: Cannot redeclare. Usa require_once para archivos de funciones (lo verás en Include y plantillas).
Error común
Olvidar return. Si la función no devuelve nada, el resultado es null y tu echo no muestra nada. Declarar el tipo de retorno (: string) hace que PHP te avise.
Ejercicios
- Crea
esPalindromo(string $texto): boolque ignore mayúsculas y espacios ('Anita lava la tina'→true). - Crea
promedio(float ...$numeros): floatque devuelva 0 si no recibe números. - Usando
array_mapy una función flecha, convierte['ana', 'LUIS', 'sOfÍa']en['Ana', 'Luis', 'Sofía'](pista:mb_convert_case).
Ver solución
php
<?php
declare(strict_types=1);
function esPalindromo(string $texto): bool
{
$limpio = mb_strtolower(str_replace(' ', '', $texto), 'UTF-8');
// mb_str_split + array_reverse invierte respetando acentos
$invertido = implode('', array_reverse(mb_str_split($limpio)));
return $limpio === $invertido;
}
var_dump(esPalindromo('Anita lava la tina')); // true
var_dump(esPalindromo('Hola')); // falsephp
<?php
declare(strict_types=1);
function promedio(float ...$numeros): float
{
if (count($numeros) === 0) {
return 0;
}
return array_sum($numeros) / count($numeros);
}
echo promedio(8, 9, 10); // 9
echo promedio(); // 0php
<?php
$nombres = ['ana', 'LUIS', 'sOfÍa'];
$bonitos = array_map(fn($n) => mb_convert_case($n, MB_CASE_TITLE, 'UTF-8'), $nombres);
print_r($bonitos); // ['Ana', 'Luis', 'Sofía']Resumen
- Define funciones con
function nombre(tipo $param): tipoRetorno. - Tipa siempre parámetros y retornos; usa
?tipo, unionesint|float,voidynever. - Valores por defecto al final; llama con parámetros con nombre cuando aporte claridad.
...$argsrecibe varios argumentos;...$arraylos desempaqueta.- Las funciones no ven variables externas: pásalas como parámetros.
- Closures con
use; funciones flechafn() =>para expresiones cortas. - Agrupa tus utilidades en un
helpers.php.