Curso de PHP: Nivel Básico – Clase 10. Funciones

Hemos llegado a la clase número 10 del Curso de PHP: Nivel Básico. Esta será la última clase de este curso de nivel inicial. En esta oportunidad vamos a concentrarnos en las funciones, un elemento imprescindible en cualquier lenguaje, base de la programación estructurada.

Como siempre, todo el código que aparece en este artículo, lo pueden encontrar aquí. La salida de ese código la encuentran aquí.

Introducción a las funciones

Ya hemos hablado de funciones e incluso las hemos utilizado en clases y ejercicios anteriores de este mismo curso. Pero ahora llega el momento de formalizar nuestro conocimiento sobre ellas y empezar a construir funciones propias.

Pues bien, ¿qué es una función? Una función es un bloque de código que realiza una o varias tareas específicas. Tiene un nombre con el cual la podemos identificar para llamarla y también puede recibir una serie de valores llamados parámetros o argumentos. Además, posee la posibilidad de devolver un único valor. Las funciones pueden ser llamadas todas las veces que sean necesarias y su principal ventaja es que nos permiten reutilizar código, para no tener que volver a escribirlo.

De esta forma, por ejemplo, podríamos tener una función que dado un número (parámetro), determine si es primo o no devolviendo true si lo es y false en caso contrario (valor de retorno). O podríamos construir una función que dada una serie de números calcule el promedio de todos. O una que reciba un array con strings y devuelva un nuevo string con una lista ordenada en HTML donde cada elemento de la lista se corresponda con cada ítem del array.

Los parámetros son siempre opcionales. Podemos hacer funciones que no reciban nada. Y a diferencia de otros lenguajes, el valor de retorno también lo es. Especial cuidado quienes estén acostumbrados a programar por ejemplo en C, donde siempre debe especificarse un tipo de retorno (si no se devuelve nada, debe especificarse void). Lo único que debe tenerse en cuenta es que cuando se construye una función y no se devuelve nada, PHP devolverá el tipo null.

Pero antes de involucrarnos con la sintaxis y el código específicamente, me parece muy importante explicar por qué debemos utilizar funciones al programar de forma estructurada.

Ventajas de la reutilización del código

El principio básico de la buena programación estructurada es crear módulos que realicen una tarea determinada y que puedan ser llamados una y otra vez para no tener que repetir código. Del libro Desarrollo Web con PHP y MySQL de Luke Welling y Laura Thompson, podemos citar las siguientes ventajas de la reutilización del código. (Interpretamos el término módulo como una librería o conjunto de funciones)

  • Costes: Desde un enfoque comercial, crear, optimizar y probar  una pieza de código lleva tiempo, lo que se traduce en horas de trabajo y dinero invertido. La idea aquí es tener módulos pequeños que realicen tareas de forma precisa para poder reutilizarlos y así ahorrar. Incluso puede llegar a ser conveniente comprar un módulo ya hecho, antes que comenzar a desarrollar desde cero.
  • Fiabilidad: Un módulo ya creado y que se encuentra funcionando es más confiable que uno nuevo sin probar. Por lo tanto es conveniente reutilizarlo antes que invertir tiempo en reescribirlo.
  • Uniformidad: Al construir un módulo es necesario que las interfaces externas (todas las partes que se comunican ya sea con el usuario o con otros sistemas) sean uniformes. Esto significa que debe tener características en común con otras interfaces teniendo todo un modo de uso similar. Lograr uniformidad en un módulo requiere también tiempo y esfuerzo. Un módulo ya hecho nos lo garantiza.

Funciones propias de PHP

Como ya hemos visto en clases anteriores PHP trae una gran variedad de funciones para realizar tareas puntuales. Por ejemplo las de manejo de strings: strlen, strpos y substr, entre otras; o las que sirven para operar arrays como in_array, array_flip, array_reverse y otras más que hemos conocido.

A pesar de ser propias del lenguaje, se basan en las mismas reglas que cualquier otro tipo de función. Reciben parámetros opcionalmente, realizan una tarea y pueden o no devolver algún valor.

