Universidad de Costa Rica
Escuela de Ciencias de la Computación e Informática
CI-2413 Desarrollo de aplicaciones web
Profesor: Jeisson Hidalgo Céspedes
15-Noviembre-2011

Laboratorio 10

Este laboratorio guía autodidácticamente al estudiante en el aprendizaje del lenguaje de programación PHP y la elaboración de sitios web dinámicos desde el lado del servidor.

Programación del servidor web: PHP

Un autor que quiera construir un sitio web dinámico deberá instalar un servidor web (como nginx), configurarlo con FastCGI, escribir un programa (en C, C++, Java, ...) que sea un servidor FastCGI y además se ocupe de la lógica del sitio. Esta tarea puede ser monumental, incluso para un sitio web sencillo. Este fue el sentimiento que tuvo Rasmus Lerdorf en 1994 cuando quiso publicar una página web personal con un mínimo grado de dinamismo, como el conteo de visitantes. Lerdorf encontró que sería más natural insertar pequeños trozos de código (scripts) dentro de sus páginas web estáticas. Cada vez que un visitante solicitara una de estas páginas, un intérprete ejecutaría estos scripts antes de responderle al cliente. Lerdorf llamó "Personal Home Page tools" a su intérprete y lo dispuso como software libre, el cual evolucionaría a ser el lenguaje de "scripting" más usado en servidores web del mundo, bajo el nombre recursivo de PHP: Hypertext Processor (PHP).

El lenguaje de programación PHP

Aunque se pueden escribir programas PHP independientes en línea de comandos o con interfaz gráfica, su uso más difundido ha sido para crear páginas web dinámicas, es decir, aquellas cuyo contenido, apariencia o comportamiento puede cambiar cada vez que un navegador las solicita. Así PHP está diseñado para mezclarse con el contenido de las páginas web. El php_hello_world muestra un posible "hola mundo" con PHP.

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Hola mundo PHP</title>
</head>

<body>
    <h1>Hola mundo en PHP</h1>
    <?php
        print("<p>Este párrafo proviene de PHP, no del documento XHTML</p>\n");
    ?>
</body>
</html>
Hola mundo en PHP. Correr este ejemplo.

Al correr el ejemplo del php_hello_world ocurre lo siguiente. El servidor web recibe un mensaje solicitando el recurso /path/to/hello_world.php, el cual es un simple archivo de texto. En condiciones normales, el servidor web podría retonarle el archivo al navegador, el cual no sabría qué hacer con él y le preguntaría al usuario un lugar para guardarlo. Sin embargo, el servidor web está configurado para tratar de forma especial a todos los archivos que terminen con extensión .php. Esta configuración indica al servidor web que debe ejecutar los scripts que se encuentran en el archivo .php antes de enviarlo al navegador.

La configuración del servidor web también le indica cómo encontrar el intérprete de PHP. Así el servidor invoca al intérprete pasándole el archivo /path/to/hello_world.php. El intérprete carga el archivo y lo recorre (parse) buscando scripts de PHP, los cuales se encuentran únicamente dentro de la etiqueta especial <?php ... ?>. El intérprete ejecuta este código y reemplaza la etiqueta especial por la salida que generó su ejecución. Una vez que ha terminado de ejecutar todos los scripts en el archivo, el resultado regresa al servidor web quien lo despacha al navegador.

El navegador sólo recibe el resultado de ejecutar los scripts, no el código fuente PHP que generó el resultado. El lector puede comprobarlo corriendo el ejemplo del php_hello_world y revisando en su navegador el código fuente de la página.

Durante muchos años se utilizó un short tag <? ... ?> en lugar de <?php ... ?> para escribir el código PHP. El uso del "short tag" debe evitarse a toda costa, ya que genera conflicto con las instrucciones de procesamiento de XML <?xml ... ?>. De hecho, si se quiere servir documentos XHTML con scripts de PHP, se debe deshabilitar la directiva short_open_tag en la configuración del intérprete de PHP (en el archivo php.ini). El php_web_server_date muestra la fecha y hora del servidor web.

<body>
    <h1>Hora y fecha del servidor</h1>
    <p><?php
        $fecha = date('d \d\e F \d\e Y');
        $hora = date('h:i:s a');
        print("Hoy es " . $fecha . ". Son las " . $hora );
    ?></p>
</body>
La hora del servidor en PHP. Correr este ejemplo.

