Programación del servidor web: PHP

Un autor que quiera construir un sitio web dinámico deberá instalar un servidor web (como nginx o Apache), 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 a tener que programar un ejecutable. Cada vez que un visitante solicitara una de estas páginas, un intérprete en el servidor web 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. Una página web es dinámica si su contenido, apariencia o comportamiento puede cambiar cada vez que es solicitada por un navegador web. 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.





	
	Hola mundo PHP


	

Hola mundo en PHP

Este párrafo proviene de PHP, no del documento XHTML

\n"); ?> ]]>
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 retornarle 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 tiene que estar 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 enviarlos 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 ... ?>, lo demás el intérprete lo deja intacto. El intérprete ejecuta el código en los scripts de PHP y reemplaza la etiqueta especial por la salida que generó su ejecución. Una vez que el intérprete de PHP ha terminado de ejecutar todos los scripts de PHP en el archivo, el resultado regresa al servidor web quien lo despacha al navegador como una página web normal.

En el caso del listado php_hello_world el script de las líneas 10 a 12 es reemplazado completamente por la salida de la función print() en la línea 11. La línea 1 contiene otro script de PHP, el cual invoca a la función header() que indica al intérprete de PHP agregar la directiva Content-type: application/xhtml+xml al mensaje de respuesta HTTP que se enviará al navegador para que trate el contenido de dicho mensaje como un documento XHTML. Si esto no se hace, se asumirá el tipo MIME por defecto: text/html y el navegador interpretará la página como HTML.

El tipo MIME por defecto es indicado a PHP por la directiva default_mimetype del archivo de configuración php.ini. Si todas o la mayoría de páginas del sitio web que se van a generar con PHP son XHTML se aconseja cambiar el valor de esta directiva a application/xhtml+xml, y utilizar la función header() sólo en aquellos casos excepcionales o cuando no se tienen permisos para modificar el archivo php.ini. Tenga en consideración que la función header() debe invocarse antes de generar cualquier otra salida, por lo que se aconseja hacerlo siempre al inicio del archivo .php. En los ejemplos de este capítulo se asumirá que el MIME type por defecto es XHTML por lo que no se harán más invocaciones a la función header().

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 4.01 (que tengan la declaración XML) 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).

En los listados de código de este capítulo, el lector puede hacer clic en los nombres de las funciones PHP para acceder a la documentación en línea. La mayoría de estas funciones no se explicarán en este texto, sino que se insta al lector a estudiar su propósito en la documentación oficial con el fin de comprender cada ejemplo completo. El php_web_server_date muestra la fecha y hora del servidor web.




	
	Hora y fecha del servidor


	

Hora y fecha del servidor

]]>
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 11), 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 ligeramente más eficiente.

5 pts.

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 multilínea en PHP. Trate de utilizar la menor cantidad de strings posible. La salida de su script debe ser un documento XHTML 5 válido. Debe configurar un servidor web local con PHP para hacer pruebas (revise el capítulo 6).

Las líneas 10 y 11 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 biblioteca de funciones cuya documentación en línea es muy rica, ya que los visitantes aportan contenido útil en ella en forma de comentarios.

El string resultado de invocar a date() en la línea 10 del php_web_server_date es almacenado 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.

10 pts.

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 12 del php_web_server_date imprime un texto resultado de concatenar dos 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, mientras que PHP utiliza el operador punto (.) por influencia de Perl. El ejemplo anterior pudo haberse escrito de otra forma muy común:




	
	Hora y fecha del servidor (2)


	

Hora y fecha del servidor (2)

]]>
La hora del servidor con interpolación de variables. Correr este ejemplo.

El ejemplo del php_variable_interpolation sólo cambia la línea 12 con respecto al php_web_server_date. En la nueva versión se insertaron las variables directamente en el string sin hacer concatenación. 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".

