7. Functions

When we use the same fragment of code on multiple occasions we should use functions. A tool to encapsulate and run the same code. Among its advantages we will see that it helps make files smaller in size and easier to maintain.

// Declare
function nombre_de_funcion(tipo_de_parametro $parametros): tipo_return
{
    ...
    return ...;
}

// Call
nombre_de_funcion($parametros);

In the example below we have a classic function, with modern syntax, where it is declared and executed followed by an echo to see the result. All it does is return a text when it is called.

/**
 * Polite function
 * @return {string}
 */
function saludar_ahora(): string
{
    return 'Hi, I am a function';
}
echo saludar_ahora();
// Hi, I am a function

The recipe for making a rich and well-founded function:

  • The first 4 lines are the comment format. A function, no matter how much of a hurry you are in, must be commented with the format from the example.
  • The word function is reserved. Then comes the name, which must be in lowercase with underscores instead of spaces. After that, some parentheses with the arguments. If it has none, they are left empty but always present. Then a colon. Finally the type of the resulting value. Below I have left you a table with accepted types.
  • Braces to wrap your code {}.
  • And inside, the reserved word return followed by the value to return.

Parameters

Our functions will be more interesting if we give them some parameters. By giving them variables we can always use the same code but with some variations.

/**
 * Trims a text to 10 letters and adds an ellipsis at the end
 * @param {string} $text - Text to process
 * @return {string}
 */
function resumen(string $text): string
{
    return substr($text, 0, 20) . '...';
}

echo resumen('Cuanto te vi me enamoré y tu sonreíste porque lo sabías');
echo resumen('La vida es una historia contada por un idiota, una historia llena de estruendo y furia, que nada significa');
// Cuanto te vi me enam...
// La vida es una histo...

If the types are not met, in PHP 5 it produces an error that stops execution. In contrast, in PHP 7, a TypeError exception is thrown but it continues.

Our input parameters can have a default value.

/**
 * Greets a person
 * @param {string} - Name
 * @return {string}
 */
function saludar(string $nombre = 'Anónimo'): string
{
    return 'Hello, person named ' . $nombre .'. From what I see your name is ' . strlen($nombre) . ' characters long.';
}
echo saludar();
// Hello, person named Anónimo. From what I see your name is 8 characters long.
echo saludar('Picasso');
// Hello, person named Picasso. From what I see your name is 7 characters long.

And, of course, we can add several parameters.

/**
 * Greets a person
 * @param {string} - Name
 * @param {string} - Profession
 * @return {string}
 */
function saludar(string $nombre = 'Anónimo', string $profesion = 'ninguna'): string
{
    return 'Hello, person named ' . $nombre .'. From what I see your name is ' . strlen($nombre) . ' characters long. By profession ' . $profesion . '.';
}
echo saludar();
// Hello, person named Anónimo. From what I see your name is 8 characters long. By profession ninguna.
echo saludar('Espartaco');
// Hello, person named Espartaco. From what I see your name is 9 characters long. By profession ninguna.
echo saludar('Picasso', 'pintor');
// Hello, person named Picasso. From what I see your name is 7 characters long. By profession pintor.

Accepted types

Type Description Minimum version
string The parameter must be a string. PHP 7.0.0
int The parameter must be an integer value. PHP 7.0.0
float The parameter must be a float number. PHP 7.0.0
bool The parameter must be a boolean value. PHP 7.0.0
array The parameter must be an array. PHP 5.1.0
callable The parameter must be a valid callable. PHP 5.4.0
self The parameter must be an instanceof the same class in which the method is defined. This can only be used in classes and instance methods. PHP 5.0.0
class/interface name The parameter must be an instanceof the given class or interface name. PHP 5.0.0

More information

Automatically, PHP will fix type incompatibilities.

function incrementar(int $num): int
{
    return $num + 1;
}

echo incrementar(4.5);
// 5

But if you want to be strict, you will have to disable this help. In other words, so that if it finds any type problem it shows an error and does not apply a magical solution.

declare(strict_types=1);

function incrementar(int $num): int
{
    return $num + 1;
}