El php_web_server_date muestra otras características de PHP. En el ejemplo del hola mundo (php_hello_world) el párrafo se generó dentro del script de PHP (línea 13), mientras que en el ejemplo de la hora (php_web_server_date) el script se escribió dentro de un elemento p. Esto muestra que el autor puede intercalar código PHP y (X)HTML a conveniencia. Algunos programadores prefieren abrir una única etiqueta <?php ... ?> y escribir mucho código PHP, otros prefieren escribir código (X)HTML y abrir etiquetas <?php ... ?> sólo para el mínimo código PHP necesario. El segundo método es quizá ligeramente más eficiente y es la convención que se tomará en este texto.

Ejercicio. Reescriba el ejemplo de la hora del servidor (php_web_server_date) utilizando sólo código PHP, es decir, su archivo tendrá sólo una etiqueta real: <?php ... ?>, y las etiquetas XHTML se generan a partir de strings en PHP. La salida de su script debe ser un documento XHTML válido. Nota 1: en los ejemplos de este material se presenta sólo fragmentos de código por razones de legibilidad; si el lector quiere reproducirlos debe agregar el marcado faltante, como la declaración de tipo de documento, el elemento html, y el encabezado del documento web. Nota 2: Las funciones estándar de PHP utilizadas en los listados de este material pueden tener enlaces hacia su documentación oficial.

Las líneas 4 y 5 del php_web_server_date invocan una función estándar de PHP: date(), la cual retorna un string con la fecha u hora en el formato que se le indique por parámetro. PHP tiene una rica biblioteca de funciones cuya documentación es quizá una de las mejores que existe en el mundo.

El string resultado de invocar a date() en la línea 4 del php_web_server_date se almacena en la variable $fecha. PHP exige anteponer el símbolo de dólar ($) a un identificador para distinguirlo como una variable, de la misma forma que ocurre en Perl y en Unix shell scripting, con el objetivo de hacer más eficiente el reconocimiento de las variables a la hora de ejecutar el código. Al igual que en JavaScript, las variables pueden almacenar valores de cualquier tipo de datos, incluso cambiar en el tiempo. Una variable se crea simplemente asignándole un valor.

Ejercicio. Escriba un programa PHP que imprima la cantidad de días que faltan para navidad (25 de diciembre) del año en curso. Si la navidad ya pasó, imprime la cantidad de días que faltan para la navidad del próximo año. Busque en la documentación de PHP una función que le pueda ayudar a construir fechas a partir de sus valores separados (hora, mes, día, etc.). Sugerencia: puede también estudiar los ejemplos provistos en la documentación de la función date().

La línea 6 del php_web_server_date imprime un texto resultado de concatenar unos strings literales con las variables $fecha y $hora utilizando el operador punto (.). Esto crea la primera diferencia con JavaScript, que utiliza el operador de suma (+) para la concatenación. El ejemplo anterior pudo haberse escrito de otra forma muy común:

<body>
    <h1>Hora y fecha del servidor (2)</h1>
    <p><?php
        $fecha = date('d \d\e F \d\e Y');
        $hora = date('h:i:s a');
        print("Hoy es $fecha. Son las $hora");
    ?></p>
</body>
La hora del servidor con interpolación de variables. Correr este ejemplo.

El ejemplo del php_variable_interpolation sólo cambia la línea 6 del php_web_server_date. En la nueva versión se insertaron las variables directamente en el string. A esto se le llama interpolación de variables o sustitución de variables y el intérprete de PHP lo hace únicamente en las cadenas con comillas dobles (como en "$var") y no en cadenas con comillas simples (como en '$var'). Lo mismo aplica para las secuencias de escape como "\n".

Ejercicio. Pruebe el efecto de cambiar las comillas dobles por simples en el ejemplo del php_variable_interpolation.

El desarrollador web debe tener clara esta diferencia, ya que JavaScript trata de forma idéntica a las cadenas literales con comillas dobles o simples, mientras que PHP hace interpolación de variables y secuencias de escape en una de ellas y no en la otra. El lector podría estar pensando en utilizar siempre comillas dobles para evitar sorpresas, sin embargo, debe tener en cuenta que el procesamiento de las cadenas literales con comillas simples es más eficiente que con comillas dobles.

Ambientes de producción y desarrollo

Para escribir un programa en PHP el desarrollador web sigue normalmente la siguiente rutina. Crea un archivo de texto con extensión .php donde escribe su código dentro de etiquetas <?php ... ?>. Lo guarda en alguna carpeta que forma parte de su sitio web. Accede al archivo .php a través de un navegador escribiendo su URL. El servidor invoca al intérprete de PHP y envía el resultado al navegador. El desarrollador puede entonces ver la salida de su programa.