Ninguna función que construyamos puede tener el mismo nombre que otra ya existente, incluyendo las que trae PHP.

Funciones del usuario

Ahora sí, nos ponemos prácticos y vamos a comenzar a definir funciones propias que podremos utilizar en nuestros programas.

Sintaxis

Para definir una función propia, escribimos en un archivo de texto la palabra clave function seguida del nombre de la función. Luego abrimos paréntesis y si tenemos parámetros los ingresamos allí separados por comas. Se abren llaves, allí comienza el bloque de código donde podemos hacer lo que necesitemos. Para finalizar y de forma opcional, con la palabra clave return es posible devolver un valor.

La función debe ir definida en un espacio aparte del documento. PHP no pondrá objeciones si definimos una función dentro de un bloque de código condicional o incluso dentro de un bucle. Sin embargo esto no es lo correcto y si lo estamos haciendo entonces hay algo que está mal con nuestro algoritmo y debemos revisarlo. Las funciones pueden estar en un mismo archivo junto a otro código, pero siempre deben estar aparte, fuera de cualquier nfivel de anidación.

El nombre de la función, a diferencia de los nombres de las variables no debe llevar el símbolo $ delante. Tampoco puede contener espacios y debe ser distinto de los nombres de otras funciones ya predefinidas, contando también las que trae PHP, como expliqué más arriba.

Los parámetros son variables comunes de PHP y el valor de retorno también puede ser una variable.

Veamos un ejemplo de una función.

La función saludar imprime un mensaje de bienvenida. No recibe ningún parámetro y tampoco devuelve nada. Debajo podemos ver la forma de llamarla: se escribe el nombre de la función, los paréntesis (vacíos en este caso porque no hay parámetros) y finalmente un punto y coma para terminar la sentencia. Esto hará que se ejecute la función. Esta acción de llamar a una función también se suele llamar “invocación”.

La salida del ejemplo anterior es la siguiente:

Salida

Hola a todo el mundo!

Ahora veamos otro ejemplo

La función se llama calcularPromedio y se encarga de definir tres valores, calcular su promedio e imprimirlo por pantalla. Noten que no se recibe ni se devuelve ningún valor.

Salida

Valor de retorno

Devolviendo números

Siguiendo con el ejemplo anterior, si quisiéramos devolver el resultado en vez de mostrarlo por pantalla, haríamos lo siguiente:

Definimos una nueva función a la cual invocamos en la primera línea. El flujo de programa pega un salto hasta donde está esta función, se ejecuta el código dentro y luego se devuelve el valor de la variable $promedio. ¿Adónde va a parar ese valor? A la variable $pr que es la que está del otro lado de la igualdad en la primera línea, donde es invocada la función. A partir de allí, $pr tendrá el valor que fue devuelto. Esto lo podemos comprobar en la siguiente línea, donde mostramos por pantalla el valor que posee.

Salida

Devolviendo strings

No sólo podemos devolver números, también podemos devolver strings:

No hay mucho que explicar aquí. Se invoca la función en medio del uso del constructor del lenguaje echo y el valor de retorno que es un string queda en ese lugar, por lo tanto, se muestra en pantalla.

Salida

Devolviendo arrays

Como siempre el valor de retorno es uno solo, un buen truco que podemos realizar para devolver más de un valor es hacerlo mediante un array.

En el siguiente ejemplo, tenemos una serie de temperaturas y queremos devolver tanto la suma como el promedio de ellas. Vamos a utilizar una array asociativo para lograrlo.

El array que devolvemos se llama $respuesta y tiene dos elementos, uno con la suma y otro con el promedio de las temperaturas que se declaran al principio de la función. El array que se devuelve se termina asignando a la variable $temp, en la invocación que aparece debajo. A partir de allí, $temp puede ser tratado como una array, tal como se hizo con $respuesta.

Salida

Devolviendo null