echo incrementar(4.5);
// PHP Fatal error:  Uncaught TypeError: Argument 1 passed to incrementar() must be of the type int, float given

Best practices: Type Hints throughout your code

Now that you know the available types and how to use them, let's talk about when to use them. The short answer: always. Adding type hints to your functions is one of the best practices in modern PHP.

Why use type hints?

1. Early error detection: Type errors are detected immediately instead of producing mysterious bugs later on.

// Without type hints - the error can appear much later
function calcularDescuento($precio, $porcentaje) {
    return $precio - ($precio * $porcentaje / 100);
}

// If someone passes a string, PHP will try to convert it
echo calcularDescuento('mil', '20'); // Result: 0 (incorrect but does not raise an error)

// With type hints - immediate and clear error
function calcularDescuentoSeguro(float $precio, float $porcentaje): float {
    return $precio - ($precio * $porcentaje / 100);
}

echo calcularDescuentoSeguro('mil', '20'); // TypeError: must be float

2. Better autocompletion in your editor: Your IDE can suggest correct methods and properties because it knows the exact type.

3. Automatic documentation: The code explains itself without the need for additional comments.

// What does this function receive? What does it return? It is not clear
function procesar($datos) {
    // ...
}

// Here it is crystal clear
function procesarUsuario(array $datos, bool $enviarEmail = false): ?int {
    // Receives an array and an optional boolean
    // Returns an int (probably an ID) or null if it fails
}

4. Bug prevention: If you change the type a function returns, PHP will warn you about all the places where it is used incorrectly.

Example: Function without type hints vs with type hints

Let's look at a typical function from a real application:

// ❌ Code without type hints (old, unclear, error-prone)
function crearPost($titulo, $contenido, $autor, $publicado = true) {
    // Is $titulo a string? Is $autor an object? An ID?
    // Is $publicado a bool or an int (0/1)?
    // We do not know without reading all the code

    $post = [
        'titulo' => $titulo,
        'contenido' => $contenido,
        'autor_id' => $autor,
        'publicado' => $publicado,
        'fecha' => date('Y-m-d H:i:s')
    ];

    // Insert into database...
    return true;
}

Now with type hints:

// ✅ Code with type hints (modern, clear, safe)
function crearPost(
    string $titulo,
    string $contenido,
    int $autorId,
    bool $publicado = true
): array {
    // Now it is crystal clear:
    // - titulo and contenido are strings
    // - autorId is an integer (author's ID)
    // - publicado is a boolean
    // - returns an array

    $post = [
        'titulo' => $titulo,
        'contenido' => $contenido,
        'autor_id' => $autorId,
        'publicado' => $publicado,
        'fecha' => date('Y-m-d H:i:s')
    ];

    // Insert into database...
    return $post;
}

// Clear usage and perfect autocompletion
$nuevoPost = crearPost(
    titulo: 'My first post',
    contenido: 'This is the content...',
    autorId: 42,
    publicado: true
);

When NOT to use type hints (exceptional cases)

There are situations where you cannot or should not use type hints:

1. Functions that intentionally accept multiple types:

// This function accepts int, float or string
function formatearNumero(int|float|string $numero): string {
    return number_format((float)$numero, 2);
}

echo formatearNumero(1234.5);  // "1,234.50"
echo formatearNumero('1234.5'); // "1,234.50"

2. Arrays with complex structure: PHP does not allow you to specify the internal structure of arrays.

// You cannot specify that it is an array of Usuario objects
function procesarUsuarios(array $usuarios): void {
    // You would have to validate manually or use a comment
    foreach ($usuarios as $usuario) {
        // ...
    }
}

Pro tip: In modern PHP, use declare(strict_types=1); at the beginning of each file. This forces the types to be exact and prevents silent automatic conversions that can hide bugs.

<?php
declare(strict_types=1);

// Now all the type hints in this file are strict
function sumar(int $a, int $b): int {
    return $a + $b;
}

sumar(5, 3);    // ✅ Correct
sumar(5.5, 3);  // ❌ TypeError

Summary

Simple rule: Add type hints to all the parameters and returns of your functions. It will save you countless hours of debugging and make your code much more professional and maintainable.

Return with alternative types