Dependiendo de la configuración de PHP, esta rutina se vuelve en contra del desarrollador o de la empresa cuando el programa PHP tiene errores. Si la configuración dicta que el intérprete de PHP debe informar los errores en la salida estándar, éstos aparecerán en el navegador, lo cual ayudará enormemente al desarrollador a depurar sus programas, pero podría hacer visible información sensible de la empresa a los visitantes, o confundirlos arruinando el contenido y diseño del sitio web.

Si la configuración dicta que el intérprete de PHP debe callar los errores (o a lo sumo enviarlos a una bitácora), el desarrollador difícilmente detectará que los hay, a menos de que el intérprete se niegue a ejecutar el código PHP con errores y no regrese del todo una respuesta al servidor web, el cual optará por enviar un mensaje de respuesta con un código de estado 500 Internal server error al navegador. Esto es mucho más eficiente y evita que el servidor web exponga información sensible a los visitantes, aunque estos se molestarán en todo caso.

La solución aconsejada es tener dos ambientes: uno de producción y otro de desarrollo. El ambiente de producción aloja el sitio web que atiende a los visitantes, se considera estable y los errores en el código PHP no son reportados al navegador. El ambiente de desarrollo es de uso exclusivo de los desarrolladores, en él los errores y advertencias son reportadas al navegador. Estas diferencias se configuran en la sección Error handling and logging del archivo php.ini.

Los ambientes de producción y desarrollo podrían estar en computadoras diferentes, o en la misma bajo servidores virtuales distintos. Es importante que el sitio web completo se encuentre administrado por algún software de control de versiones, como Subversion o Git, lo cual además facilita el mantenimiento del código entre estos ambientes.

Ejercicio. Escriba un archivo .php que tenga uno o más errores de sintaxis, como omitir un punto y coma, o escribir un trozo de texto literal sin comillas. Pruebe qué ocurre al ejecutar su programa en dos ambientes diferentes: uno local y otro en un servicio de alojamiento web gratuito con soporte para PHP. Determine el tipo de manejo de errores que hace cada cuál. Configure el ambiente local para que reporte al navegador errores y advertencias (warnings), si no lo está ya.

Antes de publicar código PHP en el sitio web o en el repositorio de control de versiones, el desarrollador siempre debería chequear sus programas con el intérprete de PHP, lo cual puede hacerse en la línea de comandos de la siguiente forma:

php /path/to/file.php

El desarrollador tampoco podrá verificar un documento .php contra los validadores del Consorcio Web (como Unicorn), pero sí su salida. Es decir, que el desarrollador deberá hacer dos verificaciones, la primera es llamar al intérprete de PHP en línea de comandos para verificar que su código PHP sea correcto, y luego la salida que su programa genera debe verificarla contra los estándares del Consorcio Web. Una tercera verificación podría deberse si se tiene código JavaScript, inspeccionando la bitácora de errores del navegador.

Generalidades del lenguaje PHP

La sintaxis de PHP se basa en C y Perl; y tiene mucha familiaridad con C++ y Java. Esto implica que los comentarios, condicionales, ciclos y operadores son similares. Este material asume que el lector conoce C y JavaScript, y se concentra en los aspectos que hacen a PHP distinto a estos lenguajes. Los comentarios en PHP se pueden escribir en cualquiera de las siguientes tres formas:

// Este es un comentario de una línea con estilo BCPL

# Este es un comentario de una línea con estilo Perl

/* Este comentario
puede abarcar varias
líneas pero no anidarse */

Al igual que JavaScript, PHP diferencia mayúsculas y minúsculas (case sensitive), y la aritmética se hace en punto flotante, es decir, 5 / 2 genera 2.5 y no 2 como ocure en C. A diferencia de JavaScript, PHP sí permite que un string se expanda varias líneas, y en tal caso, los espacios en blanco y cambios de línea pasan a formar parte natural del string, por ejemplo:

$table = /* ... */;
$condition = /* ... */;

$query = "
    SELECT *
    FROM $table
    WHERE $condition
";

equivale a haber escrito el siguiente código en una sola línea, pero que resulta más ilegible:

$query = "\n\tSELECT *\n\tFROM $table\n\tWHERE $condition\n";

Al igual que JavaScript, las variables pueden almacenar valores de diferentes tipos de datos a lo largo de la ejecución del script, y las conversiones de datos se hacen automáticamente de acuerdo al contexto. En PHP el operador de suma (+) se utiliza sólo para valores numéricos y no hace concatenación, lo cual es responsabilidad del operador punto (.). Por ejemplo:

<p>
<?php
    # Una variable que almacena un valor numérico
    $telefono = 50628241202;

    # Obtiene el cuarto digito (en la posicion 3) conviertiendo el número en un string
    # la conversion la hace PHP automaticamente al usar algo en un contexto string
    $tipo_tel = substr($telefono, 3, 1);

    # Comparación de un string con un número: el string se convierte a número
    if ( $tipo_tel != 8 )
        echo "No se puede enviar mensajes de texto al teléfono $telefono.";
?>
</p>
Conversión automática de tipos de datos. Correr este ejemplo.

El lector habrá notado que en algunos ejemplos se ha utilizado print y en otros echo para generar la salida que recibirá el navegador. No son funciones realmente, son constructos del lenguaje y por ende pueden utilizarse con o sin paréntesis. La diferencia radica en que print recibe únicamente un parámetro de tipo string y retorna siempre el entero 1; mientras que echo no tiene un valor de retorno y puede recibir un número arbitrario de parámetros de cualquier tipo separados por comas, los cuales tratará de convertir a string automáticamente. En general, print() es un constructo del lenguaje utilizado para simular una función en un contexto donde se requiere una, mientras que echo es un constructo del lenguaje que no puede utilizarse en el contexto de una función y debe obligatoriamente usarse sin paréntesis si recibe dos o más parámetros. Exceptuando esas extrañas restricciones, en la mayoría de contextos puede utilizarse cualquiera de los dos, y la escogencia la hace el programador de acuerdo a sus gustos personales.

Ejercicio. Escriba un programa PHP que reporte estadísticas sobre el uso de la memoria secundaria en la carpeta donde está alojado su archivo .php (la cual se puede obtener con la expresión dirname(__FILE__)). Imprima en una tabla (X)HTML como la siguiente.

Rubro Tamaño Porcentaje
Espacio utilizado 22.98 GiB 98.63%
Espacio libre 322 MiB 1.37%
Espacio total 23.00 GB 100.00%

Estudie las funciones disk_free_space() y disk_total_space(). Note que la cantidad de bytes debe imprimirse en unidades amigables (kiB, MiB, GiB, etc.) [Puede reutilizar código provisto en los ejemplos de la documentación]. Imprima en rojo el espacio libre si es menor al 5% para alertar al usuario de que sus programas PHP, y por ende, su sitio web, podrían fallar por falta de espacio en disco.

Tipos de datos

Los tipos de datos que PHP soporta son: booleanos, números (enteros y flotantes), strings, arreglos, objetos, recursos (resource) y el valor especial NULL. Son prácticamente los mismos tipos de datos de JavaScript con algunas diferencias. Una variable no tiene tipo de datos, sino que puede almacenar valores de cualquiera de esos tipos de datos. La función gettype($var) retorna un string que describe el tipo de datos del valor almacenado en la variable $var.

Las variables booleanas (boolean) sólo admiten los valores true y false, los cuales se pueden escribir en cualquier combinación de mayúsculas y minúsculas (case-insensitive).

Internamente PHP almacena los números utilizando enteros (int) siempre que sea posible, y cuando el número es muy grande o tiene decimales, se representa internamente como un número flotante (double). El programador no necesita preocuparse por estas diferencias. Sin embargo, ambas representaciones tienen limitaciones en cuanto a la capacidad y precisión, inadecuadas cuando se tiene que hacer cálculos precisos o manejo de dinero. En tales circunstancias el programador puede recurrir a alguna extensión matemática de PHP.

Los strings en PHP no son objetos, por tanto, no tienen propiedades ni métodos como length(); sino que deben ser manipulados en forma similar a C: a través de funciones como strlen(str) y substr(str, start, length). Sin embargo hay una diferencia importante, en C los strings son punteros mientras que en PHP son valores y por tanto, los operadores de comparación (==, ===, <, <=, >, >=) y concatenación (.) trabajan de forma esperada.

En PHP todos los arreglos son realmente arreglos asociativos, también conocidos como mapas (maps), tablas hash o diccionarios; porque almacenan parejas llave => valor. Si las llaves no se especifican, se asumen valores enteros secuenciales. Los arreglos se crean con el constructo array(v1, v2, ..., vN) que recibe una lista de valores separados por comas, e inserta esos valores en orden asociándolos con llaves enteras correspondientes a la posición del elemento. Las llaves también pueden ser strings, y en tal caso se especifican usando la notación key => value. Un arreglo que almacene varias de estas parejas se crea utilizando la notación array(k1 => v1, k2 => v2, ..., kN => vN), como se aprecia en el php_arrays_intro. Al igual que los string, los arreglos no son objetos, por lo que su manipulación se hace a través de funciones como count($arr) y sort($arr). Este tema se estudiará con más detalle adelante.