Una de las posibilidades que mencionamos antes es que puede no devolverse nada. En ese caso, PHP devolverá por defecto un valor y ese será null. Veamos que esto se cumple en el siguiente ejemplo:

La función noDevuelveNada imprime por pantalla un mensaje, pero no devuelve ningún valor. Noten que la invocación se realiza dentro de la expresión del if. Esto significa que el valor de retorno ocupará su lugar y será comparado contra null.

Un detalle importante es, para este caso, utilizar el comparador de identidad (===). Recuerden que en PHP, si utilizamos el operador de comparación de igualdad común (==) los valores false, 0 y null son exactamente iguales. Mediante el comparador de identidad podemos diferenciarlos.

A través de la salida, veremos con claridad que si no se devuelve nada, en realidad se devuelve null.

Salida

 

Pasaje de parámetros

Los parámetros o argumentos son variables o constantes que se le pasan a una función para que esta opere con ellos. Hay dos formas de pasar parámetros: por valor o por referencia. La primera es la más común y es la que veremos a continuación.

Pasaje por valor

En el pasaje por valor la variable que llega a la función puede ser modificada, pero su valor  original no. Empecemos viendo un ejemplo para entender esto mejor.

Tenemos una función llamada suma. Los parámetros que recibe son dos y sus nombres son $valor1 y $valor2. Dentro, el código es simple: se realiza la suma, se asigna el valor 0 a ambas variables y se devuelve el resultado.

Veamos ahora la parte de la invocación. Se definen dos variables con sus correspondientes valores ($m1 y $m2). A continuación se llama a la función suma, pasando estas variables como argumentos.

Noten primero que no es necesario que el nombre de las variables sea idéntico en la invocación y en la definición de la función. Los valores se corresponderán en el orden en que fueron puestos: $valor1 tendrá el valor de $m1 y $valor2, el valor de $m2.

Si prestamos atención a la salida veremos lo siguiente:

Salida

Como pueden observar, el valor de $m1 y $m2 no se altera puesto que lo que enviamos a la función es una copia de los valores y no las variables en sí mismo.

En la salida primero vemos que las variables $valor1 y $valor2 fueron modificadas dentro de la función. Pero a las variables originales $m1 y $m2 esto no les afecta. En la última línea vemos el resultado de la suma.

Arrays como parámetros

Además de pasar valores como números, también podemos pasar arrays que contengan muchos valores. Esto es una buena idea si tenemos una gran cantidad de argumentos y no queremos pasarlos de a uno. Veamos un ejemplo:

Al final de este bloque de código definimos un array de números enteros llamado $edadesPersonas. Lo pasamos como parámetro a la función obtenerEdadMasGrande.

La función se encarga de recorrer el array como ya hemos visto en una clase anterior y obtener el valor más grande, que es devuelto.

Como la invocación se hizo dentro del ámbito de un echo, el valor ocupará el lugar de la llamada a la función y será mostrado por pantalla.

Salida

Strings como parámetros

En el siguiente ejemplo vamos a ver que se pueden pasar strings como parámetros también.

La función contarAes cuenta la cantidad de letras “a” que hay en un string que llega por parámetro. Para entender mejor el tema de la reutilización del código, podemos observar que realizamos varias llamadas a la misma función con distintos parámetros, evitando tener que repetir código.

La función recorre caracter por caracter cada string y cuenta la cantidad de letras “a” que va encontrando. Finalmente devuelve ese valor.

Salida

Pasaje por referencia

Ahora vamos a conocer el otro tipo de pasaje de parámetros. En el pasaje por referencia el valor de la variable original que es pasada como argumento puede sufrir cambios si es que se realizan dentro de la función. Con un ejemplo va a quedar más claro:

La función producto calcula el producto entre $p1 y $p2 que llegan por parámetro. Como verán, no devuelve nada. Pero sí hay un tercer parámetro, es allí donde guardaremos el resultado. ¿Cómo haremos esto? Anteponiendo en la definición de la función un símbolo & delante de la variable que queremos pasar por referencia. Este simple agregado hará que en caso de ser alterada la variable $r, $resultado también se vea afectada.