Starting from PHP version 7.1 we have the possibility of indicating whether a return returns a specific type or a null. For this you will only have to add a question mark at its beginning.

function nombre(): ?string
{

}

In the following example we can return a string or a null. Depending on whether the winner is among the top 3 or not.

/**
 * Method that indicates the type of metal a medal should have based on the result
 * @param {int} $posicion - Position
 * @return {string|null} - Type of metal
 */
function tipoDeMedalla(int $posicion): ?string
{
    switch ($posicion) {
        case 1:
            return 'Oro';
        case 2:
            return 'Plata';
        case 3:
            return 'Bronce';
        default:
            return null;
    }
}

echo tipoDeMedalla(2);
// Plata

echo tipoDeMedalla(34);
// null

Perhaps this feature was added because of how practical it is when doing testing.

And from PHP 8 the possibilities are further enriched since it is possible to indicate 2 different types.

function nombre(): int|string
{

}

It can help us in cases such as, for example, giving an alternative or fallback result.

/**
 * Method that doubles a positive number
 * @param {int} $numero - Number to double
 * @return {float|string} - Result or help message
 */
function duplicarPositivo(float $numero): float|string
{
    if ($numero > 0) {
        return $numero * 2;
    } else {
        return 'You cannot use a negative number or zero';
    }
}

echo duplicarPositivo(12.1);
// 24.2

echo duplicarPositivo(-45);
// 'You cannot use a negative number or zero'

Named Arguments (PHP 8.0+)

Named arguments allow you to pass values to a function by specifying the parameter name, instead of relying on the order. This makes the code more readable and flexible.

// Traditional function
function crearUsuario(string $nombre, int $edad, string $email = '', bool $activo = true) {
    return "Usuario: $nombre, Edad: $edad, Email: $email, Activo: " . ($activo ? 'Sí' : 'No');
}

// Traditional way (strict order)
echo crearUsuario('Ana', 25, 'ana@example.com', true);

// With named arguments (any order)
echo crearUsuario(
    edad: 25,
    nombre: 'Ana',
    email: 'ana@example.com',
    activo: true
);

// You can easily omit optional arguments
echo crearUsuario(
    nombre: 'Bob',
    edad: 30
    // email and activo use default values
);

Main advantages:

  1. Readability: It is clear which value corresponds to each parameter
  2. Flexibility: The order of the arguments does not matter
  3. Optional arguments: You can skip intermediate parameters

Practical example with setcookie():

// Before (hard to read)
setcookie('token', $value, time() + 3600, '/', '', true, true);

// With named arguments (much clearer)
setcookie(
    name: 'token',
    value: $value,
    expires_or_options: time() + 3600,
    path: '/',
    secure: true,
    httponly: true
);

You can mix positional and named arguments:

function mostrarInfo(string $nombre, int $edad, string $ciudad = 'Madrid') {
    return "$nombre tiene $edad años y vive en $ciudad";
}

// Mix positional and named (positional first)
echo mostrarInfo('Carlos', 28, ciudad: 'Barcelona');

Anonymous

Anonymous functions are closed and can be declared without any name. They are mandatory when we have to pass a function as a parameter of another.

function () {
    return 'I am anonymous';
}

In the following example we increment each number of the array by 1.

$numeros = [10, 20, 30, 40];
$numerosIncrementados = array_map(function ($numero) {
    return $numero + 1;
}, $numeros);

var_dump($numerosIncrementados);

/*
array(4) {
  [0]=>
  int(11)
  [1]=>
  int(21)
  [2]=>
  int(31)
  [3]=>
  int(41)
}
*/

Using external variables

If you are going to use variables that are present in your code, you can enrich the content of the function using use.

$tienda = 'pescadería';

function () use ($tienda) {
    return "Estoy en la $tienda";
}

Arrow Functions (PHP 7.4+)

Arrow functions are a more concise syntax for anonymous functions, introduced in PHP 7.4. Their biggest advantage is that they automatically capture the variables of the parent scope without needing to use use.

// Traditional anonymous function
$numeros = [1, 2, 3, 4];
$factor = 10;
$multiplicados = array_map(function ($n) use ($factor) {
    return $n * $factor;
}, $numeros);