El desarrollador web debe tener clara esta diferencia, ya que JavaScript trata de forma idéntica a las cadenas literales con comillas dobles y simples, mientras que PHP hace interpolación de variables y de secuencias de escape sólo en cadenas literales con comillas dobles y no con comillas simples. 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, y en situaciones donde la interpolación resulta inadecuada se debe utilizar comillas simples.

5 pts.

Pruebe el efecto de cambiar las comillas dobles por simples en el ejemplo del php_variable_interpolation. Guarde una captura de pantalla del resultado que obtuvo en el navegador.

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 reportados 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.

10 pts.

Configure dos ambientes: uno de producción (ej: en el servidor de cursoweb) y otro de desarrollo (su máquina local). Configure el ambiente de producción para silenciar errores de PHP. Configure el ambiente de desarrollo para que reporte al navegador todos los posibles errores y advertencias (warnings). 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 el servidor del curso. Guarde una captura de pantalla de cada ambiente.

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 no puede 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 debe hacerse si se tiene código JavaScript, inspeccionando la consola de JavaScript en el 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. PHP 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 tipos 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:



]]>
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 o las políticas de la empresa.

15 pts.

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 dispone 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. Al igual que en JavaScript, una variable PHP no tiene tipo de datos, sino que puede almacenar valores de cualquiera de esos tipos de datos. Para saber el tipo de datos del valor almacenado en la variable $var invoque la función gettype($var), la cual retorna un string con el nombre del tipo de datos.

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.

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.




   
   Introducción a arreglos


   

Arreglos secuenciales

Arreglos asociativos

 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);
   ?>
]]>
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, de la misma forma que se hace en C, como en el ejemplo del php_explicit_type_cast.

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.




   
   Metodología de pares


   

Lógica de pares

]]>
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 sucesor al valor. Esto se ilustra en el php_array_assign.

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 se provee un valor sin llave, se asume el próximo índice que sigue al último entero utilizado en lugar de la llave (como ocurre con el valor 'vicente' en la línea 2 del php_array_construct). El resultado final del php_array_assign se podría escribir utilizando array() como se aprecia en el php_array_construct.

 6.5, 'ana' => 10.0, 'vicente', 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.




   
   Números factoriales


   

Factoriales

    1 ? $n * factorial($n - 1) : 1; } // Imprime los primeros 20 factoriales incluyendo el 0 for ( $i = 0; $i < 20; ++$i ) echo "
  • $i! = ", factorial($i), "
  • \n"; ?>
]]>
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. Al igual que C++, una referencia se crea antecediendo el operador ampersand (&) al nombre de una variable, de la forma &$var.

El php_byvalue_byreference declara dos funciones idénticas: incrementar_salario_1() recibe sus dos parámetros por copia, mientras que incrementar_salario_2() recibe su primer parámetro por referencia. Ambas intentan aplicar un incremento de salario a todos los empleados recibidos en un arreglo por el primer parámetro. Dado que incrementar_salario_1() recibe una copia del arreglo, hará el ajuste de salario a un arreglo temporal que es descartado al terminar el llamado a la función. Por su parte, incrementar_salario_2() recibe una referencia hacia el arreglo original (línea 9) logrando el efecto deseado para este ejemplo.

 $salario )
      $personal[$persona] = $salario * $incremento;
}

// Ajusta el incremento de salario en el personal
function incrementar_salario_2(&$personal, $incremento)
{
   foreach ( $personal as $persona => $salario )
      $personal[$persona] = $salario * $incremento;
}

// Salario del personal en dolares
$mi_personal = array('eli' => 2000, 'nuria' => 4500, 'lucrecia' => 1600, 'roy' => 3000);
echo "mi_personal = "; print_r($mi_personal);

// Hacer un incremento del 5% en el salario con la función 1
echo "\nincrementar_salario_1(\$mi_personal, 1.05):\n";
incrementar_salario_1($mi_personal, 1.05);
echo "mi_personal = "; print_r($mi_personal);