<h1>Arreglos</h1>
<pre><?php
    // Crear un arreglo secuencial
    $days = array('domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sabado');

    // Recorrer el arreglo
    echo "\$days == \n";
    for ( $i = 0; $i < count($days); ++$i )
        echo '   ', $i, ': ', $days[$i], "\n";

    // Agregar un elemento al arreglo
    echo "\n\$days[] = 'desconocido'\n";
    $days[] = 'desconocido';

    // Estudiar el arreglo
    echo "\n\$days == "; var_dump($days);
?></pre>

<h1>Arreglos asociativos</h1>
<pre><?php
    // Crear un arreglo asociativo user => points
    $gameRecords = array('lvargas' => 64900, 'elpapi' => 58831, 'chema' => 9215);

    // Recorrer el arreglo
    echo "\$gameRecords == \n";
    foreach ( $gameRecords as $user => $points )
        echo '   ', $user, ': ', $points, " puntos\n";

    // Agregar un elemento al arreglo
    echo "\n\$gameRecords['pinocho'] = 64901\n";
    $gameRecords['pinocho'] = 64901;

    // Ordernar el arreglo por valores descendenemente
    arsort($gameRecords);

    // Estudiar el arreglo
    echo "\n\$gameRecords == "; var_dump($gameRecords);
?></pre>
Ejemplo de uso de arreglos secuenciales y asociativos en PHP. Correr este ejemplo.

Los objetos de PHP se parecen mucho a los de Java. Un objeto es una instancia de una clase, la cual puede tener herencia y polimorfismo con otras clases. Los objetos se crean con el operador new y sus miembros se acceden con el operador ->. Este tema se explica más adelante.

El tipo de datos y valor especial null sirve para indicar que una variable no tiene valor. Si se trata de hacer referencia a una variable que no existe, PHP puede o no generar un warning de acuerdo a su configuración, y el valor de reemplazo de la variable no existente es null, el cual se puede escribir en cualquier combinación de mayúsculas y minúsculas.

Cuando se utiliza un valor de un tipo de datos en un contexto donde se espera otro, PHP trata de hacer conversiones automáticamente; lo cual funciona bien en la mayoría de casos para los valores primitivos. El programador puede forzar la conversión de datos utilizando explicit type cast, anteponiendo el tipo de datos entre paréntesis a la expresión que se quiere convertir, como en el ejemplo del php_explicit_type_cast.

$num = 5 / 2;
echo "5 / 2 == $num\n";         // prints 2.5

$num = (int) (5 / 2);
echo "(int) (5 / 2) == $num\n"; // prints 2
Conversión explícita de datos en PHP. Correr este ejemplo.

Operadores

El programador puede escribir los mismos operadores que usa en C o JavaScript sin cambios en PHP. Las diferencias son mínimas, como las que se exponen a continuación.

El operador de concatenación en PHP es el punto (.), así el operador de suma (+) pretende sólo recibir operandos numéricos, de lo contrario, tratará de convertirlos a números.

Al igual que en JavaScript, el operador de igualdad == indica si dos valores son el mismo aún después de hacer conversiones, así la expresión "+12.3" == 12.30 se evaluará como verdadera. El operador de identidad === se evalúa verdadero sólo si sus operandos son iguales sin hacer conversiones.

Los operadores lógicos clásicos && y || trabajan de la misma forma que en otros lenguajes de programación. Sin embargo, PHP introduce los operadores lógicos nombrados and y or (que también pueden escribirse en mayúsculas: AND y OR), que tienen una prioridad menor, por lo que pueden causar conflictos si se combinan operadores clásicos con los nombrados en una misma expresión. Se aconseja al programador utilizar sólo los operadores clásicos a menos de que se entienda bien el efecto del operador nombrado.

Control de flujo

Los condicionales en PHP se indican con tres cláusulas: if/else/elseif, ?: y switch. La cláusula else if se puede escribir con el espacio o sin él (elseif); su efecto es el mismo en ambos casos, como se aprecia en el php_elseif.

<body>
    <h1>Lógica de pares</h1>
    <p>
        <?php
            $promedio = rand() % 100;
            echo "Promedio = $promedio: ";
            if ( $promedio < 30 )
                echo 'Vuelva a explicar';
            elseif ( $promedio < 70 )
                echo 'Discusión de pares';
            else
                echo 'Pase al siguiente tema';
        ?>
    </p>
</body>
La cláusula else if se puede escribir con o sin el espacio en PHP. Correr este ejemplo.