Es por eso que en la salida obtenemos lo siguiente:

Lo que ocurre realmente es lo siguiente: al anteponer el & delante de la variable en la definición de la función, lo que hacemos es pasar no una copia del valor como hacíamos cuando pasábamos por valor, sino la dirección de memoria de la variable. Es decir, una referencia a la variable (de ahí el nombre del tipo de pasaje).

Para sintetizar y dejar bien diferenciados los dos tipos de pasajes podemos decir que el pasaje por valor se envía una copia de la variable y en el caso del pasaje por referencia, la variable en sí misma (a través de su dirección de memoria). En el primer caso, el valor original no puede ser alterado y en el segundo, sí.

Ámbito de variables con funciones

El ámbito y alcance de las variables se ve afectado con la utilización de las funciones. Hay cierto comportamiento que es necesario tener en cuenta a la hora de programar. De esto nos vamos a ocupar a continuación, empezando como siempre con un ejemplo, para facilitar el entendimiento.

Veamos el siguiente código:

La salida es la siguiente:

Salida

Aparecen unos Notices, que son algo así como errores. En realidad son avisos del intérprete de PHP. Son un nivel inferior al de los warnings (advertencias) que a su vez estan por debajo de los errores. Lo que nos dicen estos avisos es que hay variables sin definir. Veamos por qué ocurre.

Inicialmente definimos la variable $nombre con el valor ‘Belinda’. A continuación, una función saludoNombre() que usa una variable de nombre $nombre y donde se asigna el valor ‘Arquitecta’ a una variable llamada $profesion.

Debajo se hace una llamada a esa función y al ejecutarse, la variable $nombre no es reconocida. Esto se debe a que no fue definida dentro del ámbito de la función. La definición fue hecha por afuera y al ingresar a ejecutarse la función no la reconoce.

Tras el fallo de la función, mostramos por pantalla la variable $nombre de forma normal y vemos que el valor está definido correctamente, por eso obtenemos la salida “Ahora mi nombre es Belinda”.

Volvemos a obtener un aviso en la siguiente línea. ¿Por qué? Porque estamos intentando utilizar la variable $profesion pero la hemos definido en el ámbito de la función saludoNombre y no fuera de ella. Esto significa que no está definida para poder ser usada por afuera.

Importar con global

Hay dos maneras de “resolver” este “problema” y pongo ambas palabras entre comillas por algo que explicaré al final y que es muy importante.

La palabra clave global sirve para traer desde dentro de una función alguna variable que esté afuera.

Tiene un uso muy sencillo. Se pone la palabra global delante del nombre de la variable que se quiere utilizar. Aquí sí el nombre debe ser el mismo. De esta forma estamos importando la variable y es como si hiciéramos un pasaje de parámetros.

Salida

Otra manera de hacer esto mismo es utilizando el array $GLOBALS definido por PHP. Se trata de una variable del tipo superglobal de PHP, similar a $_POST o $_GET. En este caso $GLOBALS tiene todas las variables definidas fuera de la función y puede ser accedida desde cualquier parte del código.

La sintaxis es simple, igual a la de una array asociativo. Se pone $GLOBALS y la clave es el nombre de la variable (sin el símbolo $ delante). Con ello ya tendremos acceso a su valor.

Lo importante que debía explicar es lo siguiente. El uso de variables globales es controvertido y en más de un lenguaje está considerado una mala práctica de programación y por lo tanto desaconsejada. PHP no es la excepción así que les recomiendo que si en algún momento se ven en la necesidad de utilizar alguna de estas “soluciones” porque tienen variables globales, piensen primero si están encarando el programa de la forma adecuada.

Argumentos variables

Si es necesario, en PHP es posible pasar una cantidad de parámetros variables a una función. Esto se hace con tres funciones específicas para esto: func_num_args(), func_get_arg() y func_get_args().