// Hacer un incremento del 5% en el salario con la función 2
echo "\nincrementar_salario_2(\$mi_personal, 1.05):\n";
incrementar_salario_2($mi_personal, 1.05);
echo "mi_personal = "; print_r($mi_personal);
]]>
El paso de valores por copia o por referencia es explícito en PHP. Correr este ejemplo.
15 pts.

Escriba una función PHP que retorna un arreglo asociativo con los nombres de los usuarios del sistema operativo y la cantidad de bytes que cada uno de ellos está consumiendo en su carpeta personal. Es fácil obtener esta información en la línea de comandos de Linux/Unix. PHP dispone de funciones para invocar comandos del sistema operativo y capturar su salida, como shell_exec() y las comillas invertidas (``), por ejemplo:

Construya otra función PHP que recibe por parámetro una referencia a un arreglo asociativo, y lo imprime en una tabla (X)HTML ordenable. Invoque ambas funciones para generar una tabla que permita a un administrador conocer los usuarios que más espacio consumen. Reutilice su biblioteca JavaScript para ordenar tablas (ejercicio 5.37 y 5.38). Es importante que el ordenamiento se haga en el lado del cliente y no del lado del servidor, con el fin de ahorrar recursos. Si gusta puede ordenar descendentemente por espacio utilizado la tabla con JavaScript al cargar la página.

En la tabla resultado debe aparecer el nombre del usuario (por ejemplo, a54321) y no su carpeta de inicio (/home/a54321). El programador debe ser consciente del poder que le confiere PHP al permitirle invocar cualquier programa en el servidor; y utilizarlo con cuidado. Por ejemplo, se debe evitar invocar programas que consuman muchos recursos, ya que uno o muchos visitantes están esperando una respuesta del servidor. Además se debe evitar brindar información sensible a usuarios que no son de confianza.

Reglas de alcance de variables (variable scope)

Una importante diferencia de 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 23 al tratar de acceder a $count, ya que PHP busca una variable $count local, la cual no fue declarada en ese contexto.




   
   Números factoriales 2


   

Factoriales 2

    1 ? $n * factorial($n - 1) : 1; } function print_factorials() { // Error: $count no esta definida localmente for ( $i = 0; $i < $count; ++$i ) echo "
  • $i! = ", factorial($i), "
  • \n"; } print_factorials(); ?>
]]>
>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 en los cuales las variables globales son accesibles desde cualquier lugar del código. PHP provee varias alternativas. 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 hizo en las líneas 20 y 26 del php_variable_scope_fix1.




   
   Números factoriales 3


   

Factoriales 3

    1 ? $n * factorial($n - 1) : 1; } function print_factorials($count) { for ( $i = 0; $i < $count; ++$i ) echo "\n\t\t
  • $i! = ", factorial($i), "
  • "; } print_factorials($count); ?>
]]>
Pasar variables globales por parámetro a una función. Correr este ejemplo.

Si por alguna razón es imprescindible acceder directamente a las 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 anteponiéndole la palabra reservada global, como se aprecia en la línea 20 del php_variable_scope_fix2. En todo caso, el uso de variables globales se considera una mala práctica de programación.




   
   Números factoriales 4


   

Factoriales 4

    1 ? $n * factorial($n - 1) : 1; } function print_factorials() { // Hace a $count accesible en el contexto local global $count; for ( $i = 0; $i < $count; ++$i ) echo "\n\t\t
  • $i! = ", factorial($i), "
  • "; } print_factorials($count); ?>
]]>
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 y cada uno se explica más adelante en este mismo capítulo.

Arreglo Descripción
$GLOBALS Sirve para acceder a las variables globales definidas en el programa. Es especialmente útil cuando 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 de PHP por parámetro en el URL (método HTTP GET).
$_POST Aloja las variables pasadas al script de PHP 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 htmlspecialchars() que reemplaza los caracteres especiales (<, >, &) por sus entidades correspondientes (&lt;, &gt;, &amp;). Estas funciones se estudiarán más adelante (ver php_sanitize_functions).