// Arrow function (more concise)
$multiplicados = array_map(fn($n) => $n * $factor, $numeros);
// [10, 20, 30, 40]

Important characteristics:

  • They are declared with fn instead of function
  • They use => to separate the parameters from the body
  • They can only have one expression (which is returned automatically)
  • They automatically capture variables from the parent scope
  • They do not need an explicit return

Practical examples:

$precios = [100, 200, 300];
$iva = 0.21;

// Calculate price with VAT
$preciosConIVA = array_map(fn($precio) => $precio * (1 + $iva), $precios);
// [121, 242, 363]

// Filter even numbers
$numeros = [1, 2, 3, 4, 5, 6];
$pares = array_filter($numeros, fn($n) => $n % 2 === 0);
// [2, 4, 6]

// Convert to uppercase
$nombres = ['ana', 'bob', 'carlos'];
$mayusculas = array_map(fn($nombre) => strtoupper($nombre), $nombres);
// ['ANA', 'BOB', 'CARLOS']

Functional Paradigm

The essential functions for iterating and managing an array are: array_walk, array_filter, array_map and array_reduce.

array_walk (Iterate)

Goes through an array, similar to a foreach.

array_walk({array}, {función});

In this example we are going to print all the cities.

<?php

// Dictionary
$apartamentos = [
    [
        'precio/noche' => 40,
        'ciudad' => 'Valencia',
        'wifi' => True,
        'pagina web' => 'https://hotel.com'
    ],
    [
        'precio/noche' => 87,
        'ciudad' => 'Calpe',
        'wifi' => True,
        'pagina web' => 'https://calpe.com'
    ],
    [
        'precio/noche' => 67,
        'ciudad' => 'Valencia',
        'wifi' => False,
        'pagina web' => 'https://denia.com'
    ],
    [
        'precio/noche' => 105,
        'ciudad' => 'Benidorm',
        'wifi' => False,
        'pagina web' => 'https://benidorm.com'
    ]
];

array_walk($apartamentos, function ($apartamento, $posicion) {
    echo $apartamento['ciudad'] . PHP_EOL;
});

/*
Valencia
Calpe
Valencia
Benidorm
*/

array_filter (filter)

We get a smaller array from another one.

array_filter({array}, {función});

In this example we will filter $apartamentos to keep the ones that are in Valencia.

<?php

// Dictionary
$apartamentos = [
    [
        'precio/noche' => 40,
        'ciudad' => 'Valencia',
        'wifi' => True,
        'pagina web' => 'https://hotel.com'
    ],
    [
        'precio/noche' => 87,
        'ciudad' => 'Calpe',
        'wifi' => True,
        'pagina web' => 'https://calpe.com'
    ],
    [
        'precio/noche' => 67,
        'ciudad' => 'Valencia',
        'wifi' => False,
        'pagina web' => 'https://denia.com'
    ],
    [
        'precio/noche' => 105,
        'ciudad' => 'Benidorm',
        'wifi' => False,
        'pagina web' => 'https://benidorm.com'
    ]
];


$todosLosApartamentosValencia = array_filter($apartamentos, function ($apartamento) {
    return $apartamento['ciudad'] === 'Valencia';
});

var_dump($todosLosApartamentosValencia);

/*
array(2) {
  [0]=>
  array(4) {
    ["precio/noche"]=>
    int(40)
    ["ciudad"]=>
    string(8) "Valencia"
    ["wifi"]=>
    bool(true)
    ["pagina web"]=>
    string(17) "https://hotel.com"
  }
  [2]=>
  array(4) {
    ["precio/noche"]=>
    int(67)
    ["ciudad"]=>
    string(8) "Valencia"
    ["wifi"]=>
    bool(false)
    ["pagina web"]=>
    string(17) "https://denia.com"
  }
}
*/

array_map (modify)

Transforms the content of an array but keeps the number of elements.

array_map({función}, {array});

In this example we are going to reduce the price per night by 1.

<?php