La función func_num args devuelve la cantidad de parámetros que se recibieron. A func_get_arg hay que pasarle un número entero y nos devuelve el argumento que corresponde a esa posición según el orden en que fueron pasados los parámetros. Por último, func_get_args no recibe ningún valor y devuelve un array donde cada elemento es un parámetro que fue pasado.

Veamos un ejemplo donde usamos dos de estas funciones:

La función colores chequea que la cantidad de parámetros recibidos sea mayor que cero. Noten que no es necesario especificar ningún argumento en la definición. La variable $params recibe el array con los parámetros. Luego se recorre y se muestran por pantalla uno por uno.

Debajo de la función, aparece una primera llamada donde se pasan dos parámetros.

La salida de ese código es la siguiente.

Salida

Pero también podemos pasar más parámetros:

Las variables $color1 y $color2 siguen siendo las mismas de antes; se agregan $color3, $color4 y $color5. El resultado es el que se ve a continuación:

Salida

 

Recursividad

Se dice que una función es recursiva si dentro de su código existe al menos una llamada a sí misma.

La recursividad es un tema muy importante en programación. Hay estructuras que sólo pueden ser recorridas a través de esta técnica que muchas veces resulta difícil para los principiantes. El caso típico que siempre se ejemplifica es el de los árboles.

Veamos un ejemplo de recursividad. La siguiente función sirve para determinar si un número es par o no.

La función esPar recibe siempre un parámetro, que en este caso es un número entero. Si el número es 1, determina que el número no es par y si es cero, determina que sí lo es. ¿Y en el resto de los casos? Eso es lo que se hace en el else: si el número no es ni 0 ni 1, se vuelve a llamar a la función una vez más, pero en este caso el parámetro que se pasa es el que se recibió menos 2. Puede ocurrir nuevamente que en la próxima llamada vuelva a suceder que ninguno de los valores sea 0 ó 1. Si esto ocurre, entonces se volverá a llamar a sí misma cuantas veces sea necesario. En algún momento se dará el caso en que el valor sea 0 ó 1, piensen que si a cualquier número le restamos siempre 2, por más grande que sea, en la última llamada, el valor será 0 ó 1. Cuando eso ocurra, la función dejará de llamarse a sí misma y empezará a volver hacia atrás, devolviendo el valor que corresponda (true o false, según el caso).

Veamos todo esto con un ejemplo. Supongamos que, tal como se muestra el código de arriba, se le pasa a esPar el número 9. En la primera llamada, 9 no equivale ni a 0 ni a 1. Así que se volverá a llamar a esPar, pero ahora el parámetro que se reciba será 9 – 2= 7. Pero nuevamente 7 no equivale ni a 0 ni a 1, así que se volverá a llamar por tercera vez a esPar, aunque ahora el valor que reciba será 7 – 2 = 5. Con 5 ocurre lo mismo una vez más, y luego con 3. Con 3, al volver a llamar a la función, el nuevo valor será 3 – 2 = 1. Ahora sí, veremos que el parámetro es igual a 1, por lo tanto se devuelve false. ¿A dónde se devuelve false? A la función invocante, que es la misma función. Como ven en la siguiente línea:

Este resultado también es devuelto. Es decir que el false que recibe la última función invocante, es transmitido hacia atrás, hasta la primera invocación que es nada más y nada menos que la primera llamada original que se hizo desde afuera de la función.

Salida

Podría escribir mucho más acerca de la recursividad, pero no es el propósito de esta clase. Sin duda alguna, el lenguaje donde se debe aprender esto no es precisamente PHP, sino C ó C++.  Sí me gustaría dejarles bien en claro que es un tema vital que todo programador debe dominar. Uno de esos temas que hace la diferencia entre un programador amateur y uno profesional.

Librerías

Por último me gustaría volver sobre el concepto de reutilización del código y hablar sobre las liberías. Las librerías, en cualquier lenguaje de programación, suelen ser conjuntos de funciones (o clases) que sirven para realizar alguna tarea específica.

Son lo más parecido a una caja de herramientas. En sí, su uso de forma aislada puede no servir para mucho, pero pueden ser útiles para construir proyectos más grandes.