Los ciclos en PHP se realizan con las mismas cláusulas de C: while, do-while y for, las cuales mantienen la misma sintaxis. PHP agrega una estructura de control más: foreach, la cual sirve para iterar arreglos y objetos, como se verá más adelante en sus respectivas secciones.

Arreglos

En PHP los arreglos son contenedores asociativos, es decir, son contenedores de parejas key => value, donde las llaves (key) pueden ser números o strings, y si se omiten, PHP asume valores enteros. Un arreglo se crea simplemente asignándole valores a sus elementos o con el constructo array(). Para crear una pareja key => value y agregarla al arreglo, se emplea la siguiente notación:

$arr[key] = value;

Si se asigna un valor a una llave que ya existe dentro del arreglo, no se inserta de nuevo, si no que se reemplaza su valor anterior. Si se omite la llave en la asignación, PHP busca el último entero asociado en el arreglo y asigna el subsecuente al valor. Esto se ilustra en el php_array_assign.

// Crea un arreglo asignándole un valor
$notas['luis'] = 9.5;
echo "\n\$notas['luis'] = 9.5;\n\$notas = "; print_r($notas);

// Crea otra asociación (key,value) y la agrega al arreglo
$notas['ana'] = 10.0;
echo "\n\$notas['ana'] = 10.0;\n\$notas = "; print_r($notas);

// Llave 'luis' ya existe en arreglo, actualiza valor
$notas['luis'] = 6.5;
echo "\n\$notas['luis'] = 6.5;\n\$notas = "; print_r($notas);

// Asocia un valor a la mayor clave numérica libre en el arreglo
$notas[] = 'ana';
echo "\n\$notas[] = 'ana';\n\$notas = "; print_r($notas);

// Asocia la posición numérica dada con el valor
$notas[3] = 'diana';
echo "\n\$notas[3] = 'diana';\n\$notas = "; print_r($notas);

// Asocia en la posición 1 ó 4?
$notas[] = 'fiona';
echo "\n\$notas[] = 'fiona';\n\$notas = "; print_r($notas);
Insertar y actualizar elementos en un arreglo utilizando el operador corchetes y asignación. Correr este ejemplo.

La segunda forma de crear un arreglo es utilizando el constructo array(k1 => v1, k2 => v2, ..., kN => vN), que recibe las parejas llave=valor utilizando la notación key => value que asemeja una asignación, pero en lugar de ser una asignación directa indica actualizar el valor del índice o llave key. En el constructo array(), si una llave se omite, se asume el próximo índice que sigue al último entero utilizado. El resultado final del php_array_assign se podría escribir utilizando array() como se aprecia en el php_array_construct.

// Crea un arreglo utilizando el constructo array()
$notas = array('luis' => 6.5, 'ana' => 10.0, 'ana', 3 => 'diana', 'fiona');
echo "\n\$notas = "; print_r($notas);

// Se puede asociar nuevos elementos a un arreglo creado con array()
echo "\nAgregando notas para diana y fiona:";
$notas['diana'] = 8.5;
$notas['fiona'] = 5.0;

// Imprime todos los elementos del arreglo utilizando el ciclo foreach
foreach($notas as $key => $value)
    echo "\n    $key: $value";

// Calcula el promedio de la clase
$sum = 0.0;
$count = 0;
foreach($notas as $key => $value)
{
    if ( gettype($value) == 'double' )
    {
        $sum += $value;
        ++$count;
    }
}
printf("\n\nEl promedio de los $count alumnos es %.2f", $sum / $count);
Crear un arreglo utilizando el constructo array() y recorrerlo con el ciclo foreach-as. Correr este ejemplo.

PHP provee el ciclo foreach-as, el cual recorre un arreglo por cada una de sus parejas key => value y permite al programador hacer algo con ellas en cada iteración del ciclo. El orden de recorrido es el mismo en que se encuentran los elementos en el arreglo, y puede alterarse con funciones de ordenamiento.

Las llaves en un arreglo asociativo sólo pueden ser enteras o string, pero el valor puede ser de cualquier tipo de datos, incluso otro arreglo. Esto permite representar matrices o arreglos de más dimensiones.

Funciones

Las funciones en PHP se declaran con la palabra reservada function seguido por la lista de parámetros entre paréntesis, de la misma forma que se hace en JavaScript. El php_factorial_function ilustra la función factorial() y su uso para imprimir los primeros 19 factoriales.

<body>
    <h1>Factoriales</h1>
    <ul>
    <?php
        # Retorna el factorial de $n
        function factorial($n)
        {
            return $n > 0 ? $n * factorial($n - 1) : 1;
        }

        // Imprime los primeros 20 factoriales incluyendo el 0
        for ( $i = 0; $i < 20; ++$i )
            echo "<li>$i! = ", factorial($i), "</li>\n";
    ?>
    </ul>