// Dictionary
$apartamentos = [
    [
        'precio/noche' => 40,
        'ciudad' => 'Valencia',
        'wifi' => True,
        'pagina web' => 'https://hotel.com'
    ],
    [
        'precio/noche' => 87,
        'ciudad' => 'Calpe',
        'wifi' => True,
        'pagina web' => 'https://calpe.com'
    ],
    [
        'precio/noche' => 67,
        'ciudad' => 'Valencia',
        'wifi' => False,
        'pagina web' => 'https://denia.com'
    ],
    [
        'precio/noche' => 105,
        'ciudad' => 'Benidorm',
        'wifi' => False,
        'pagina web' => 'https://benidorm.com'
    ]
];

$apartamentosMasBaratos = array_map(function ($apartamento) {
    return array_merge($apartamento, ['precio/noche' => $apartamento['precio/noche'] - 1]);
}, $apartamentos);

var_dump($apartamentosMasBaratos);
/*
array(4) {
  [0]=>
  array(4) {
    ["precio/noche"]=>
    int(39)
    ["ciudad"]=>
    string(8) "Valencia"
    ["wifi"]=>
    bool(true)
    ["pagina web"]=>
    string(17) "https://hotel.com"
  }
  [1]=>
  array(4) {
    ["precio/noche"]=>
    int(86)
    ["ciudad"]=>
    string(5) "Calpe"
    ["wifi"]=>
    bool(true)
    ["pagina web"]=>
    string(17) "https://calpe.com"
  }
  [2]=>
  array(4) {
    ["precio/noche"]=>
    int(66)
    ["ciudad"]=>
    string(8) "Valencia"
    ["wifi"]=>
    bool(false)
    ["pagina web"]=>
    string(17) "https://denia.com"
  }
  [3]=>
  array(4) {
    ["precio/noche"]=>
    int(104)
    ["ciudad"]=>
    string(8) "Benidorm"
    ["wifi"]=>
    bool(false)
    ["pagina web"]=>
    string(20) "https://benidorm.com"
  }
}
*/

array_reduce (calculate)

Obtains a result from an array.

array_reduce({array}, {función}, {inicial});

In this example we are going to calculate what the average price per night is.

<?php

// Dictionary
$apartamentos = [
    [
        'precio/noche' => 40,
        'ciudad' => 'Valencia',
        'wifi' => True,
        'pagina web' => 'https://hotel.com'
    ],
    [
        'precio/noche' => 87,
        'ciudad' => 'Calpe',
        'wifi' => True,
        'pagina web' => 'https://calpe.com'
    ],
    [
        'precio/noche' => 67,
        'ciudad' => 'Valencia',
        'wifi' => False,
        'pagina web' => 'https://denia.com'
    ],
    [
        'precio/noche' => 105,
        'ciudad' => 'Benidorm',
        'wifi' => False,
        'pagina web' => 'https://benidorm.com'
    ]
];

$media = array_reduce($apartamentos, function ($acumulador, $apartamento) {
    return $apartamento['precio/noche'] + $acumulador;
}, 0) / count($apartamentos);

echo $media;
// 74.75
Activity 1
  • Build a form that asks for the following information: nickname, age and profile image.
  • When submitted, show the information in a format similar to Twitter or a social network. The image must be present.
Activity 2
  • Create a form to upload a product to a store: serial number, name, price and image.
  • Create a login page before entering the products: name, password 1 and password 2.
  • Both passwords must match, as well as the name and password with some variables we have stored.
Activity 3

Create a form to upload an image. When it is uploaded you must:

  • Resize it to a width of 400 pixels.
  • Show it below the form.

Pro:

  • Convert it to black and white.
  • Create a crop of the image so that it is square without deforming it (400x400).

This work is under a Attribution-NonCommercial-NoDerivatives 4.0 International license.

Desafíos de programación atemporales y multiparadigmáticos

Desafíos de programación atemporales y multiparadigmáticos

Te encuentras ante un librillo de actividades, divididas en 2 niveles de dificultad. Te enfrentarás a los casos más comunes que te puedes encontrar en pruebas técnicas o aprender conceptos elementales de programación.

Buy the book

Will you buy me a coffee?

This is how I keep writing without ads or paywalls.

Comments

There are no comments yet.