La clave a la hora de construir librerías es agrupar las funciones por tema o afinidad. De esta forma, podríamos tener una librería para validación de datos, una librería para realizar estadísticas, otra para trabajar con números complejos. O podríamos crear asistentes de HTML. Imaginen un grupo de funciones que reciban parámetros tipo strings y que devuelvan elementos HTML como párrafos y formularios.

Una librería se puede armar en un archivo PHP separado añadiendo todas las funciones que nos parezca conveniente, teniendo en cuenta siempre que deben juntarse siempre que tengan sentido. No sería razonable mezclar funciones que manejen fechas con otras que se encarguen realizar gráficos.

Una vez que se tiene la librería ya hecha, se puede incluir en otros archivos PHP utilizando la familia de funciones require e include que ya hemos visto en una clase anterior.

Conclusiones

A lo largo de esta clase hemos formalizado parte del conocimiento que teníamos sobre las funciones y hemos ido más allá, entendiendo de forma más precisa qué son exactamente y cómo funcionan. También hicimos un repaso por las ventajas más importantes de la reutilización del código y el uso de librerías.

A nivel práctico, vimos la sintaxis básica de una función para poder construirlas y exploramos cada una de las distintas variantes como pueden ser utilizadas según reciban argumentos y devuelvan o no valores. Además, vimos que es posible pasarles una lista de parámetros variables.

Definimos la diferencia entre el pasaje por valor y el pasaje por referencia y nos detuvimos a analizar el comportamiento de las variables según el ámbito de una función.

Sobre el final de la clase conocimos el significado de recursividad y lo pusimos en práctica a través de un ejemplo, marcando la importancia que tiene este tema no sólo en PHP sino en el desarrollo en general.

Ejercicios propuestos

  1. Hacer una función que determine si un año es bisiesto o no. Dar varios ejemplos de invocación.
  2. Realizar una función que reciba un número entero y determine si es capicúa o no. Ejemplos de número capicúa: 12721, 33133, 70807, 23932
  3. Realizar un programa en PHP que escriba la serie de Fibonacci utilizando para ello una función recursiva. La serie de Fibonacci comienza con 1, 1 y luego sigue con 2, 3, 5, 8, 13, 21, 34…
  4. Realizar una función que si recibe dos parámetros números enteros, los multiplique. Si recibe tres números, los sume y si recibe cuatro, los promedie. Debe devolver el resultado en cualquiera de los casos.
  5. Realizar una función que reciba una cadena  de texto. Debe devolver un array que indique: a) Cantidad de caracteres (incluyendo los espacios) y b) cantidad de letras vocales por cada vocal (cantidad de “a”, de “e”, de “i”, de “o” y de “u”)
  6. Realizar una función que reciba una cantidad de parámetros variables y devuelva un string con una lista ordenada (<ol></ol>) en código HTML, donde cada elemento de la lista sea un parámetro recibido.
  7. Realizar un programa que reciba un array con una serie de números enteros. Debe hallar el menor y devolver true si es positivo y false si es negativo. Además, deberá guardar el valor del menor de forma que pueda ser usado desde el programa invocante.

Bibliografía

  1. Zend PHP5 Certification Study Guide de Davey Shafik con Ben Ramsey, php architect nanobooks
  2. Desarrollo web con PHP y MySQL, de Luke Welling y Laura Thomson, Editorial Anaya
  3. Estructuras de Datos Estáticas en Pascal, de Zulma Cataldi y Fernando J. Lage. Centro de Estudiantes de Ingeniería.
Soy programador web y me desempeño como Líder Técnico y de Proyectos en Polar Bear Development. Trabajo con tecnologías como PHP, Javascript, MySQL y HTML5 para el desarrollo de sitios y sistemas web. Me especializo en Zend Framework 2 y otros frameworks MVC, como también en WordPress y otros CMS. Lidero equipos de desarrolladores trabajando con Scrum. Vivo en Buenos Aires, Argentina.
 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*