</body>
Números factoriales con una función recursiva en PHP. Correr este ejemplo.

En JavaScript el manejo de valores o referencias es implícito, es decir, el lenguaje se reserva su manejo y el programador no puede alterarlo. En cambio, en PHP el manejo de valores y referencias es explícito, es decir, el programador debe indicar cuándo quiere hacer una copia de un valor o cuando quiere tener una referencia hacia el valor original. Como es de esperar, las referencias se crean con el operador ampersand (&), como se aprecia en el ejemplo del php_byvalue_byreference.

Reglas de alcance de variables (variable scope)

Una importante diferencia en PHP con otros lenguajes de programación son sus reglas de alcance de variables (variable scope). PHP asume que toda referencia a variable que se haga en el cuerpo de una función, es una referencia a una variable local, a menos de que se indique lo contrario. En el php_variable_scope_error, la función print_factorials() falla en la línea 16 al tratar de acceder a $count, ya que PHP busca una variable $count local, la cual no fue declarada en ese contexto.

<body>
    <h1>Factoriales</h1>
    <ul>
    <?php
        # Variable global
        $count = 25;

        function factorial($n)
        {
            return $n > 0 ? $n * factorial($n - 1) : 1;
        }

        function print_factorials()
        {
            // Error: $count no esta definida localmente
            for ( $i = 0; $i < $count; ++$i )
                echo "<li>$i! = ", factorial($i), "</li>\n";
        }

        print_factorials();
    ?>
    </ul>
</body>
Error tratando de acceder a una variable local no definida. Correr este ejemplo.

Este problema es un error común de programación, debido a PHP rompe la norma de la mayoría de lenguajes con los cuales podría estar familiarizado el desarrollador. Hay varias soluciones. Si es factible, se recomienda pasar por parámetro el valor que requiere la función, así se mantiene la modularidad del código y se reduce la dependencia de variables globales, como se hace en el php_variable_scope_fix1.

<body>
    <h1>Factoriales</h1>
    <ul>
    <?php
        // Variable global
        $count = 25;

        function factorial($n)
        {
            return $n > 0 ? $n * factorial($n - 1) : 1;
        }

        function print_factorials($count)
        {
            // Error: $count no esta definida localmente
            for ( $i = 0; $i < $count; ++$i )
                echo "<li>$i! = ", factorial($i), "</li>\n";
        }

        print_factorials($count);
    ?>
    </ul>
</body>
Pasar variables globales por parámetro a una función. Correr este ejemplo.

Si por alguna razón se necesita trabajar con variables globales, se le puede indicar a la función que la variable $count no está definida en el contexto local si no en el global, lo cual se hace con la palabra reservada global, como se aprecia en la línea 16 del php_variable_scope_fix2. El uso de variables globales se considera una mala práctica de programación.

<body>
    <h1>Factoriales</h1>
    <ul>
    <?php
        // Variable global
        $count = 25;

        function factorial($n)
        {
            return $n > 0 ? $n * factorial($n - 1) : 1;
        }

        function print_factorials()
        {
            // Hace a $count accesible en el contexto local
            global $count;

            // Error: $count no esta definida localmente
            for ( $i = 0; $i < $count; ++$i )
                echo "<li>$i! = ", factorial($i), "</li>\n";
        }

        print_factorials();
    ?>
    </ul>
</body>
Uso de variables globales. Correr este ejemplo.

PHP provee algunas variables globales que son accesibles desde cualquier contexto, es decir, sin tener que declararlas primero con la palabra reservada global. Se les conocen como variables superglobales y son arreglos asociativos con información muy útil para el programa. Se listan en la php_superglobal_variables.

Arreglo Descripción
$GLOBALS Sirve para acceder a las variables globales, en especial si se quiere acceder a una variable global y existe una homónima en el contexto local.
$_SERVER Contiene variables enviadas por el servidor web al intérprete de PHP, como $_SERVER['PHP_SELF'] que tiene la ruta del programa PHP relativa a la raíz del sitio, el nombre del servidor web ($_SERVER['SERVER_NAME']), detalles del navegador que solicitó ejecutar el programa PHP ($_SERVER['HTTP_USER_AGENT']), el IP de la máquina del cliente ($_SERVER['REMOTE_ADDR']) y muchos otros.
$_GET Aloja las variables pasadas al script por parámetro en el URL (método HTTP GET).
$_POST Aloja las variables pasadas al script por parámetro utilizando el método HTTP POST.
$_FILES Aloja información sobre los potenciales archivos que se hayan enviado (uploaded) por el método HTTP POST.
$_COOKIE Un arreglo con variables y valores que el navegador aloja por petición del sitio web.
$_SESSION Almacena variables globales para controlar la sesión con el navegador.
$_REQUEST Un arreglo que contiene la unión de los arreglos $_GET, $_POST y $_COOKIE.
$_ENV Las variables ambiente que dispone el intérprete de JavaScript. Son dependientes del sistema operativo.
Arreglos asociativos superglobales en PHP

