Está en la página 1de 44

Curso Avanzado de PHP

Unidad 2: Funciones avanzadas de PHP

Unidad 2. Funciones avanzadas de PHP

Introducción
En esta Unidad vamos a crear, leer y modificar documentos XML utilizando
las bibliotecas de PHP.

Además, aprenderás a tratar archivos de Microsoft Office en Windows


mediante la biblioteca COM y DOTNET de PHP.

Después, conocerás las funciones de cifrado para cifrar documentos.


Aprenderás a utilizar la biblioteca mcrypt de PHP para abrir el módulo del
algoritmo que se vaya a aplicar, inicializar el generador de números aleatorios,
crear un vector de inicialización que sirva de semilla para iniciar el proceso de
cifrado, inicializar los buffers necesarios para la encriptación y cifrar el texto
dado con la clave proporcionada.

Más tarde estudiaremos las funciones de control de salida guardando la


información en un buffer hasta que se quiera enviar esta al navegador del
usuario.

Finalmente, veremos cómo implementar la autentificación de usuarios de


forma segura.

2.1. Sobre el formato XML


XML (eXtensible Markup Language) es un lenguaje de etiquetas como HTML.
Esto significa que consiste en una serie de elementos o "tags" que engloban
información. La principal diferencia respecto a lenguaje HTML es que no está
orientado a la presentación de datos; es decir, XML no define las etiquetas ni
cómo utilizarlas; sólo define unas cuantas reglas sintácticas para crear
documentos. Por eso, XML es un metalenguaje (un lenguaje para definir otros
lenguajes).

El lenguaje XML se utiliza para poder separar contenido y presentación en los


sitios web de manera que los mismos datos se puedan mostrar de varias
formas distintas sin demasiado esfuerzo. Además, también se usa para el
intercambio de información entre sitios web.

1
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Las características principales de XML son:

• XML es un lenguaje de etiquetas, como HTML.


• XML está diseñado para transportar información, no para mostrarla.
• XML no tiene etiquetas predefinidas. Puedes definir las tuyas.
• XML está diseñado para ser auto-explicativo.
• XML es un estándar del consorcio W3C.
• XML no hace nada por sí solo, es un metalenguaje.

Este formato es un estándar definido por el consorcio de la "World Wide Web"


(W3C). Puedes obtener más información sobre las características del lenguaje
XML en la dirección http://www.w3.org/XML/.

Para definir un documento XML, la primera línea debe ser:

<?xml version=” 1.0” encoding=”utf-8” ?>

Los elementos o propiedades XML se definen así:

<mensaje>Texto de ejemplo</mensaje>

El documento completo quedaría así:

<?xml version=" 1.0" encoding="utf-8" ?>


<mensaje>Texto de ejemplo</mensaje>

Hay que tener en cuenta que XML distingue mayúsculas y minúsculas (es
case-sensitive). Por lo tanto, el elemento siguiente es diferente:

<Mensaje>Texto de ejemplo</Mensaje>

Elementos XML

El nombre de un elemento tiene las siguientes características:


• Puede contener letras, números, puntos, dos puntos y tildes, es decir,
cualquier carácter del juego UTF-8.
• Sólo puede comenzar por letras.
• No puede empezar con las letras XML, xml, Xml, etcétera.
• No puede contener espacios en blanco.
• Todas las etiquetas tienen que estar debidamente cerradas o, si no, hay
que escribir: <mensaje />.

2
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Cualquier documento XML tiene un único elemento raíz:

<?xml version=” 1.0” ?>


<colección>
<película>
Avatar
</película>
<película>
El resplandor
</película>
</colección>

La siguiente definición sería incorrecta al tener 2 elementos raíz:

<?xml version=” 1.0” ?>


<película>
Avatar
</película>
<película>
El resplandor
</película>

Los elementos deben estar correctamente anidados:

<colección>
<película>
<directores>
<director>James Cameron</director>
</directores>
</película>
</colección>

Atributos XML

Propiedad de un elemento utilizada para almacenar metainformación de este


elemento:

<mensaje lang=”es”>Texto de ejemplo</mensaje>

Como en PHP, podemos usar comillas tanto simples como dobles:

<mensaje lang='es' >Texto de ejemplo</mensaje>

Comentarios

Los comentarios en XML tienen el siguiente formato:

<!--- Esto es un comentario --->


<!-- Otro comentario -->

3
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

XML válidos y bien formados

Los documentos XML deben mantener una estructura correcta cumpliendo con
las reglas del W3C. Puedes usar la siguiente página para comprobar que un
documento XML es correcto según el estándar:

http://www.w3schools.com/X ML/xml_validator.asp

El ecosistema XML

Además, dentro del lenguaje XML podemos encontrar los siguientes


componentes:

• DTD: Document Type Definition (en español "definición de tipo de


documento") es un archivo que define los tipos de elementos, atributos y
entidades permitidas en un documento XML y también puede expresar
algunas limitaciones para combinarlos.
• Un Schema XML es similar a un DTD. Define que elementos puede
contener un documento XML, como están organizados y que atributos y
de que tipo pueden ser sus elementos. Ventajas de los Schemas XML
frente a los DTD: usan sintaxis de XML (no DTD), permiten especificar
los tipos de datos y son extensibles.
• Namespaces: el uso de un Schema XML se basa en espacios de
nombres (namespaces). Un espacio de nombres XML es una
recomendación W3C para proporcionar elementos y atributos con
nombre único en un archivo XML.
• XForms: nuevo lenguaje de etiquetado para formularios Web diseñado
para ser el sustituto de los formularios tradicionales HTML. Permite a los
desarrolladores de formularios Web distinguir entre el propósito del
formulario y su presentación. Así se consigue una separación clara entre
contenido y presentación.
• XPath: del inglés XML Path Language, es un lenguaje que permite
construir expresiones que recorren y procesan un documento XML.
Permite seleccionar partes de un texto sin atributos.

4
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

• XSLT o Transformaciones XSL: estándar de W3C que se usa para


transformar documentos XML en otros e incluso a formatos que no son
XML.

2.2. PHP y XML


PHP también dispone de bibliotecas que pueden tratar documentos XML. Con
los años, PHP ha simplificado mucho el uso de XML al evolucionar sus
versiones. Entre las bibliotecas más destacadas disponemos de: DOM y
SimpleXML, que son las que vamos a estudiar en este curso.

Veamos sus características principales:

SimpleXml

• Biblioteca pensada para aplicaciones sencillas de XML.


• La API tiene limitaciones a la hora de trabajar con los elementos.
• Todos los elementos son iguales.
• Se accede a los elementos de un documento XML como si se tratara un
objeto. Por ejemplo, $raiz->elemento['atributo'].
• No permite añadir comentarios en los documentos XML.

DOM

• Diseñada para cualquier aplicación que necesite XML.


• Es una implementación de la API oficial del consorcio W3C por lo que no
tiene limitaciones a la hora de llevar a cabo un desarrollo complejo.
• Distingue entre tipos de elementos.
• Permite cargar archivos HTML que no estén bien conformados.
• Permite realizar consultas XPath dentro del documento XML.

Nota: ambas bibliotecas están basadas en libxml y, como veremos más


adelante, es muy sencillo mezclar código de ambas bibliotecas sin
problemas en un mismo script PHP.

Depende un poco del proyecto que estemos desarrollando elegir una u otra
biblioteca. DOM permite acceder y manipular los documentos XML de forma
más completa; sin embargo, la curva de aprendizaje es más pronunciada ya
que la API es algo más compleja si la comparamos con SimpleXML, que nos
permite tratar documentos XML de una manera sencilla e intuitiva, como bien
dice su nombre.

5
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Lectura de archivos XML


A continuación, vamos a estudiar de forma práctica las funciones de ambas
bibliotecas PHP en el Ejemplo 1 de esta Unidad para interpretar la información
de un fichero de tipo XML y mostrarla en una página web, en formato HTML.
Veamos qué funciones hemos usado y cuál es su finalidad. Si abres el código
fuente de este proyecto verás que el archivo index.php contiene las siguientes
sentencias:

<?php

$filename = "discos.xml";
echo "<p>Leemos los datos del fichero: $filename";

/***Creamos objeto DOM ***/


$dom = new DOMDocument;
/*** Leemos el archivop XML ***/
$dom->load( $filename );
/*** Indicamos que se importe el archivo XML en formato SimpleXML ***/
$musica = simplexml_import_dom($dom);

// Si comentas las 3 sentencias anteriores y descomentas las siguientes, se


utilizará la biblioteca SimpleXML para leer el fichero en lugar de la DOM.
/*if( ! $musica = simplexml_load_file($filename) )
{
die ("ERROR: no se puede abrir el archivo XML: $filename.");
} */
// Obtenemos el nº de elementos principales
$n_discos = sizeof($musica);
echo "<P>Se ha leído $n_discos registros.<HR>";

for ($i=0;$i<$n_discos; $i++) {


// Vamos mostrando la información de cada elemento
echo "Título: <B>".$musica->disco[$i]->titulo."</B>";
echo "<BR>Intérprete: <B>".$musica->disco[$i]->interprete . "</B>";
echo "<BR>Estilo: <B>".$musica->disco[$i]->estilo . "</B>";
$n_canciones = sizeof($musica->disco[$i]->canciones->cancion);
echo "<BR>Nº canciones: <B>".$n_canciones. "</B>";
for ($j=0;$j<$n_canciones; $j++) {
// Obtenemos los atributos de la canción $j
echo "<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;".
$musica->disco[$i]->canciones->cancion[$j]->attributes()
.". <B>". $musica->disco[$i]->canciones->cancion[$j].
"</B>";
}
echo "<BR>Comentarios: <B>".$musica->disco[$i]->comentarios . "</B>";
echo "<HR>";
}

/*
Si descomentas estas sentencias, puedes ver el xml en crudo
echo "<PRE>";
foreach( $musica as $elemento )
{
print_r( $elemento );
}
*/
?>

6
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Veamos, a continuación, las funciones que hemos utilizado:

1. DOMDocument crea un objeto DOM analizador de XML. Representa un


documento HTML o XML en su totalidad y sirve como raíz del árbol de
documento.

2. DOMDocument::load($nombre, $opciones) carga un archivo o URL del tipo


XML o HTML indicado en $nombre. El parámetro $opciones sirve para indicar a
la biblioteca las opciones que debe aplicar en la carga de este fichero, por
ejemplo, qué hacer cuando haya errores en la carga del archivo XML.

3. SimpleXMLElement representa un elemento en un documento XML en forma


de un objeto de PHP. Por ejemplo, para acceder al elemento “interprete” del
“disco” 1 del documento XML siguiente:

Podemos escribir: $musica->disco[$i]->interprete;

4. SimpleXMLElement simplexml_import_dom($dom) toma como parámetro un


nodo de un documento DOM y lo convierte en un nodo SimpleXML. A partir de
este momento, el nuevo objeto se puede usar como un elemento SimpleXML
nativo. Es decir, estamos cambiando de biblioteca DOM a SimpleXML.

5. SimpleXMLElement::count es un método que cuenta el número de elementos


hijos de un elemento. Se puede utilizar también la sentencia
sizeof($elemento) directamente para obtener este número de elementos hijos.

6. simplexml_load_file($nombre) interpreta un fichero o URL XML en un


objeto SimpleXMLElement.

7. SimpleXMLElement::attributes es la función que permite acceder a los


atributos y valores definidos dentro de una etiqueta XML.

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 1 (Lectura de un


documento de tipo XML) de la Unidad 2. Estudia el código fuente y
ejecútalo para mostrar en el navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 1 de la Unidad 2 verás que aparece la siguiente página:

7
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Además, estas funciones existen múltiples funciones y métodos que nos


pueden ayudar a la hora de trabajar con el formato XML.

Algunas de las más importantes de SimpleXML son:

• SimpleXMLElement::children busca los hijos del nodo indicado.


• SimpleXMLElement::asXMLdevuelve en una cadena (string) el documento
XML. Permite además almacenar el documento XML.

En el caso de la biblioteca DOM verás que existen muchos más métodos


disponibles para el programador:

• DOMDocument::getElementById busca un elemento con cierto id.


• DOMDocument::getElementsByTagName busca todos los elementos con el
nombre de etiqueta indicada.
• DOMDocument::importNode importa un nodo dentro del documento actual.
• DOMDocument::loadHTML Carga un documento HTML almacenado en una
cadena.
• DOMDocument::loadHTMLFile carga un documento HTML desde un
fichero.
• DOMDocument::normalizeDocument normaliza el documento XML o HTML.
Es decir, lo arregla si le falta alguna etiqueta.
• DOMDocument::validate valida el documento basado en su DTD.
• DOMElement::getAttribute devuelve el valor de un atributo.
• DOMElement::getAttributeNode devuelve el nodo de un atributo.
• DOMElement::getElementsByTagName obtiene los elementos por el
nombre de una etiqueta.

8
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

• DOMElement::hasAttribute indica si existe un atributo.


• DOMNode::hasChildNodes comprueba si el nodo tiene hijos.

Atención: no dudes en utilizar la ayuda de PHP para ver con ejemplos cómo
se usan éstas y otras funciones disponibles en ambas bibliotecas.

Escritura y búsqueda de archivos XML


Ahora vamos a estudiar de forma práctica las funciones de ambas bibliotecas
PHP en el Ejemplo 2 de esta Unidad para buscar y guardar información en
formato XML. Veamos qué funciones hemos usado y cuál es su finalidad.

En este Ejemplo se exportan datos de una base de datos MySQL a un


documento de tipo XML. Luego, se lee éste, se buscan algunos elementos y se
interpreta mandando el resultado a una página web.

Si abres el código fuente de este proyecto verás que el archivo index.php


contiene las siguientes sentencias interesantes:

try
{
/***Creamos objeto DOM ***/
$dom = new domDocument;
/*** Indicamos que el formato de salida debe ir tabulado ***/
$dom->formatOutput = true;
/*** Codificación UTF-8 ***/
$dom->encoding='utf-8';

/*** Creamos el elemento raíz del XML ***/


$raiz = $dom->appendChild($dom->createElement("agenda"));
/*** Creamos un objeto SimpleXMLElement de un nodo DOM ***/
$simplexml = simplexml_import_dom($dom);
// Obtenemos los datos de las encuestas
$resultado = ejecuta_SQL("SELECT * FROM agenda");
foreach ($resultado as $valor)
{
/*** Añadimos el contacto ***/
$contacto = $simplexml->addchild("contacto");
/*** Indicamos el nommbre del contacto ***/
$contacto->addChild("nombre", $valor['Nombre']);
/*** Apellidos ***/
$contacto->addChild("apellidos", $valor['Apellidos']);
/*** Dirección postal ***/
$contacto->addChild("dirección", $valor['direccion']);
/*** Localidad ***/
$contacto->addChild("localidad", $valor['localidad']);
/*** Provincia ***/
$contacto->addChild("provincia", $valor['provincia']);
/*** Código postal ***/
$contacto->addChild("cod_postal", $valor['codigo_postal']);
/*** Teléfonos ***/
$telefonos = $contacto->addChild("teléfonos");
/*** Teléfono 1 ***/

9
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

$telefono = $telefonos->addChild("teléfono",
$valor['Telefono_movil']);
/*** Atributo teléfono 1 ***/
$telefono->addAttribute("tipo", "móvil");
/*** Teléfono 2 ***/
$telefono = $telefonos->addChild("teléfono",
$valor['Telefono_oficina']);
/*** Atributo teléfono 2 ***/
$telefono->addAttribute("tipo", "oficina");
/*** Teléfono 3 ***/
$telefono = $telefonos->addChild("teléfono",
$valor['telefono']);
/*** Atributo teléfono 3 ***/
$telefono->addAttribute("tipo", "fijo");
/*** Correo electrónico ***/
$contacto->addChild("email", $valor['email']);
/*** Notas ***/
$contacto->addChild("notas", $valor['notas']);
}
$db=0;
/*** Guardamos el archivo xml ***/
$dom->save($filename);
}
catch( Exception $e )
{
echo "ERROR: al crear el documento XML: ". $e->getMessage();
}

// Leemos los datos ahora


if( ! $agenda = simplexml_load_file($filename) )
{
die ("ERROR: no se puede abrir el archivo XML: $filename.");
}
// Nº de contactos
$n_contactos = sizeof($agenda);
echo "<P>Se ha leído $n_contactos contactos.<HR>";

echo "<B>Buscamos sólo los nombres </B><PRE>";


$resultado=$agenda->xpath("/agenda/contacto/nombre");
/*** Esta sentencia es similar a la anterior, se busca 2 etiquetas cualquier
primero y luego la etiqueta nombre ***/
//$resultado=$agenda->xpath("//nombre");
foreach($resultado as $elemento)
{
echo"\n$elemento";
}

echo "</PRE><HR><B>Mostramos únicamente el contacto 'Jaime'</B><PRE>";


// Buscamos el registro para el nombre=Jaime

print_r($agenda->xpath("//*[nombre='Jaime']"));

Nota: no mostramos el script completo ya que el alumno o alumna puede


abrirlo para estudiarlo en detalle. En este ejemplo hemos utilizado PDO para
acceder a la base de datos MySQL, crea la tabla (si es necesario) y realiza las
consultas correspondientes.

10
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Veamos, a continuación, las funciones que hemos utilizado:

1. DOMDocument::formatOutput propiedad que indica que se debe dar formato


a la salida con indentación y espacios extra.

2. DOMDocument::encoding propiedad para indicar el juego de caracteres


utilizado en el documento.

3. DOMNode::appendChild método que añade un nuevo elemento hijo al final de


los hijos.

4. SimpleXMLElement::addChild método que incluye un elemento hijo al nodo


indicado.

5. SimpleXMLElement::addAttribute método que define un atributo al elemento


SimpleXML.

6. DOMDocument::save guarda en un archivo el documento XML.

7. SimpleXMLElement::xpath ejecuta una petición XPath sobre los datos XML


para realizar búsquedas.

Si tenemos en cuenta la estructura del documento XML que genera el Ejemplo


2 de esta Unidad:

Podemos hacer las siguientes búsquedas:

• xpath("/agenda/contacto/nombre"): busca todos los elementos “nombre”


que dependan del árbol “agenda->contacto”. La sintaxis básica de XPath
es similar a la que utilizamos en el sistema de ficheros. Un camino que
se inicia con '/', representa un camino absoluto hacia el elemento
requerido.

11
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

• xpath("//nombre"): busca todos los elementos “nombre” descendientes.


“//” significa que se deben seleccionar TODOS los elementos que
desciendan del conjunto de elementos, es decir, no sólo los primeros
hijos, sino también los hijos de los hijos, etcétera. Por lo tanto, el
resultado será similar a la búsqueda anterior, pero el elemento podría
ser de cualquier nivel.
• xpath("//*[nombre='Jaime']"): busca todos los elementos cuyo nombre
sea “Jaime”. El asterisco '*' selecciona todos los elementos ubicados por
el camino que lo precede.
• xpath("/agenda/contacto/teléfonos/teléfono[@tipo='móvil']"): busca
todos los elementos cuyo atributo tipo sea “móvil”. Una expresión entre
corchetes permite precisar la especificación de un elemento. Un número
dentro de los corchetes representa la posición del elemento en el
conjunto seleccionado. La función last() selecciona el último elemento
en la selección. Además, los atributos se especifican con el símbolo
prefijo '@'.
• xpath("//teléfono/.."): Seleccionar todos los nodos que tienen algún
hijo de tipo “teléfono”. De igual formas que en los sistemas de ficheros,
se utilizan los dos puntos para identificarlo.

La sintaxis de XPath es muy completa y permite realizar búsquedas


complejas de forma sencilla e intuitiva. Animamos a los alumnos y alumnas
a que busquen en Internet más ejemplos sobre su utilización. El objetivo de
este apartado es mostrar cómo se usa en combinación con PHP.

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 2 (Escritura de


un documento de tipo XML) de la Unidad 2. Estudia el código fuente y
ejecútalo para mostrar en el navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 2 de la Unidad 2 verás que aparece la siguiente página:

12
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Además, existen múltiples funciones y métodos que nos pueden ayudar a la


hora de crear un documento XML con la biblioteca DOM:

• DOMDocument::createAttribute crea un nuevo atributo en un nodo.


• DOMDocument::createComment crea un nuevo comentario.
• DOMDocument::createElement crea un nuevo nodo elemento.
• DOMDocument::createTextNode crea un nuevo nodo de texto.

Más adelante, en la Unidad 5 veremos cómo utilizar el formato XML en un


servidor SOAP.

13
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

2.3. COM y DOTNET


En el tratamiento de páginas y sitios web desde PHP es posible acceder a
otros objetos y componentes de Windows. Para ello, hay que hacer uso de las
funciones COM (Component Object Model) y DOTNET (.NET), que tienen
como finalidad servir de intermediarias entre los programas en sistemas
operativos Microsoft. PHP está implementado para poder acceder a objetos y
componentes de Windows.

En esta segunda Unidad del Curso avanzado de PHP 5 vamos a explicar estas
funciones y las aplicaremos en algunos de los ejemplos que integran este
segundo bloque del curso.

ATENCIÓN: esta biblioteca sólo se puede utilizar en un servidor de tipo


Windows. Los alumnos o alumnas que trabajen en otro sistema operativo
pueden leer igualmente este apartado y ver cómo se utilizaría esta biblioteca.
Además, para poder ver los ejemplos del curso de este apartado deberás tener
instalado Office en tu ordenador.

Las funciones COM son una vía que permite identificar e intercambiar
información entre objetos y componentes de diferentes sistemas, arquitecturas,
lenguajes y máquinas. COM es el modelo más usado para conectar software
de diferentes fabricantes. Proporciona un amplio conjunto de herramientas
cliente-servidor de servicios integrados, fáciles de usar y eficientes en la
conexión de los componentes de variados lenguajes.

En esta Unidad vamos a acceder desde el lenguaje PHP a dos programas de


Microsoft: al procesador de texto Word y a la hoja de cálculo Excel.
Lógicamente en nuestra máquina deben estar instalados también estos dos
programas.

Para obtener más información sobre lo que se puede hacer con las clases y los
objetos mediante las funciones COM, aconsejamos acceder a la dirección
http://www.microsoft.com/com/ .

Veamos, a continuación, las principales funciones COM y cómo se aplican


dentro del código PHP.

Antes de poder trabajar con esta biblioteca en XAMPP es necesario habilitarla


en PHP. Para ello, abrimos el Panel de Control de XAMPP y pulsamos en las
siguientes opciones para editar el archivo PHP.INI:

14
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Una vez abierto el archivo de configuración de PHP con el editor de texto,


debemos añadir la siguiente línea: extension=php_com_dotnet.dll. Tal y como
se observa en la siguiente imagen:

Ahora reiniciamos el servidor Apache y pulsando en el botón “Admin” del Panel


de Control de XAMPP hacemos clic en el enlace “phpinfo()” del navegador para
ver que, efectivamente, la biblioteca ya está disponible:

15
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Ahora ya puedes empezar a trabajar con esta biblioteca tan versátil.

En el Manual de PHP se describen las funciones COM en el sistema operativo


Windows. No dudes en acceder a esta página para ampliar conocimientos
sobre este apartado.

Lo primero que hacemos al integrar las funciones COM dentro de los scripts
PHP es comprobar que la biblioteca está cargada en el servidor PHP
correctamente escribiendo las siguientes sentencias:

if (!extension_loaded('com_dotnet')) {
die ("ERROR: la biblioteca COM_DOTNET no está habilitada en este
servidor PHP. No se puede continuar la ejecución.");
}

Después, tenemos que utilizar la clase COM de esta biblioteca. Es decir, dentro
de PHP, COM se comporta como una clase de la que es preciso siempre hacer
al menos una instancia creando un objeto. Así pues, para poder usar las
funciones que vamos a explicar a continuación, es necesario crear un objeto
con la sentencia new COM. En el Ejemplo 3, donde accedemos desde PHP a MS
Word, usamos esta instrucción para crear el objeto:

$word = new COM("word.application")


or die("No se puede instanciar el Word");

Como parámetro obligatorio de la clase COM debe indicarse el nombre del


módulo al que vamos a acceder, en este caso la aplicación Word.
Opcionalmente, pueden indicarse también, separados por comas, el nombre
del servidor desde el cual será instanciada la clase y el código de página que
debe utilizarse para hacer compatibles las cadenas entre las dos aplicaciones,
PHP y Word en este ejemplo. En los ejemplos y ejercicios del curso no
hacemos uso de estos dos últimos.

En el Ejemplo 4 accedemos desde PHP a MS Excel creando así el objeto:

$excel = new COM("Excel.sheet")


or die("No se puede instanciar el Excel");

16
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Una vez creado el objeto, ya podemos hacer referencia al mismo a través de


los métodos y propiedades definidos para éste dentro de la clase COM. Los
métodos y propiedades de cada módulo de la clase COM pueden hallarse en el
lenguaje del propio módulo. En la página citada al principio de este apartado
puede obtenerse información abundante sobre este tema, además de la
referencia a algunos libros sobre los módulos COM.

Siguiendo con el Ejemplo 3, una vez que hemos creado el objeto $word,
podemos llevar a cabo estas operaciones:

1. Hacer visible la ventana de Word usando la propiedad Visible

$word->Visible = 1;

2. Abrir un documento nuevo de Word con el método Add()

$word->Documents->Add();

3. Establecer el tamaño de la fuente

$word->Selection->Font->size=16;

4. Fijar la letra negrita

$word->Selection->Font->bold=true;

5. Escribir un texto

$word->Selection->TypeText("Esto es una prueba...");

6. Guardar el documento

$word->Documents[1]->SaveAs($nombre_fichero);

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 3 (Biblioteca


COM - Word) de la Unidad 2. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 3 de la Unidad 2 verás que aparece la siguiente página


y que se ejecuta MS Word:

17
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Siguiendo con el Ejemplo 4, una vez que hemos creado el objeto $excel,
podemos llevar a cabo estas operaciones:

1. Desactivamos los avisos de Excel usando la propiedad DisplayAlerts

$excel->Application->DisplayAlerts = False;

3. Abrimos el documento Excel incluido en el proyecto con el método


Open()

$excel->application->Workbooks->Open($nombre_fichero);

4. Creamos una copia del documento original para no perderlo

$excel->Application->ActiveWorkbook->SaveAs($nombre_fichero_copia);

5. Hacemos visible MS Excel

$excel->Application->Visible = True;

18
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

6. Hacemos operaciones sobre las hojas

//Abrimos la hoja del excel "Hoja de Prueba".

$hojas = $wkb->Worksheets($nombre_hoja); //Seleccionamos la hoja.


$hojas->activate; //Activamos la hoja.
$celda = $hojas->Cells(2,1) ; // Seleccionamos la celda A2.
$celda->activate; // Activamos la celda.
// Mostramos el valor de la celda.
echo "Valor de la celda = {$celda->value} <BR>";
// Cambiamos el valor de la celda a 55555.
$celda->value = 55555;
// Mostramos el nuevo valor.
echo "Nuevo valor de la celda = {$celda->value}<BR> ";

// Recalculamos las fórmulas de la hoja entera.


$hojas->Calculate;

// Obtenemos el resultado de la celda C3.


$celda = $hojas->Cells(3,3);
$numero = number_format($celda->value, 0, ',', '.');
echo "Suma total = $numero.<BR>";

// Ejemplo del funcionamiento de una función incluida en el excel.


// Función: PMT (porcentaje interés/12 meses,Nº de pagos,Cantidad
// prestada).
$pago_mensual = $excel->application->pmt(0.08/12, 10, 10000);

7. Guardamos el documento

$excel->Application->ActiveWorkbook->SaveAs($nombre_fichero_copia);

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 4 (Biblioteca


COM - Excel) de la Unidad 2. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 4 de la Unidad 2 verás que aparece la siguiente página


y que se ejecuta MS Excel:

19
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 5 (Biblioteca


COM - Conversor HTML a Word) de la Unidad 2. Estudia el código fuente
y ejecútalo para mostrar en el navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 5 de la Unidad 2 verás que aparece el siguiente


programa que convierte páginas HTML en documentos Word:

20
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Si seleccionas las páginas HTML adjuntas al proyecto PHP y pulsas el botón


“Convertir->” verás que el conversor modifica su formato y permite descargarlas
mediante un enlace:

En este ejemplo permitimos al usuario que seleccione varios ficheros al mismo


tiempo que se subirán al servidor y se convertirán al formato Word clásico
(.doc) mediante la etiqueta HTML siguiente:

<input name="ficheros[]" type="file" multiple>

A día de hoy todos los navegadores de Internet soportan este tipo de etiqueta.

Además, en las sentencias que tratan los ficheros subidos al servidor se lleva a
cabo una validación de su tamaño y formato antes de iniciar la conversión a
Word.

Atención: la primera ver que ejecutes este Ejemplo puede ocurrir que Office
solicite la instalación del conversor correspondiente:

Debes seguir las instrucciones del instalador para poder ver cómo funciona
esta programa conversor.

21
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Es conveniente que el alumno o alumna estudie bien los Ejemplos 3, 4 y 5,


donde puede ver el código y las páginas que permiten mostrar estas funciones
dentro de los respectivos scripts de PHP.

El problema aquí se plantea cuando hay que identificar el nombre de las


aplicaciones que debemos poner como parámetro de la clase COM. Hemos
visto que para acceder al módulo MS Word hemos puesto "Word.Application".
También hubiera sido válido poner "Word.Application.15" para indicar la versión
15 de Word del paquete Office 2013. En el caso de MS Excel hemos puesto
"Excel.Sheet". También hubiera sido válido poner "Excel.Application". Si
hubiéramos querido acceder a ADOBE Acrobat, deberíamos haber puesto
"Exch.Application" o "PdfDistiller.PdfDistiller".

¿Cómo podemos saber qué identificador debemos usar para referirnos a cada
aplicación? No siempre es fácil. Lo más directo es buscar en nuestro ordenador
el documento de ayuda “Referencia de Microsoft Forms Visual Basic”, que
suele estar en alguna de las carpetas de MS. Por ejemplo, en nuestro
ordenador, que tiene instalada la versión 7 de Windows, en la carpeta
C:\Program Files\Microsoft Office 15\root\vfs\ProgramFilesCommonX86\
Microsoft Shared\VBA\VBA7.1\3082 está el fichero FM20.CHM. En éste y en
otros muchos documentos de ayuda que tienen la extensión CHM pueden
encontrarse muchas de las informaciones necesarias para poder hacer
referencia a los nombres de los módulos, propiedades, métodos y eventos de
cada aplicación.

Otra forma de saber cómo se escribe el nombre de la aplicación a la que


queremos acceder es mirar en el registro de Windows (Inicio / Ejecutar regedit)
dentro de la carpeta HKEY_CLASSES_ROOT. Al final de las carpetas con
nombres de extensiones aparecen otras con el nombre de las aplicaciones
instaladas en nuestro ordenador. Los identificadores de aplicaciones
disponibles son los de las carpetas que tienen subcarpetas con el nombre
CLSID.

Para conocer los nombres de los eventos, métodos y propiedades de las


aplicaciones que integran Office, hay que arrancar la aplicación en primer
lugar; luego, es preciso abrir el Editor de Visual Basic con la opción adecuada
o pulsando a la vez las teclas ALT+F11 y seleccionar el Examinador de
Objetos con F2. Escribiendo el nombre del método o de la propiedad en la
ventanita superior o buscando en la lista, seleccionamos una clase o miembro
de la misma y con el botón derecho sobre su nombre accedemos a otra
ventana donde podemos pedir ayuda, entre otras posibilidades, sobre el
elemento seleccionado:

22
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

También puede obtenerse información en la página de Microsoft MSDN. Por


ejemplo, para obtener información sobre MS Excel podemos acceder a la
dirección siguiente:

http://msdn.microsoft.com/library/officedev/off2000/xltocobjectmodelapplication.htm

23
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

2.4. Cifrado en PHP


Un proceso de cifrado completo crea el mensaje cifrado C, a partir del mensaje
en claro M y de la "clave de cifrado"; realiza el proceso inverso (descifrado) y,
así, determina M a partir del mensaje cifrado y la "clave de descifrado”. Fíjate
en el siguiente esquema sencillo:

PHP utiliza la biblioteca mcrypt para cifrar datos. Esta biblioteca soporta una
gran variedad de algoritmos de bloque como DES, TripleDES, Blowfish (por
defecto), 3-WAY, SAFER-SK64, SAFER-SK128, TWOFISH, TEA, RC2 y GOST
en los modos de cifrado CBC, OFB, CFB y ECB. Adicionalmente, soporta RC6
e IDEA, que son algoritmos "no-libres".

Mcrypt puede operar en cuatro modos de cifrado:

1. ECB (Electronic Code Book o libro de código electrónico), que es


adecuado para cifrar datos aleatorios y cadenas no muy largas, como
claves.

2. CBC (Cipher Block Chaining o cifrado en bloque encadenado), que es


muy adecuado para cifrar ficheros.

3. CFB (Cipher Feed Back o cifrado realimentado), que es la forma más


adecuada de cifrar paquetes de bytes uno a uno.

4. OFB (Output Feed Back o salida realimentada), que es similar al modo


CFB, si bien es más adecuado para cifrar el código de aplicaciones
informáticas, ya que en éstas la aparición de errores debe evitarse.
5. NOFB (Output Feed Back en n bits) es similar a OFB, pero más seguro
al operar en bloques de datos.
6. STREAM es un modo extra para incluir algunos algoritmos de flujo tales
como "WAKE" o "RC4".

En general, se puede acceder al cifrado desde PHP con la función


MCRYPT_nombredelcifrado.

A continuación citamos los principales cifrados que están soportados


actualmente por la extensión mcrypt:

24
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

• MCRYPT_3DES
• MCRYPT_ARCFOUR_IV (solo mcrypt > 2.4.x)
• MCRYPT_ARCFOUR (solo mcrypt > 2.4.x)
• MCRYPT_BLOWFISH
• MCRYPT_CAST_128
• MCRYPT_CAST_256
• MCRYPT_CRYPT
• MCRYPT_DES
• MCRYPT_DES_COMPAT (solo mcrypt 2.2.x)
• MCRYPT_ENIGMA (solo mcrypt > 2.4.x, alias de MCRYPT_CRYPT)
• MCRYPT_GOST
• MCRYPT_IDEA (no libre)
• MCRYPT_LOKI97 (solo mcrypt > 2.4.x)
• MCRYPT_MARS (solo mcrypt > 2.4.x, no libre)
• MCRYPT_PANAMA (mcrypt > 2.4.x only)
• MCRYPT_RIJNDAEL_128 (solo mcrypt > 2.4.x)
• MCRYPT_RIJNDAEL_192 (solo mcrypt > 2.4.x)
• MCRYPT_RIJNDAEL_256 (solo mcrypt > 2.4.x)
• MCRYPT_RC2
• MCRYPT_RC4 (solo mcrypt 2.2.x)
• MCRYPT_RC6 (solo mcrypt > 2.4.x)
• MCRYPT_RC6_128 (solo mcrypt 2.2.x)
• MCRYPT_RC6_192 (solo mcrypt 2.2.x)
• MCRYPT_RC6_256 (solo mcrypt 2.2.x)
• MCRYPT_SAFER64
• MCRYPT_SAFER128
• MCRYPT_SAFERPLUS (solo mcrypt > 2.4.x)
• MCRYPT_SERPENT (solo mcrypt > 2.4.x)
• MCRYPT_SERPENT_128 (solo mcrypt 2.2.x)
• MCRYPT_SERPENT_192 (solo mcrypt 2.2.x)
• MCRYPT_SERPENT_256 (solo mcrypt 2.2.x)
• MCRYPT_SKIPJACK (solo mcrypt > 2.4.x)
• MCRYPT_TEAN (solo mcrypt 2.2.x)
• MCRYPT_THREEWAY
• MCRYPT_TRIPLEDES (solo mcrypt > 2.4.x)
• MCRYPT_TWOFISH MCRYPT_TWOFISH128
• MCRYPT_TWOFISH192
• MCRYPT_TWOFISH256
• MCRYPT_WAKE (solo mcrypt > 2.4.x)
• MCRYPT_XTEA (solo mcrypt > 2.4.x)

Algunos de estos cifrados, como los modos CFB, OFB o CBC, exigen llevar un
vector de inicialización (IV) en la correspondiente función de cifrado. El IV debe
ser único y el mismo para cifrar y descifrar.

En el apartado Funciones de Cifrado Mcrypt del Manual de PHP puede


hallarse más información sobre los tipos de cifrados soportados hasta la fecha
por la extensión mcrypt, así como sobre las funciones más importantes para
cifrar o descifrar usando esta biblioteca.

25
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

En el Ejemplo 6 de esta Unidad hemos usado el cifrado AES 256, que fue
diseñado en 2000 por el NIST norteamericano y se utiliza hoy en día por la
seguridad que ofrece. Además, notamos que los sistemas de clave simétrica
son, en general, más rápidos que los de clave asimétrica.

Veamos ahora las funciones de cifrado usadas en el Ejemplo 6:

• mcrypt_list_algorithms devuelve en modo matriz todos los algoritmos


que están disponibles en la biblioteca MCRYPT. Utilizamos esta función
en este Ejemplo para comprobar que está disponible el algoritmo AES
256 en la biblioteca antes de cifrar la cadena.

• mcrypt_module_open(MCRYPT_RIJNDAEL_256,"", MCRYPT_MODE_ECB,"")
para abrir el módulo del algoritmo que vamos a utilizar
MCRYPT_RIJNDAEL_256 (AES 256) e indicar el modo
MCRYPT_MODE_ECB. Esta función devuelve un identificador de
cifrado o False si se ha producido algún error. El primero de los cuatro
parámetros indica el tipo de cifrado o algoritmo de cifrado que se va a
utilizar. En el segundo parámetro se indica dónde está este módulo, es
decir, el directorio donde está el fichero que lo contiene. Si no se indica
nada, hay que poner una cadena vacía, como en esta instrucción, y se
asume que el módulo está en el directorio por defecto (directiva
mcrypt.algorithms_dir). En el tercer parámetro se indica el modo de
cifrado. En el cuarto parámetro se indica dónde está este modo, es
decir, el directorio donde está el fichero que lo contiene. Si no se indica
nada, hay que poner una cadena vacía, como en esta instrucción, y se
asume que el módulo está en el directorio por defecto (directiva
mcrypt.modes_dir). El módulo abierto se cierra con la función
mcrypt_module_close().

• srand((double)microtime()*1000000) para inicializar el generador de


números aleatorios desde un determinado número de microsegundos.

• mcrypt_create_iv(mcrypt_enc_get_iv_size($td),MCRYPT_RAND) para
crear un vector de inicialización (IV) que sirve de semilla para iniciar el
proceso de cifrado. En el primer parámetro debemos indicar el tamaño
(size) de este vector. Para ello, nos servimos de la función
mcrypt_enc_get_iv_size($td), que devuelve el tamaño del módulo de
cifrado creado en la instrucción anterior. Esta última función lleva como
argumento el nombre del identificador de cifrado. En el segundo
parámetro de la función mcrypt_create_iv se indica la fuente de la
semilla de cifrado. Ésta puede ser MCRYPT_RAND (generador de números
aleatorios del sistema), MCRYPT_DEV_RANDOM (que lee datos de
/dev/random) y MCRYPT_DEV_URANDOM (que lee datos de /dev/urandom).
Estos dos últimos procedimientos generadores de números aleatorios
sólo están disponibles en muchos sistemas Unix. En el caso de que
usemos la fuente MCRYPT_RAND hay que asegurarse de llamar antes a la
función srand() para inicializar el generador de números aleatorios,
como hemos hecho en una instrucción anterior.

26
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

• mcrypt_generic_init($td, $clave, $iv) para inicializar los buffers


necesarios para el cifrado. Esta función devuelve el valor 0 si se ha
producido la inicialización o un valor menor que 0 si no ha sido posible.
Lleva como parámetros el identificador del cifrador, la clave de cifrado
solicitada al usuario y el identificador del vector de inicialización.

• mcrypt_generic($td, $texto) para llevar a cabo, finalmente, el cifrado


de los datos. Lleva como parámetros el identificador del cifrador y el
texto que debe cifrarse solicitado al usuario. Devuelve el texto cifrado.
Además, en el Ejemplo, usamos la función base64_encode para codificar
el resultado en el formato MIME base64 y así todos caracteres serán
visibles en pantalla.

• mcrypt_generic_deinit($td) para finalizar el cifrado. Lleva como


parámetro el identificador del cifrador. Borra todos los buffers y cierra
todos los módulos usados. Devuelve False en caso de error y True si se
ha ejecutado bien.

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 6 (Cifrado datos)


de la Unidad 2. Estudia el código fuente y ejecútalo para mostrar en el
navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 6 de la Unidad 2 verás que aparece la siguiente


página:

27
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

2.5. Control de flujos de datos en PHP

PHP realiza un procesamiento de la página y envía al navegador del usuario el


resultado de procesar el código PHP. En general, a medida que se va
procesando la página, se envía el código HTML resultante al cliente.

Las Funciones de Control de Salida (Output Control Functions) permiten


indicar en qué momento debe ser enviada la salida de la ejecución de un script
PHP al navegador del usuario. En determinadas circunstancias puede ser
necesario usar estas funciones, como cuando se quiere autentificar a los
usuarios que solicitan acceder a una página, evitar que aparezcan errores de
ejecución de una página, etcétera. El uso correcto de estas funciones permite
modificar la página antes de que se envíe al navegador del usuario.

El uso de estas funciones no afecta a otras que permiten enviar cabeceras,


como header(), o crear cookies con setcookie(), pero sí a las funciones que
generan salidas al navegador del usuario solicitante, como echo(), print(),
etcétera. Para obtener más información conviene leer en el Manual de PHP en
el apartado Funciones de Control de Salida.

Veamos el siguiente ejemplo:

<?
ob_start();
echo "Hola\n";
setcookie ("nombre_cookie", "datos_cookie");
ob_end_flush();
?>

Como se observa en el código anterior, la función ob_start() de control de


salida, activa el buffer de memoria e inhibe la salida de la siguiente orden de
salida echo "Hola\n" al navegador del usuario. Después, se crea una cookie
con la instrucción correspondiente y, finalmente, la función ob_end_flush() de
control de salida envía al navegador del usuario la salida de echo "Hola\n" y
desactiva el buffer de memoria.

Destacamos que, si no se hubiera inhibido la salida del script y el servidor PHP


no cacheara la página, la cookie no podría haberse mandado y se produciría el
típico error "Cannot modify header information - headers already sent by…".
Esto se debe a que la cookie debe ser siempre enviada en la cabecera de la
página (principio del script), es decir, antes de mandar el contenido de la
página.

Atención: hay que tener en cuenta que el servidor PHP de XAMPP activa la
caché automáticamente por defecto y este script no mostraría el error descrito
anteriormente. En su archivo php.ini de configuración se indica la directiva

28
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

“output_buffering=4096” que activa a 4KB el tamaño de la caché automática.


Para desactivar esta caché podemos escribir “output_buffering=Off”.

En el Ejemplo 7 hemos utilizado las siguientes funciones de control de salida:

1. ob_start() para activar el buffer de salida. Desde este momento


cualquier operación que comporte una salida al navegar del usuario es
almacenada en este buffer interno de salida. La función complementaria
ob_implicit_flush(), que se usa para evitar (on, 0) o permitir (off,
distinto de 0) que la información se descargue de forma automática
cuando el buffer de memoria se llena. En este ejemplo no la usamos,
pero sí lo haremos en la Unidad siguiente.
2. ob_get_contents() para recuperar el contenido actual del buffer de
salida y guardarlo en una variable de tipo string. Si el buffer interno no
estuviera activado, esta función devuelve False.
3. ob_clean() para borrar el contenido del buffer sin desactivarlo.
4. ob_end_clean() para borrar el contenido del buffer y desactivarlo. A
partir de este momento, las salidas se producen de forma normal
enviándose al navegador del usuario.

Hay otra función de control de salida muy útil y de uso frecuente:

5. ob_get_length(), que devuelve el tamaño del contenido del buffer de


salida. Si el buffer interno no estuviera activado, esta función devuelve
False.

Recomendamos a los alumnos y alumnas de este curso que estudien


detenidamente el Ejemplo 7 de esta Unidad, para ver cómo se usan las
funciones de control de salida para cambiar el título de la página
dinámicamente y para verificar que una página existe en el servidor.

Si abres el fichero index.php de este proyecto verás que contiene el siguiente


código fuente:

<?php
// Empezamos a guardar lo que se va a enviar al navegador del usuario
ob_start();
?>

<HTML>
<HEAD><TITLE>Sin título</TITLE>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style type="text/css">
table.con_borde { margin: 1em; border: 1px solid #ccc; }
td, th { padding: .3em; font-family:verdana;
font-size:12px;
color:navy;
}
</style>
</HEAD>
<BODY>
<CENTER>

29
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

<TABLE width=700 border=0><TR><TD>


<TABLE width=100% border=0 cellspacing=0 cellpadding=10>
<TR><TD colspan=2 bgcolor=CCCCCC border=0>
<FONT size=3><B>Unidad 2 - Ejemplo 7: Control flujo
datos</B></FONT></TD></TR>
<TR>
<TD bgcolor=FFFFFF align=center>

<?php

// Variable donde el usuario indeica la página


if (isset($_GET['pagina'])) $_GET['pagina'] = strtolower($_GET['pagina']);
else $_GET['pagina']="index";

// Variable donde vamos a ir guardando la página que se devuelve al usuario


// Obtenemos el contenido de la página hasta ahora
$contenido = ob_get_contents();
// Eliminamos el contenido de la caché sin desactivarlo
ob_clean();

// Buscamos si existe el fichero de la página solicitada por el usuario


if (is_readable('paginas/'.$_GET['pagina'].'.php')) {
$contenido = preg_replace("/<TITLE>(.*?)<\/TITLE>/",
"<title>Unidad 2 - Ejemplo 7: "
.$_GET['pagina']."</title>", $contenido);
include('paginas/'.$_GET['pagina'].'.php');
// Si no existe, mostramos una página genérica
} else {
// Reemplazamos el título de la página HTML
$contenido = preg_replace("/<TITLE>(.*?)<\/TITLE>/",
"<title>Unidad 2 - Ejemplo 7: Control flujo datos</title>",
$contenido);
include('paginas/not-found.php');
}
// Obtenemos el contenido de esta nueva página
$contenido2 = ob_get_contents();
// Limpiamos la caché
ob_end_clean();

// Ahora es cuando imprimimos el contenido de la página completa


echo $contenido.$contenido2;

?>

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 7 (Control flujo


datos) de la Unidad 2. Estudia el código fuente y ejecútalo para mostrar en
el navegador el resultado de su interpretación.

Si ejecutas el Ejemplo 7 de la Unidad 2 verás que aparece la siguiente


página:

30
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Si haces clic en un enlace (página) que existe entonces se muestra la siguiente


imagen:

31
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

En cambio, si la página no existe, entonces aparece la siguiente imagen de


error:

32
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

2.6. Autenticación segura en PHP

La Autenticación consiste en verificar la identidad del usuario que accede a una


aplicación con objeto de personalizar su experiencia de navegación, habilitarle
acceso a recursos privados, etcétera.

En el Curso de iniciación de PHP 5 explicamos la forma de autentificar a los


usuarios antes de permitirles entrar en una página web. En la Unidad 5 del
mismo decíamos que el control de usuarios es uno de los procedimientos más
frecuentes y comunes utilizados para que sólo puedan acceder a determinadas
páginas aquellas personas que sean reconocidas y estén autorizadas según
diferentes perfiles.

Este control suele hacerse preguntando un nombre o identificador de usuario


(id o login) y una clave o contraseña (password). También se explicaba allí que,
generalmente, esta validación de los usuarios que acceden a un servidor web
suele ser llevada a cabo por el propio servidor. Pero también es posible hacerlo
enviando en la cabecera una solicitud de validación para entrar en una
aplicación concreta.

En este apartado vamos a explicar una manera simple y segura de autenticar


usuarios en una página web con PHP utilizando sesiones y funciones hash.

Para empezar, debemos entender cómo funciona el mecanismo de


autenticación que consta de dos pasos: registro y autenticación. La primera
vez que el usuario entra al sistema y crea una cuenta de usuario
proporcionando un nombre de usuario y una contraseña realiza el proceso de
"registro". En el proceso de "autenticación" el usuario acredita para entrar en
la página web.

La Inyección SQL, aunque suene a un término médico, en realidad es un


mecanismo de ataque basado en la inclusión de sentencias SQLs externas
utilizando una vulnerabilidad informática presente en una aplicación cuando se
realiza la validación de los datos introducidos por un usuario y que, a su vez, se
usan para realizar consultas a una base de datos.

MUY IMPORTANTE: las técnicas que se muestran a continuación deben servir


al alumno o alumna para aprender a escribir código robusto. En ningún caso
deben utilizarse en Internet para atacar páginas web ajenas. Es ilegal hacerlo,
¡estás avisado!

En el Ejemplo 8 de esta Unidad hemos diseñado, a propósito, una aplicación


PHP que presenta debilidades, desde el punto de vista de seguridad, para
poder mostrar cómo se explotan estas debilidades de desarrollo mediante la
técnica de Inyección SQL (en inglés, SQL Injection); posteriormente veremos
cómo implementar un sistema de acceso robusto.

33
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Esta aplicación tiene dos opciones:

• Añadir usuarios, para dar de alta nombres de usuario y sus


contraseñas en la tabla de MySQL denominada “usuarios_debil”.
• Acceso al sistema, que simula una página de entrada en una aplicación
web, solicitando un nombre de usuario y su contraseña, comprobando si
está en la tabla de usuarios y respondiendo los siguientes mensajes:
o Tu sesión con el usuario admin ha sido validada correctamente y
puedes acceder a la zona privada.
o ERROR: credenciales no válidas. Por favor, introduce el usuario y
la contraseña correctos para acceder.

El aspecto que tiene esta aplicación es el siguiente:

Cuando se accede por primera vez a la aplicación se crea automáticamente la


base de datos y se añade el usuario admin/12345.

Veamos ahora cómo explotar esta vulnerabilidad para obtener información de


la base de datos, e incluso, modificar su contenido.

Inyección SQL 1: acceso a la aplicación sin tener nombre de usuario ni


contraseña

Como hemos dicho, cuando el visitante escribe su nombre de usuario y


contraseña, la aplicación responde uno de estos dos mensajes:

• ERROR: credenciales no válidas. Por favor, introduce el usuario y la


contraseña correctos para acceder:
• Tu sesión con el nombre de usuario ha sido validada correctamente y
puedes acceder a la zona privada.

34
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Primero vamos a comprobar si la aplicación valida los datos enviados por el


usuario escribiendo unas comillas como dato, por ejemplo, en el nombre de
Usuario:

A continuación, si pulsamos “Enviar” vemos que aparece el siguiente mensaje


de la aplicación: “ERROR: credenciales no válidas. Por favor, introduce el
usuario y la contraseña correctos para acceder”.

Si ahora escribimos una comilla simple:

Si pulsamos de nuevo el botón “Enviar” vemos que aparece el siguiente


mensaje de la aplicación: “ERROR: no se ha podido ejecutar la consulta.”

Este último mensaje de error indica que no se están validando los datos de
entrada y que, además, las consultas SQL de la base de datos están
delimitadas por comillas simples. ¿Por qué?

Probablemente, el código de la aplicación sea similar a este ejemplo:

// SQL que selecciona la contraseña del usuario únicamente


$strSQL = "SELECT password FROM usuarios_debil WHERE usuario =
'$usuario' and password= '$clave'";
$resultado=ejecuta_SQL($strSQL, true);

// Si el resultado da alguna línea, leemos el campo clave


if ($resultado->rowCount()!=1)
{
print "<p>ERROR: no se ha podido ejecutar la consulta.</p>\n";
} elseif {
print "<p> Tu sesión con el usuario a ha sido validada
correctamente y puedes acceder a la zona
privada.</p>\n";

35
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Al escribir una comilla simple al principio del nombre de usuario, la consulta se


convierte en:

SELECT password FROM usuarios_debil WHERE usuario = ''prueba' and


password= 'prueba'

Esta consulta es NO correcta (contiene errores de sintaxis con la doble comilla


simple) y cuando se ejecuta, el servidor de bases de datos devuelve un error.

Ahora que sabemos que la consulta está delimitada por comillas simples,
podemos escribir un texto que modificará la consulta y hará que la aplicación
piense que hemos introducido las credenciales de un usuario legítimo ya
registrado. En concreto, vamos a introducir este texto en el campo “Clave”:

Si escribimos en el campo Clave el texto:

prueba' OR '1'='1' limit 1 #'

Veremos que se accede correctamente a la parte privada de la aplicación:

En este caso, la consulta a la base de datos sería algo parecido a esto:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba' OR '1'='1' limit 1 #'

Puedes ver que esta consulta es correcta y, al ejecutarse, la base de datos


devuelve todos los registros de la tabla ya que siempre se cumple la condición
final OR '1'='1', aunque el nombre del usuario y la contraseña sean incorrectos.

36
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Inyección SQL 2: descubrir los nombres de los campos

Para averiguar los nombres de campos utilizamos la técnica de prueba y error.


Es decir, vamos a escribir texto que construya consultas en las que aparezcan
posibles nombres de los campos. Si la consulta devuelve un error es indicativo
de que el nombre es incorrecto; si no es así significa que hemos acertado con
el nombre de los campos.

Por ejemplo, vamos a probar si el nombre de uno de los campos es "user"


escribiendo en el campo “Clave” lo siguiente:

prueba' AND user='prueba

Si pulsamos “Enviar”, la respuesta de la aplicación es "ERROR: no se ha


podido ejecutar la consulta.", lo que indica que no hay un campo que se llame
"user". En este caso, la consulta a la base de datos habrá sido algo parecido a
esto:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password=' prueba' AND user='prueba'

Ahora hacemos un segundo intento, con el nombre del campo "usuario":

prueba' AND usuario='prueba

A continuación, aparece el mensaje “ERROR: credenciales no válidas. Por


favor, introduce el usuario y la contraseña correctos para acceder:”. Es decir,
ahora sabemos que uno de los campos se llama "usuario".

En este caso, la consulta a la base de datos tendría un aspecto parecido a


éste:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba' AND usuario='prueba'

Inyección SQL 3: averiguar los nombres de las tablas

De igual forma que en el caso anterior, los nombres de las tablas se pueden
averiguar mediante la técnica de prueba y error. De nuevo, introducimos texto
que construye consultas en las que aparezcan posibles nombres de las tablas.
En el caso de que las consultas indiquen un error, entonces el nombre es
incorrecto; si, por el contrario, no ocurre así, es que hemos acertado con el
nombre de las tablas.

Por ejemplo, vamos a probar si el nombre de la tabla es "users" escribiendo


este texto en el campo “Clave”:

prueba' AND 1=(SELECT COUNT(*) FROM users); #

37
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

La respuesta de la aplicación es "ERROR: no se ha podido ejecutar la


consulta.", indicando que no hay una tabla que se llame "users". En este caso,
la consulta a la base de datos es algo parecido a esto:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba' AND 1=(SELECT COUNT(*) FROM users); #'

Hacemos ahora un segundo intento con el nombre "usuarios_debil":

prueba' AND 1=(SELECT COUNT(*) FROM usuarios_debil); #

Como hemos obtenido la respuesta "ERROR: credenciales no válidas... ",


sabemos que una de las tablas se llama "usuarios_debil".

En este caso, la consulta a la base de datos se podría parecer a esto:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba' AND 1=(SELECT COUNT(*) FROM usuarios); #'

Inyección SQL 4: averiguar el contenido de los registros

Una vez conocemos el nombre de la tabla de usuarios y los nombres de los


campos es posible conocer valores concretos de un registro mediante, de
nuevo, la técnica de prueba y error. Vamos a introducir texto que construyan
consultas correctas y buscar datos concretos en ciertos campos de la base de
datos. Nuevamente, en el caso de que las consultas den un error entonces es
que el contenido no está en la base de datos; si no es así, es que hemos
acertado en la búsqueda del contenido.

Por ejemplo, vamos a buscar nombres de usuarios escribiendo en el campo


“Clave” lo siguiente:

prueba' OR usuario= 'admin '; #

La respuesta de la aplicación es "Tu sesión con el usuario admin ha sido


validada correctamente y puedes acceder a la zona privada.", lo que nos
indica que hay un usuario cuyo nombre es "admin" y, además, hemos accedido
directamente a la base de datos.

En este caso, la consulta a la base de datos se podría parecer a esto:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba' OR usuario= 'admin '; #

Nota: podríamos haber utilizado el operado LIKE de SQL para ir buscando el


nombre letra a letra con consultas el tipo: usuario LIKE 'a%'.

38
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Inyección SQL 5: añadir un nuevo usuario

En este caso vamos a aprovecharnos de la debilidad de la aplicación que


ejecuta varias consultas que se encuentran en una misma línea.

Por ejemplo, vamos a insertar un nuevo registro escribiendo en el campo


“Clave” lo siguiente:

prueba'; INSERT INTO usuarios_debil VALUES ('99', 'hacker', 'hacker');


#

En este caso, la consulta a la base de datos sería así:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba'; INSERT INTO usuarios_debil VALUES ('99', 'hacker',
'hacker'); #'

Al pulsar el botón “Enviar” aparece el mensaje “ERROR: credenciales no


válidas. Por favor, introduce el usuario y la contraseña correctos para acceder:”
indicando que la consulta no se ha ejecutado correctamente.

Para comprobar si el ataque ha tenido éxito, habría que probar a acceder en la


aplicación con el usuario "hacker" y contraseña "hacker".

Evidentemente para que este ataque tenga éxito debemos conocer


previamente la estructura de la tabla, lo que exige varias consultas al servidor.
También podríamos obtener los datos de un registro averiguando el nombre de
algún usuario (mediante sentencias del tipo LIKE y algo de paciencia) e inyectar
una consulta que modifique su contraseña.

Inyección SQL 6: borrar una tabla

La técnica consiste en incluir una sentencia SQL que borre una tabla en el
campo “Clave”:

prueba'; DROP TABLE usuarios_debil; #

Si pulsas “Enviar”, verás que aparece un error en la propia aplicación:

En este caso, la consulta a la base de datos sería algo parecido a esto:

SELECT password FROM usuarios_debil WHERE usuario = 'prueba' and


password='prueba'; DROP TABLE usuarios_debil; #'

39
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Si el ataque ha tenido éxito, la aplicación seguramente dejará de funcionar,


puesto que ha desaparecido una de las tablas. En este caso, si refrescas el
navegador, verás que la aplicación recrea la tabla borrada automáticamente.

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 8 (Autenticación


de usuarios) de la Unidad 2. Estudia el código fuente y ejecútalo para
mostrar en el navegador el resultado de su interpretación.

Diseño robusto de autenticación usuario


Veamos ahora cómo implementar un mecanismo robusto de autenticación de
usuario en PHP en el Ejemplo 9 de esta Unidad. Se trata de una aplicación
exactamente igual que la anterior en la que hemos tenido en cuenta criterios de
seguridad. Por lo tanto, utilizaremos la tabla “usuarios” que almacena cifrado el
campo “password” mediante un algoritmo hash.

Cuando un visitante realiza el Registro de un usuario ocurre lo siguiente:

1. El navegador aplica en remoto el algoritmo Sha-1, mediante una


biblioteca de JavaScript, transformando la clave en texto claro en un
hash para que no lo pueda descifrar ningún intruso que utilice la técnica
de ataque “man in the middle” (significa el hombre en medio y es un
ataque que consiste en escuchar el tráfico entre el cliente y el servidor).
Recordamos que los algoritmos hash son unidireccionales, es decir, el
servidor no sabe cuál es la verdadera clave del usuario.
2. Se envía el nombre y la contraseña del usuario al servidor para
almacenarlos en la base de datos.

El esquema de Registro sería el siguiente:

Al dar de alta un usuario nuevo y antes de enviar la clave introducida por el


usuario primero invocamos la función JavaScript "valida_cifra()" incluida en
la cabecera de la página que se encarga de verificar que las dos claves

40
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

introducidas son iguales y, además, aplica la función str_sha1() (que se


encuentra en el archivo "sha1.js") para transformar la clave introducida en un
texto Hash para que no se pueda descifrar.

Pasos para la autenticación del usuario:

1. El usuario rellena el formulario de autenticación con su usuario y clave.


2. El navegador aplica el algoritmo Sha-1 a la clave del usuario mediante
una biblioteca de JavaScript.
3. Se envía el usuario y la clave cifrada al servidor PHP.
4. El servidor limpia los datos de entrada quitando comillas y permitiendo
una única palabra de entrada (nada de espacios que permitan
Inyecciones SQL).
5. Buscamos el registro en la base de datos que corresponde a ese usuario
y leemos el campo “password” que está cifrado con un hash.
6. Si el campo “clave” recibido es igual al campo “password” almacenado
en la base de datos, entonces iniciamos una sesión para ese usuario.

El esquema de Autenticación sería el siguiente:

Igualmente, en la fase de Autenticación, mediante la función JavaScipt


“valida()” recogemos la contraseña escrita por el usuario y aplicamos de
nuevo la función str_sha1() antes de enviarla a la página del servidor.

Veamos ahora cómo hemos fortalecido la aplicación del Ejemplo 8 para que
sea robusta frente a ataques del tipo Inyección SQL.

En el archivo index.php podemos ver que hemos incluido un código que evita
que un atacante pueda escribir más de una palabra en los campos “usuario” y
“clave”, así evitamos que se pueda introducir sentencia SQLs que necesitan
espacios en blanco:

41
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

// Leemos variables clave y usuario si tienen algún valor


if (isset($_POST['clave']) && isset($_POST['usuario']))
{
// Evitamos que haya escrito más de una palabra en el login
// (evita sql injection) separando la palabras por el espacio y
// nos quedamos con la primera palabra
$usuario = split(" ",trim($_POST['usuario']));
$usuario=$usuario[0];
$clave = split(" ",trim($_POST['clave']));
$clave=$clave[0];
}
else {
$usuario="";
$clave="";
}

Al buscar las credenciales del usuario en el servidor MySQL, en lugar de


ejecutar una consulta que incluya el campo “password” en los términos de
búsqueda, comparamos el campo directamente en el código PHP:

// SQL que selecciona la contraseña del usuario únicamente


$strSQL = "SELECT password FROM usuarios WHERE usuario = '$usuario'";
$resultado=ejecuta_SQL($strSQL, true);

// Si el resultado da alguna línea, leemos el campo clave


if ($resultado->rowCount()==1)
{
$fila = $resultado->fetch();
// Si los hash de las claves coinciden
if($_POST['clave']==$fila['password']) {
// Guardamos la identidad del usuario que desea
// autenticarse
$_SESSION["usuario"]= $usuario;
$_SESSION["valido"]=1;
// Vamos a la página de la cuenta
header("Location: micuenta.php");
// Salimos de este script
exit();
}
}

Así, sólo dejamos que se busque por el campo “usuario” y evitamos puntos de
entrada a la Inyecciones SQL.

Es decir, primero verificamos que el nombre del usuario existe y, si es así,


comprobamos que la clave proporcionada y la que está almacenada en la base
de datos coinciden. En este caso creamos la variable de sesión
$_SESSION["valido"] con el valor “1” que indica que se trata de un usuario
válido y puede acceder al contenido privado redireccionando a la página
micuenta.php.

En el archivo conexión.php podemos encontrar este código:

42
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

// Función que ejecuta una SQL y sólo devuelve error cuando es


necesario
function ejecuta_SQL($sql) {
global $db;
$resultado=$db->prepare($sql);
if (!$resultado->execute() && DBMuestraErrores)
{
echo"<H3>ERROR: No se ha podido ejecutar la
consulta.</H3>";
die ();
}
return $resultado;
} // end ejecuta_SQL

El uso del método prepare() de PDO evita las inyecciones SQL ya que valida
el contenido de la consulta y sólo permite que se envíe al servidor un única
consulta evitando así que un atacante concatene varias consultas. Además,
hemos incluido la constante DBMuestraErrores que usamos para marcar si se
deben mostrar los errores de ejecución a la hora de depurar la aplicación
cuando sea necesario. Cuanta menos información demos al usuario con malas
intenciones, más difícil será que éste consiga acceder al contenido privado.

Utilizando Eclipse PDT puedes abrir el proyecto Ejemplo 9 (Autenticación


segura de usuarios) de la Unidad 2. Estudia el código fuente y ejecútalo
para mostrar en el navegador el resultado de su interpretación.

Recomendamos al alumno o alumna que pruebe la nueva aplicación


escribiendo la comilla simple o doble en los campos de entrada. Verás que,
para evitar darle información a un atacante, aparece siempre el mismo error:

“ERROR: credenciales no válidas. Por favor, introduce el usuario y la


contraseña correctos para acceder”

43
Curso Avanzado de PHP
Unidad 2: Funciones avanzadas de PHP

Resumen

Hay que saber al final de esta unidad…

• Usar las funciones de intérprete XML para crear un


analizador de XML, fijar sus opciones, establecer los
gestores de principio y de fin de documento,
establecer el gestor de datos e iniciar el análisis
del documento.

• Acceder a otros objetos y componentes de Windows


usando las funciones COM. Por ejemplo, tratar desde
PHP textos con MS Word o información contendida en
celdas de MS Excel.

• Servirse de la biblioteca mcrypt para abrir el módulo


del algoritmo que se vaya a aplicar en el cifrado,
inicializar el generador de números aleatorios, crear
un vector de inicialización que sirva de semilla para
iniciar el proceso de cifrado, inicializar los buffers
necesarios para cifrar el texto dado con la clave
proporcionada.

• Utilizar correctamente las funciones de control de


flujo sabiendo guardar la información en un buffer
hasta que se quiera enviar al navegador del usuario.

• Implementar la Autenticación segura en la validación


de un usuario mediante PHP.

44