Siempre que se va a usar una variable cuyo valor fue provisto por una fuente externa, como un parámetro, debe evitarse que su valor sea ejecutado por el intérprete de JavaScript o genere código (X)HTML/JavaScript, ya que permite a un atacante inyectar código maligno que podría revelar información confidencial de su sitio web u otro tipo de daño. PHP provee varias funciones que convierten código en una representación no ejecutable, como htmlentities() que reemplaza los caracteres especiales (<, >, &) por sus entidades correspondientes (&lt;, &gt;, &amp;).

Ejercicio. Escriba un programa PHP que imprima en tablas (X)HTML el contenido de cada uno de los arreglos superglobales de PHP. Estudie el resultado para encontrar algunos valores que le pueden resultar de utilidad en sus aplicaciones web. Pruebe su programa con algunos parámetros. Si nombra su archivo superglobals.php y lo coloca en la raíz de su sitio web, puede probar con este enlace: http://localhost/superglobals.php?test=hola_mundo.

Objetos

Los objetos en PHP utilizan prácticamente la misma sintaxis de Java, con algunas variaciones de C++. Los objetos son instancias de clases. Una clase se declara con la palabra reservada class seguida por un identificador. Las propiedades (miembros de datos) y métodos de la clase, se deben anteceder con el tipo de acceso que deben tener: private, protected o public. Si se omite, PHP asume public.

Los métodos se declaran como cualquier otra función de PHP, sólo que dentro del cuerpo de la clase. Un método de una clase a diferencia de una función normal, tiene acceso a tres identificadores reservados: $this es una referencia hacia la instancia del objeto al que se le invocó el método, y se utiliza para acceder a sus propiedades y otros métodos. self:: es una referencia hacia la clase misma y sirve para acceder a sus miembros estáticos. parent:: permite acceder a propiedades y métodos de la clase base.

Para instanciar un objeto se utiliza el operador new clase(parametros), enviándole los parámetros que recibe la clase en el constructor. Un constructor es un método con el nombre especial __construct, el cual se encarga de inicializar los miembros de datos del objeto. Aunque se puede utilizar un método con el mismo nombre de la clase como constructor, no se recomienda esta práctica, ya que versiones recientes de PHP lo interpretan como un método normal.

class Player
{
    protected $nickname, $email, $score;

    function __construct($nickname, $email, $score = 0)
    {
        $this->nickname = $nickname;
        $this->email = $email;
        $this->score = $score;
    }

    function increaseScore($by = 1) { $this->score += $by; }
    function getNickname() { return $this->nickname; }
    function getScore() { return $this->score; }
}

$players[] = new Player('pinocho', 'pinocho@cajafishel.com');
$players[] = new Player('chema', 'chema@semeolvido.com');
$players[] = new Player('osqui', 'dumbo@tlc.com');

$bets = rand(30, 100);
echo count($players), " jugadores, $bets apuestas... ";
for ( $i = 0; $i < $bets; ++$i )
{
    $winner = $players[ rand(0, count($players) - 1) ];
    $winner->increaseScore();
}

echo "Resultados:\n";
for ( $i = 0; $i < count($players); ++$i )
    echo '   ', $players[$i]->getNickname(), ': ', $players[$i]->getScore(), "\n";
Ejemplo de clases y objetos en PHP. Correr este ejemplo.

Ejercicio. Escriba un programa PHP que cuente cuántos días feriados "caen" cada día de la semana para el año en curso. Imprima el resultado en una tabla, por ejemplo:.

Feriados de 2011 por día de la semana
Feriados Detalle
Domingo 2 Día del trabajo, Navidad
Lunes 3 Batalla de Rivas, Anexión de Nicoya, Día de la madre
Martes 1 Día de la Virgen de los Ángeles
Miércoles 1 Día de las culturas
Jueves 1 Independencia de Costa Rica
Viernes 0
Sábado 1 Año nuevo

Sugerencia: Represente cada feriado como un objeto, y el conjunto de ellos como un arreglo.