Manual PHP
PHP
10/07/2007
PHP
Sobre este documento
Copyright 2007 Ales Zabala Alava <shagi@gisa-elkartea.org>
Es posible copiar, distribuir y/o modificar éste documento bajo los términos de la FDL (Free Documentation License), versión 1.2 o posterior.
Este documento está basado en diferentes manuales, aunque principalmente se ha utilizado de base el manual de PHP. La documentación de cada módulo y extensión diferentes se han cogido de las webs de cada proyecto. Para consultar la documentación original y ver sus respectivas licencias consultar sección Bibliografia y Licencias.
Introducción
PHP (acrónimo de "PHP: Hypertext Preprocessor") es un lenguaje de "código abierto" interpretado, de alto nivel, embebido en páginas HTML y ejecutado en el servidor. Es muy fácil de aprender, pero a su vez ofrece muchas características avanzadas.
Ejemplo:
<html> <head> <title>Ejemplo de PHP</title> </head> <body> <?php echo "<p>Hola Mundo</p>"; ?> </body> </html>
Proceso de publicación web, caches incluidas
- El usuario introduce la dirección web en su navegador:
    
http://dominio.com/index.php - El navegador comprueba si tiene la página en caché. Si la tiene la muestra directamente en vez de pedirla de nuevo al servidor.
 - Si no está en esa cache, la petición sale del ordenador, y pasa por los proxy-cache que encuentre por el camino (el de la red local, el del ISP, el del servidor donde está la web, etc.).
 - Si ningún proxy-cache tiene la página, la petición llega al servidor web
    
dominio.com. - El servidor web localiza la página correspondiente a 
/index.php. Debido a su configuración, detecta que tiene código php. - El fichero 
index.phppasa por el procesador de php, que genera un fichero en (x)html. - El servidor publica el fichero (x)html generado por php
 
  Hay que destacar que el contenido del fichero index.php hay que
  entenderlo como un fichero de código fuente, que ha de ser compilado. El
  hecho de que contenga código (x)html es una peculiaridad de php (y de otros
  lenguajes de programación parecidos).
Cuando pedimos que una web se recargue, se envía una cabecera http especial que avisa a las diferentes caches que invaliden su página cachea y pidan la web de nuevo. Que los proxy-cache hagan caso de esta cabecera http no es definitivo, pero es habitual.
Referencia del lenguaje
Sintaxis básica
A la hora de procesar un fichero php deja el contenido tal cual, sólo procesa el contenido que esta dentro de algún delimitador de escape:
<?phpy?>(recomendado)<script language="php">y</script><?y?><%y%>
Los dos últimos puede que no funcionen, dependiendo de la configuración del servidor. Si pretendemos publicar nuestro código no deberíamos utilizar los delimitadores cortos, ya que no tenemos garantía de que vayan a funcionar.
Las instrucciones se separan con ;. Todas las instrucciones deben
terminar con este carácter con la etiqueta de fin de bloque (?>).
Los comentarios son los mismos que en C y en los intérpretes de comandos de UNIX:
#comentan hasta fin de línea//comentan hasta fin de línea/*...*/comenta el bloque interior
Tipos de variables
Los tipos de las variables no se declaran por el programador, se deciden en tiempo de compilación dependiendo del contexto.
Para ver el contenido de una variable: var_dump($variable)
Para comprobar el tipo actual de una variable se pueden utilizar las
funciones is_tipo($variable). Por ejemplo:
// Si este valor es un entere, incrementarlo en cuatro
if (is_int($int)) {
    $int += 4;
}
// Si $bool es una cadena, imprimirla
if (is_string($bool)) {
    echo "Cadena: $bool";
}
Para forzar un determinado tipo de variable se puede hacer casting o
utilizar la función settype():
$foo = 10; // $foo es un entero $bar = (boolean) $foo; // $bar es un booleano $foo = "5bar"; // string $bar = True; // boolean settype($foo, "integer"); // $foo es ahora 5 (integer) settype($bar, "string"); // $bar es ahora "1" (string)
Los diferentes tipos de variables son:
- Escalares:
    
- Booleanos
 - 
      
TrueyFalseA la hora de convertir una variable a boolean (para cuando está dentro de una comprobación de
:if, por ejemplo) se evalúan aFalse- El integer 0 (cero)
 - El float 0.0 (cero)
 - Los strings 
""y"0" - Los arrays de cero elementos
 - El tipo especial 
NULL 
 - Enteros
 - 
      (...,-2,-1,0,1,2,...) 
$a = 1234; // numero decimal $a = -123; // un numero negativo $a = 0123; // numero octal (equivalente al 83 decimal) $a = 0x1A; // numero hexadecimal (equivalente al 26 decimal)El tamaño de depende de la plataforma, normalmente cercano a 2 billones (32 bits con signo). En caso de overflow el número entero se pasará a float.
La división entre dos números enteros siempre devolverá un float. Al hacer casting a entero se redondea hacia abajo.
 - Números de punto flotante:
 - 
      
1.234,1.2e3,7E-10El tamaño depende de la plataforma lo más normal son 14 dígitos decimales (64 bits).
Hay que tener cuidado con el margen de error, no se recomienda comparar dos números float para saber si son iguales. Para eso están las funciones matemáticas de precisión arbitraria.
 - Cadenas
 - 
      En php cada uno de los caracteres de una cadena es un byte, por lo
      que no tiene soporte nativo de unicode. Si necesitamos unicode
      tenemos que utilizar 
utf8_encode()yutf8_decode().Las cadenas se pueden especificar de tres modos diferentes: Con comillas simples, con comillas dobles y mediante HEREDOC.
- 
      
'hola': Para introducir una comilla simple hay que escaparla:'O\'Connor' - 
      
"hola": Para introducir una comilla doble hay que escaparla:<span "lang=\"eu\">Kaixo</span>" - HEREDOC: igual que las comillas dobles. Útil para no tener que escapar las comillas dobles.
 
$var = << FINDETEXTO Hola esto es un texto muy largo en varias líneas con un <a href="index.php">enlace</a> FINDETEXTO //findetexto tiene que ir al principio de la líneaTanto las cadenas delimitadas por comillas dobles como mediante HEREDOC procesan el contenido de la cadena. Los\tse convierten en tabuladores, los\nse convierten en retornos de carro, y las variables se interpretan:$nombre='Paco'; echo "Tu nombre es: $nombre"; # imprime 'Tu nombre es: Paco' $frutas = array('fresa' => 'roja', 'platano' => 'amarillo'); echo "Un plátano es {$frutas['platano']}."; $cerveza = 'Keler'; echo "quiero tres {$cerveza}s"; # imprime "quiero tres Kelers"Para unir cadenas:$numeros = 'uno, ' . 'dos'; # $números ahora es 'uno, dos' $cerveza .= ' fría'; # $cerveza ahora es 'Keler fría'Para conseguir un carácter:echo $cerveza{0} # imprime 'K'Para modificarlo:$cerveza{2}='o' # Ahora $cerveza es 'Keoer' - 
      
 
 - Compuestos:
    
- Matrices:
 - 
      En php no existen los arrays típicos, son estructuras donde se
      relacionan índices con valores.
$dos = array('padre'=>'Jokin', 'madre'=>'Edurne', 1=>'uno') $uno = array('cero','uno',2,3) # Si no se especifican indices se # asignan automáticamente: 0, 1, 2,... $tres = array(array(1,2),array(3,4) #Una matriz de dos dimensionesPara conseguir un elemento:$variable[0] $variable['padre']Para borrar:unset($variable['madre'])Para añadir más elementos:$variable[11]='once' # si el indice 11 ya existía se reemplaza. $variable[]='nuevo y al final' # indice = el indice más alto + 1 - Objetos: (ya los veremos más adelante)
 - 
class foo { function hacer_foo() { echo "Haciendo foo."; } } $bar = new foo; $bar->hacer_foo(); 
 - Especiales:
    
- Recurso
 - 
      Para almacenar recursos externos a php. Muy habituales programando
      sin orientación a objetos.
$corrector = aspell_new() $resultados = mysql_query($sql) - NULL
 - 
      Para cuando una variable no ha sido definida, o
      eliminada con 
unset(), o asignada a NULL.$variable = NULL; 
 
Variables
Se representan con un dolar seguido de el nombre de la variable. Se distingue entre mayúsculas y minúsculas. Las variables tienen que empezar por letra o subrayado, y continuar con letras, números o subrayado.
$variable = 'valor' $$variable = '1' # igual que $valor='1' $copia = $variable &$referencia = $variable $foo = 'Bob'; // Asigna el valor 'Bob' a $foo $bar = &$foo; // Referencia $foo vía $bar. $bar = "Mi nombre es $bar"; // Modifica $bar... echo $foo; // $foo también se modifica. echo $bar;
Variables superglobales
Se les llama así porque son variables definidas antes de que se ejecute el script, que se hacen globales automáticamente. Contienen datos variables del servidor.
- $GLOBALS
 - Contiene una referencia a cada variable disponible en el espectro de las variables del script. Las llaves de esta matriz son los nombres de las variables globales.
 - $_SERVER
 - Variables definidas por el servidor web ó directamente relacionadas con el entorno en don el script se esta ejecutando.
 - $_GET
 - Variables proporcionadas al script por medio de HTTP GET.
 - $_POST
 - Variables proporcionadas al script por medio de HTTP POST.
 - $_COOKIE
 - Variables proporcionadas al script por medio de HTTP cookies.
 - $_FILES
 - Variables proporcionadas al script por medio de la subida de ficheros via HTTP.
 - $_ENV
 - Variables proporcionadas al script por medio del entorno.
 - $_REQUEST
 - Variables proporcionadas al script por medio de cuaquier mecanismo de entrada del usuario y por lo tanto no se puede confiar en ellas. Por ejemplo contiene tanto los valores de $_POST como los de $_GET
 - $_SESSION
 - Variables registradas en la sesión del script.
 
Obteniendo datos de formularios
El siguiente formulario XHTML:
  <form action="foo.php" method="post">
    Nombre: <input type="text" name="nombre" />
    Email: <input type="text" name="email" />
    <input type="submit" name="submit" value=" Enviar " />
  </form>
  Pone accesibles dos variables nuevas en foo.php a través de la
  variable superglobal $_POST: $_POST['nombre'] y
  $_POST['email']. En este caso son variables simples, a partir de php4
  podemos enviar variables más complejas:
  <form action="foo.php" method="post">
      Nombre: <input type="text" name="personal[name]" />
      Email: <input type="text" name="personal[email]" />
      Cervezas:
      <select multiple name="cervezas[]">
          <option value="rubia">Rubia</option>
          <option value="tostada">Tostada</option>
          <option value="negra">Negra</option>
      </select>
      <input type="submit" name="submit" value=" Enviar " />
  </form>
  En este caso $_POST['personal'] será un array con los índices
  name y email, mientras que $_POST['cervezas'] será un
  array que contendrá la lista de cervezas elegidas por el usuario.
Constantes
No pueden cambiar durante la ejecución del script.
Solo pueden albergar valores escalares.
El ámbito de las constantes es siempre global.
define("CONSTANTE", "Hola mundo.");
echo CONSTANTE; //"Hola mundo."
Operadores
Operadores de Aritmética
| Ejemplo | Nombre | Resultado | 
|---|---|---|
| -$a | Negación | El opuesto de $a. | 
| $a + $b | Adición | Suma de $a y $b. | 
| $a - $b | Substracción | Diferencia entre $a y $b. | 
| $a * $b | Multiplicación | Producto de $a y $b. | 
| $a / $b | División | Cociente de $a y $b. | 
| $a % $b | Módulo | Resto de $a dividido por $b. | 
Operadores de Asignación
  El principal es el operador =.
  Se puede combinar = con los operadores aritméticos: +=, -=,
  *=, /=, %=:
$cont = 1; $cont += 3; # $contador ahora vale 4
Operadores Bit a Bit
| Ejemplo | Nombre | Resultado | 
|---|---|---|
| $a&$b | Y | Los bits que están activos tanto en $a como en $b son activados. | 
| $a|$b | O | Los bits que están activos ya sea en $a o en $b son activados. | 
| $a^$b | O exclusivo (Xor) | Los bitos que estén activos en $a o $b, pero no en ambos, son activados. | 
| ~$a | No | Los bits que estén activos en $a son desactivados, y vice-versa. | 
| $a<<$b | Desplazamiento a izquierda | Desplaza los bits de $a, $b pasos a la izquierda (cada paso quiere decir "multiplicar por dos") | 
| $a>>$b | Desplazamiento a derecha | Desplaza los bits de $a, $b pasos a la derecha (cada paso quiere decir "dividir por dos") | 
Operadores de Comparación
| Ejemplo | Nombre | Resultado | 
|---|---|---|
| $a == $b | Igual | TRUE si $a es igual a $b. | 
| $a === $b | Idéntico | TRUE si $a es igual a $b, y son del mismo tipo. (A partir de PHP 4) | 
| $a != $b | Diferente | TRUE si $a no es igual a $b. | 
| $a <> $b | Diferente | TRUE si $a no es igual a $b. | 
| $a !== $b | No idénticos | TRUE si $a no es igual a $b, o si no son del mismo tipo. (A partir de PHP 4) | 
| $a < $b | Menor que | TRUE si $a es escrictamente menor que $b. | 
| $a > $b | Mayor que | TRUE si $a es estrictamente mayor que $b. | 
| $a <= $b | Menor o igual que | TRUE si $a es menor o igual que $b. | 
| $a >= $b | Mayor o igual que | TRUE si $a es mayor o igual que $b. | 
Operadores de control de errores
  Poner el carácter @ antes de una expresión hace que los mensajes de
  error generados por la expresión sean ignorados. El mensaje de error se
  guardarán en la variable $php_errormsg
$valor = @$cache[$llave]; #No produce error si el índice $llave no existe
Operadores de ejecución
El texto que pongamos entre comillas invertidas se intentará ejecutar en la shell y se devolverá la salida, por lo que se pued asignar a una variable:
$salida = `ls -al`; echo "<pre>$salida</pre>";
Operadores de Incremento/Decremento
| Ejemplo | Nombre | Efecto | 
|---|---|---|
| ++$a | Pre-incremento | Incrementa $a en uno, y luego devuelve $a. | 
| $a++ | Post-incremento | Devuelve $a, y luego incrementa $a en uno. | 
| --$a | Pre-decremento | Decrementa $a en uno, luego devuelve $a. | 
| $a-- | Post-decremento | Devuelve $a, luego decrementa $a en uno. | 
Operadores de Lógica
| Ejemplo | Nombre | Resultado | 
|---|---|---|
| $a and $b | Y | TRUE si tanto $a como $b son TRUE. | 
| $a or $b | O | TRUE si cualquiera de $a o $b es TRUE. | 
| $a xor $b | O exclusivo (Xor) | TRUE si $a o $b es TRUE, pero no ambos. | 
| ! $a | No | TRUE si $a no es TRUE. | 
| $a && $b | Y | TRUE si tanto $a como $b son TRUE. | 
| $a||$b | O | TRUE si cualquiera de $a o $b es TRUE. | 
Operadores de Matrices
| Ejemplo | Nombre | Resultado | 
|---|---|---|
| $a + $b | Unión | Unión de $a y $b. Los índices de $a NO se sobreescriben por $b | 
| $a == $b | Igualdad | TRUE si $a y $b tienen las mismas parejas llave/valor. | 
| $a === $b | Identidad | TRUE si $a y $b tienen las mismas parejas llave/valor en el mismo orden y de los mismos tipos. | 
| $a != $b | No-igualdad | TRUE si $a no es igual a $b. | 
| $a <> $b | No-igualdad | TRUE si $a no es igual a $b. | 
| $a !== $b | No-identidad | TRUE si $a no es idéntico a $b. | 
Operadores de Tipo
  Sólo existe el operador instanceof para saber una variable es
  instacia de una clase determinada:
  class A { }
  class B { }
  $cosa = new A;
  if ($cosa instanceof A) { #En este if sí que entramos
      echo 'A';
  }
  if ($cosa instanceof B) { #En este no
      echo 'B';
  }
Estructuras de control
If, else, elseif, switch
    if ($a > $b) {
        print "a es mayor que b";
    } elseif ($a == $b) {
        print "a es igual que b";
    } else {
        print "a es mayor que b";
    }
    switch ($i) {
        case 0:
      print "i equals 0";
      break;
        case 1:
      print "i equals 1";
      break;
        case 2:
      print "i equals 2";
      break;
    }
While, Do while
    $i = 1;
    while ($i <= 10):
        print $i;
        $i++;
    endwhile;
    $i = 0;
    do {
        print $i;
        $i++;
    } while ($i>5);
For, foreach
    for ($i = 1; $i <= 10; $i++) {
         print $i;
    }
    $arr = array("one", "two", "three");
    foreach ($arr as $value) {
        echo "Value: $value\n";
    }
    foreach( $arr as $key => $value ) {
       echo "Key: $key; Valor: $value\n";
    }
Break, continue
    $i = 0;
    while (++$i) {
        switch ($i) {
        case 5:
          echo "At 5\n";
          break 1;  /* Exit only the switch. */
        case 10:
          echo "Al 10; saliendo<br>\n";
          break 2;  /* Exit the switch and the while. */
        default:
      break;
        }
    }
    $i = 0;
    while ($i++ < 5) {
        echo "Outer";
        while (1) {
      echo "Middle";
      while (1) {
          echo "Inner";
          continue 3;
      }
      echo "Esto nunca se imprime.";
        }
        echo "Y esto tampoco.";
    }
Require, include, require_once, include_once
require() y include() son idénticas en todos los aspectos excepto en el modo de actuar ante un error. include() produce un Warning mientras que require() produce un Error Fatal.
Las versiones _once solo se ejecutan la primera vez que se les llama.
    require('fichero.php');
    include('fichero.php');
    require_once('fichero.php');
    include_once('fichero.php');
Funciones
function foo ($arg_1, $arg_2, ..., $arg_n)
{
    #cuerpo de la funcion
    return $retval;
}
Las funciones pueden declararse en cualquier lugar del código.
No está soportada la redefinición de funciones.
Sólo pueden devolver un elemento (aunque pueden devolver listas).
Se pueden especificar valores por defecto. El valor por defecto tiene que ser una constante, y los parametros con valor por defecto tienen que ir al final.
function hola ($arg_1="mundo")
{
    #cuerpo de la funcion
    echo "Hola {$arg_1}!;
}
Para pasar un valores por referencia:
function add_some_extra(&$string)
{
    $string .= ' y algo más.';
}
$str = 'Esto es una cadena, ';
add_some_extra($str);
echo $str;    // Saca 'Esto es una cadena, y algo más.'
Para pasar un número variable de parámetros se pueden utilizar las funciones func_num_args(), func_get_arg(), y func_get_args():
function foo()
{
     $num_args = func_num_args();
     echo "Numero de argumentos: $num_args<br />\n";
     if ($num_args >= 2) {
     echo "El segundo argumento es: " . func_get_arg(1) . "<br />\n";
     }
}
foo (1, 2, 3);
Se pueden utilizar funciones variables:
function foo()
{
    echo "In foo()<br>\n";
}
function bar($arg = '')
{
    echo "In bar(); argument was '$arg'.<br>\n";
}
// This is a wrapper function around echo
function echoit($string)
{
    echo $string;
}
$func = 'foo';
$func();        // This calls foo()
$func = 'bar';
$func('test');  // This calls bar()
$func = 'echoit';
$func('test');  // This calls echoit()
PHP ofrece muchas funciones internas, algunas que están siempre disponibles
(como phpinfo()), otras necesitarán que php esté compilado con soporte para
ellas (como mysql_connect()).
Objetos
Un poco de teoría
(Basado en la wikipedia: http://es.wikipedia.org/wiki/Programaci%C3%B3n_orientada_a_objetos) La Programación Orientada a Objetos es un paradigma de programación que define los programas en términos de "clases de objetos". Los objetos conbinan estado (datos), comportamiento (métodos) e identidad (se pueden diferenciar entre ellos). Un objeto contiene información necesaria para definirlo (los atributos) y mecanismos de interacción con otros objetos (los métodos).
En vez de pensar en términos de procedimientos y funciones, tendremos que pensar en 'cosas' que interactúan entre ellas. Con las funciones realizamos ciertas acciones sobre los datos con intención de conseguir datos nuevos. Con los objetos crearemos ciertos entes que interactuarán entre ellos por medio de sus propios métodos.
La programación orientada a objetos tiene las siguientes características:
- Abstracción
 - 
      Cada objeto puede realizar su trabajo sin necesidad de revelar cómo lo
      realiza. Ejemplo: El objeto 
cochese podrá acelerar o frenar, pero no tenemos porqué saber cómo realiza la aceleración (eléctrico o de gasolina) o como frena (frenos o paracaídas). - Encapsulamiento
 - 
      Un objeto contiene todos los datos y métodos necesarios para trabajar
      con una entidad. Ejemplo: El objeto 
cochetendrá información (atributos) sobre su velocidad, su matrícula, número de ocupantes, etc. y métodos para acelerar, frenar, cambiar matrícula, subir y bajar gente, etc. - Principio de ocultación
 - 
      Los datos se ocultan, permitiendo su modificación únicamente mediante
      los métodos. De este modo se garantiza la integridad de los datos.
      Ejemplo: La velocidad del objeto 
cochesolo se puede cambiar mediante los métodosaceleraryfrenar; estos métodos no permitirán cambios antinaturales de velocidad (de 0 a 100 en 0 segundos) ni velocidades por encima de las permitidas en un coche (match 3). - Polimorfismo
 - 
      Diferentes objetos pueden tener métodos del mismo nombre que realicen
      tareas equivales para cada objeto. Ejemplo: El objeto 
cochey el objetomototendrán el métodoacelerar. - Herencia
 - 
      Las clases se pueden organizar en jerarquías. Cada objeto heredará todos
      los métodos y atributos de las clases a las que pertenece. Se pueden
      añadir métodos o atributos nuevos, o cambiar el comportamiento de los
      métodos.  Ejemplo: Tanto el objeto 
cochecomo el objetomotoheredarán sus características de la clasevehiculo. 
POO en PHP
Sintaxis de las clases:
  class vehículo {
      // declaración de atributos
      public $velocidad = 0; //valor por defecto
      public $velocidadMAX = 0; //valor por defecto
      // declaración de métodos
      public function acelerar($velocidad_nueva) {
          if ($velocidad_nueva > $this-velocidadMAX) {
            $this->velocidad=$this->velocidadMAX
          }
          else {
            $this->velocidad=$velocidad_nueva
          }
          echo "Velocidad actual: {$this->velocidad} Km/s"
      }
  }
Para crear nuevas instancias de las clases (objetos):
    $auto = new vehículo();
    $auto->acelerar(100);
  En php5 no está soportada la herencia múltiple, sólo se puede heredar de una
  clase. Se puede acceder a los métodos de la clase padre con parent.
  class coche extends vehículo {
      // Redefinimos el atributo velocidadMAX
      public $velocidadMAX = 140;
  }
  Si el objeto necesita ser inicializado y/o destruído de forma especial, se
  definen el constructor __construct() y el destructor __destruct().
  class coche extends vehículo {
      // Redefinimos el atributo velocidadMAX
      public $velocidadMAX = 140;
      // Añadimos atributo matrícula
      public $matricula;
      function __construct($matricula) {
        $this->matricula=$matricula;
      }
  }
  $coche1 = new coche("SS-0000-A");
Se puede controlar la visibilidad de los métodos y los atributos:
public- El elemento se puede acceder desde cualquier parte.
 protected- El elemento sólo se puede acceder desde la propia clase y sus herederas.
 private- El elemento solo se puede acceder desde la propia clase.
 
  En los atributos es obligatorio especificar visibilidad, en los métodos es
  public por defecto.
  class NombreDeClase {
    public $variable = "me pueden leer todos";
    protected $protegida ="me pueden leer los de esta clase y sus hijos";
    private $secreto="solo se me lee desde esta clase";
    //por defecto las funciones son public
    function __construct(parametros) {
      //Los '::' son necesarios para acceder a la versión
      //vieja de la función que ha sido sobreescrita;
      parent::__construct(parametros_del_padre);
    }
    protected function funcion1($prefijo,$nombre) {
      echo "Saludos! $prefijo $nombre";
    }
    function __destruct() {
      echo "Adiós mundo cruel!";
    }
  }
Cuando una clase se define como abstracta varias de sus funciones se dejan sin implementar.
    abstract class claseAbstracta {
      abstract protected function funcionabstracta();
      abstract protected function funcionabstracta2($nombre);
      public function saludo() {
        echo "hola";
      }
    }
Cuando una clase se define como interfaz, solo se definen los nombre de las funciones. Los objetos pueden implementar más de una interfaz. Es obligatorio implementar todos los métodos de la interfaz.
La principal ventaja de las interfaces es que permiten definir qué comportamiento tiene que tener un objeto determinado. Por ejemplo:
  interface modo_de_desplazamiento {
    public function acelerar();
    public function frenar();
  }
  interface transporta_carga {
    public function cargar();
    public function descargar();
  }
  interface extensible {
    public function incorporar();
    public function remover();
  }
  class coche implements modo_de_desplazamiento, transporta_carga {
    //...
  }
  class moto implements modo_de_desplazamiento {
    //...
  }
  class tren implements modo_de_desplazamiento, transporta_carga, extensible {
    //...
  }
Es posible sobrecargar métodos y variables, pero es bastante rebuscado: http://www.php.net/manual/es/language.oop5.overloading.php
  Los objetos se pueden utilizar en un foreach. Por defecto recorrerá todos
  los atributos accesibles:
  class MyClass
  {
      public $var1 = 'value 1';
      public $var2 = 'value 2';
      public $var3 = 'value 3';
      protected $protected = 'protected var';
      private   $private   = 'private var';
      function iterateVisible() {
         echo "MyClass::iterateVisible:\n";
         foreach($this as $key => $value) {
             print "$key => $value\n";
         }
      }
  }
  $class = new MyClass();
  foreach($class as $key => $value) {
      print "$key => $value\n";
  }
  echo "\n";
  $class->iterateVisible();
  //Resultado:
  //var1 => value 1
  //var2 => value 2
  //var3 => value 3
  //MyClass::iterateVisible:
  //var1 => value 1
  //var2 => value 2
  //var3 => value 3
  //protected => protected var
  //private => private var
  Podemos controlar este comportamiento implementando la interfaz Iterator:
  interface Iterator {
      public function rewind();
      //Prepara la clase para recorrerla desde el principio
      //(index=0, por ejemplo)
      public function current();
      //Devuelve el valor actual
      public function key();
      //Devuelve el indice actual
      public function next();
      //Pasa al siguiente y devuelve su valor
      public function valid();
      //True si existe un valor actual
  }
  En php están definidos los patrones Factory y Singleton:
  http://www.php.net/manual/es/language.oop5.patterns.php
  A todos los métodos que empiecen por dos marcas de subrayado __ se les
  llama métodos mágicos y se consideran reservados por php. Nos servirán para
  cambiar el comportamiento por defecto de nuestros objetos:
__constructy__destruct- Para inicializar y finalizar un objeto
 __call,__set,__get,__issety__unset- Para sobrecarga de métodos y atributos
 __sleepy__wakeup- Para serialización de objetos
 __toString- 
      Para cuando se haga un 
echodel objeto __clone- Para cuando queramos obtener una copia del objeto
 __autoload- Para la carga automática de módulos
 
Las funciones definidas como 'final' no podrán ser sobreescritas por los hijos. Las clases definidas como 'final' no podrán ser extendidas.
  final class coche {
    //...
  }
  Cuando se comparan dos objetos diferentes se consideran iguales (==) si
  tienen los mismos atributos con los mismos valores y son instancias de la
  misma clase. Se consideran idénticos (===) si son la misma instancia.
Excepciones
El tratamiento de excepciones es muy parecido a otros lenguajes. Cuando se lanza una excepción la siguiente línea de código no se ejecuta y se busca el primer bloque de captura de excepciones. Si no se captura la excepción en ningún momento php lanza un error fatal.
try {
    $error = 'Siempre lanzar error';
    throw new Exception($error);
    // El código después de una excepción no se ejecuta nunca
    echo 'Never executed';
} catch (Exception $e) {
    echo 'Capturada exception: ',  $e->getMessage(), "\n";
}
// Continuar ejecución
echo 'Hello World';
MySQL
Crear una conexión
$enlace = mysql_connect('host_mysql', 'usuario_mysql', 'contrasenya_mysql')
  or die('No pudo conectarse : ' . mysql_error());
Seleccionar una base de datos
mysql_select_db('mi_base_de_datos') or die('No pudo seleccionarse la BD.');
Ejecutar un comando SQL
$consulta  = 'SELECT * FROM mi_tabla';
$resultado = mysql_query($consulta) or die('La consulta falló: ' . mysql_error());
  dni_bueno = mysql_real_escape_string ($DNI, $enlace)
Obtener las líneas de respuesta
// Impresion de resultados en HTML
echo "<table>\n";
while ($linea = mysql_fetch_array($resultado, MYSQL_ASSOC)) {
    echo "\t<tr>\n";
    foreach ($linea as $valor_col) {
        echo "\t\t<td>$valor_col</td>\n";
    }
    echo "\t</tr>\n";
}
echo "</table>\n";
Todo junto:
// Conexion, seleccion de base de datos
$enlace = mysql_connect('host_mysql', 'usuario_mysql', 'contrasenya_mysql')
    or die('No pudo conectarse : ' . mysql_error());
echo 'Conexión exitosa';
mysql_select_db('mi_base_de_datos') or die('No pudo seleccionarse la BD.');
// Realizar una consulta SQL
$consulta  = 'SELECT * FROM mi_tabla';
$resultado = mysql_query($consulta) or die('La consulta falló: ' . mysql_error());
// Impresion de resultados en HTML
echo "<table>\n";
while ($linea = mysql_fetch_array($resultado, MYSQL_ASSOC)) {
    echo "\t<tr>\n";
    foreach ($linea as $valor_col) {
        echo "\t\t<td>$valor_col</td>\n";
    }
    echo "\t</tr>\n";
}
echo "</table>\n";
// Liberar conjunto de resultados
mysql_free_result($resultado);
// Cerrar la conexion
mysql_close($enlace);
No es necesario cerrar la conexión, se cierra sola al acabar el script.
Es habitual guardar en un fichero aparte los datos necesarios para realizar la conexión, y en otro el código necesario para establecer la conexión.
Algunas funciones útiles:
string mysql_real_escape_string ( string cadena_no_escapada [, resource id_enlace] )- 
    Escapa todos los carácteres de la cadena para que sea seguro utilizarla en
    una consulta sql (ayuda a evitar los ataques SQL Injection)
//Ejemplo de inyección SQL // Consultar la base de datos para verificar si hay una coincidencia de usuario $consulta = "SELECT * FROM usuarios WHERE usuario='{$_POST['username']}' AND password='{$_POST['password']}'"; mysql_query($consulta); // No revisamos $_POST['password'], podria ser cualquier cosa que el usuario // quiera! Por ejemplo: $_POST['username'] = 'aidan'; $_POST['password'] = "' OR ''='"; // Esto quiere decir que la consulta enviada a MySQL seria: echo $consulta; //SELECT * FROM usuarios WHERE usuario='aidan' AND password='' OR ''='' //SOLUCIÓN function comillas_inteligentes($valor) { // Retirar las barras if (get_magic_quotes_gpc()) { $valor = stripslashes($valor); } // Colocar comillas si no es entero if (!is_numeric($valor)) { $valor = "'" . mysql_real_escape_string($valor) . "'"; } return $valor; } // Realizar una consulta segura $consulta = sprintf("SELECT * FROM usuarios WHERE usuario=%s AND password=%s", comillas_inteligentes($_POST['username']), comillas_inteligentes($_POST['password'])); mysql_query($consulta); cadena mysql_error ( [int identificador_de_enlace] )yint mysql_errno ( [int identificador_de_enlace] )- 
    Para obtener el mensaje y el número de error de MySQL cuando una consulta falla. Si no
    ha habido error devuelve una cadena vacía.
$link = mysql_connect("localhost", "mysql_user", "mysql_password"); if (!mysql_select_db("nonexistentdb", $link)) { echo mysql_errno($link) . ": " . mysql_error($link). "\n"; } mysql_select_db("kossu", $link); if (!mysql_query("SELECT * FROM nonexistenttable", $link)) { echo mysql_errno($link) . ": " . mysql_error($link) . "\n"; } //esto produciría: //1049: Unknown database 'nonexistentdb' //1146: Table 'kossu.nonexistenttable' doesn't exist int mysql_insert_id ( [int identificador_de_enlace] )- 
    Para obtener el último id generado para una columna autonumérica. Si la columna
    autonumérica es de tipo bigint es mejor utilizar la función mysql 
LAST_INSERT_ID().mysql_query("INSERT INTO mytable (product) values ('kossu')"); printf("Last inserted record has id %d\n", mysql_insert_id()); int mysql_affected_rows ( [int identificador_de_enlace] )- 
    Devuelve el número de líneas afectadas en las consultas 
INSERT,UPDATEyDELETE.mysql_query('DELETE FROM mytable WHERE id < 10'); printf("Records deleted: %d\n", mysql_affected_rows()); int mysql_num_rows ( int id_resultado )- 
    Devuelve el número de líneas del resultado de un 
SELECT.$result = mysql_query("SELECT * FROM table1", $link); $num_rows = mysql_num_rows($result); 
Sesiones
Con las sesiones podemos 'recordar' datos sobre un cliente. A cada cliente se le genera un identificador de sesión, que se propagará mediante una cookie o mediante la url. Si se utilizan sesiones mediante cookies la sesión tiene que empezar antes de mostrar nada en pantalla.
session_start();
if (!isset($_SESSION['count'])) {
    $_SESSION['count'] = 0;
} else {
    $_SESSION['count']++;
}
//para borrar una variable de la sesión:
unset($_SESSION['count']);
//para borrar todas las variables de la sesión:
$_SESSION = array();
session_destroy();
Formularios y ficheros
Se tienen que enviar mediante el metodo post, y el tipo de
codificación tiene que ser multipart/form-data:
<form enctype="multipart/form-data" action="destino.php" method="post"> <label for="fichero">Enviar imagen:</label> <input id="fichero" name="fichero" type="file" /> <input type="submit" value="Enviar Imagen" /> </form>
Variables en el script destino:
$_FILES['fichero']['name']- El nombre original del fichero en la máquina cliente.
 $_FILES['fichero']['type']- El tipo mime del fichero (si el navegador lo proporciona). Un ejemplo podría ser "image/png". (mirar /etc/mime.types)
 $_FILES['fichero']['size']- El tamaño en bytes del fichero recibido.
 $_FILES['fichero']['tmp_name']- El nombre del fichero temporal que se utiliza para almacenar en el servidor el archivo recibido.
 
Funciones:
bool is_uploaded_file ( string nombre_archivo )- 
    Para verificar que el archivo ha sido subido al servidor por 
post. No queremos que alguien nos pida nuestro/etc/passwd...if (is_uploaded_file($_FILES['fichero']['tmp_name'])) { echo "El archivo ". $_FILES['fichero']['name'] ." fue cargado satisfactoriamente.\n"; echo "Mostrando su contenido\n"; readfile($_FILES['fichero']['tmp_name']); } else { echo "Posible ataque de carga de archivo: "; echo "nombre de archivo '". $_FILES['fichero']['tmp_name'] . "'."; } bool move_uploaded_file ( string nombre_archivo, string destino )- 
    Para guardarlo en el sistema de fiheros. Antes hace la misma comprobación
    que 
is_uploaded_file()move_uploaded_file($_FILES['fichero']['tmp_name'], "/path/del/servidor/para/el/fichero"); 
Autenticación mediante HTTP Basic
La autenticación se hace mediante HTTP. El navegador sacará una ventana para que introduzcamos el usuario y la contraseña. Mientras no se cierre el navegador podremos entrar en la zona privada.
No es un sistema muy seguro, ya que la contraseña se envía en texto plano (codificado en base64). Para conseguir un sitio seguro además hay que configurar SSL en el servidor (direcciones https).
if (!isset($_SERVER['PHP_AUTH_USER'])) {
	header('WWW-Authenticate: Basic realm="My Realm"');
	header('HTTP/1.0 401 Unauthorized');
	echo 'Tienes que ser un usuario registrado para poder entrar.';
	exit;
} else {
	echo "<p>Hola {$_SERVER['PHP_AUTH_USER']}.</p>";
	echo "<p>Has metido la contraseña {$_SERVER['PHP_AUTH_PW']}.</p>";
}
BasicAuthenticator es un ejemplo de implementación de autenticación con HTTP Basic.
Su uso:
require_once('BasicAuthenticator.php');
$auth = new BasicAuthenticator("Galeria");
$auth->authenticate();
Imágenes con GD
El funcionamiento general del tratamiento de imágenes es siempre parecido:
- Convertir la imagen a un formato que comprenda GD
 - Manipular la imagen
 - Convertir el formato de GD al formato que nos interese.
 
El formato que utiliza GD no tiene compresión, por lo que tendremos que andarnos con ojo con el consumo de memoria.
Las imágenes se tratan como si fueran matrices de píxeles.
Manipular imágenes
http://www.php.net/manual/es/ref.image.php
- Obtener el tamaño de una imagen
 - 
array getimagesize ( string nombre_archivo ) list($ancho, $altura, $tipo, $atr) = getimagesize("img/bandera.jpg");El array nos dará información tanto del tamaño como del tipo de imagen:- array[0]->Ancho
 - array[1]->Alto
 - array[2]->Tipo MIME (/etc/mime.types)
 - array[3]->string 'height="yyy" width="xxx"'
 
 - Crear un lienzo en blanco de 
anchura x alturapara una imagen nueva - 
resource imagecreatetruecolor ( int anchura, int altura )> <
dd> - Crear un lienzo con el contenido de una imagen
 - 
resource imagecreatefromgif ( string nombre_archivo ) resource imagecreatefromjpeg ( string nombre_archivo ) resource imagecreatefrompng ( string nombre_archivo )
Además de estas, hay otras funciones parecidas para otros formatos. Todas se utilizan de manera parecida.> <
dd> - Liberar la memoria utilizada por la imagen 
im - 
bool imagedestroy ( resource im )Como las imágenes suelen ocupar bastante memoria, es interesante ir destruyéndolas a medida que no nos hacen falta.> <
dd> - Copiar en una imagen una porción de otra
 - 
bool imagecopy ( resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h )Copia en src_im, en las coordenadas (dst_x,dst_y) la porción de la imagen fuente definida por las coordenadas (src_x,src_y) y por el ancho y alto src_w y src_h respectivamente.> <
dd> - Fundir dós imágenes
 - 
bool imagecopymerge ( resource dst_im, resource src_im, int dst_x, int dst_y, int src_x, int src_y, int src_w, int src_h, int pct )Igual queimagecopy, pero se realiza un fundido entre las dos imágenes. Sipctes 0 no se realiza ninguna acción, sipctes 100 funciona idéntico aimagecopy.> <
dd> - Rotar la imagen
 - 
resource imagerotate ( resource src_im, float angle, int bgd_color [, int ignore_transparent] )Rota la imagen src_im con el ángulo angle, rellenando los huecos nuevos con el color bgd_color. Si ignore_transparent diferente de cero se ignoran los colores transparentes.> <
dd> - Copiar una porción de una imagen en otra, redimensionándola si es necesario.
 - 
bool imagecopyresized ( resource dst_im, resource src_im, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH )Copia la imagen src_im en dst_im. Si el tamaño de destino es diferente del de origren la imagen se redimensiona.> La función <code>imagecopyresampled<
code>, con los mismos parámetros, además de redimensionar suaviza los píxeles mediante interpolación, consiguiendo mejor calidad.> <
dd> - Espejar imágenes:
 - 
    PHP no tiene una función especial para espejar imágenes, pero es sencilla
    de programar:
function imageflip($image, $mode) { $w = imagesx($image); $h = imagesy($image); $flipped = imagecreate($w, $h); if ($mode) { for ($y = 0; $y < $h; $y++) { imagecopy($flipped, $image, 0, $y, 0, $h - $y - 1, $w, 1); } } else { for ($x = 0; $x < $w; $x++) { imagecopy($flipped, $image, $x, 0, $w - $x - 1, 0, 1, $h); } } return $flipped; } 
Creación de imágenes
  Aunque bastante menos habitual, es posible que necesitemos crear imágenes
  desde cero. Generalmente será más interesante utilizar algún módulo ya
  desarrollado para que haga ese trabajo para nosotros: JpGraph para crear
  gráficos, PEAR:Image_Barcode para crear códigos de barras,
  PEAR:Text_CAPTCHA para captchas en formularios, etc.
- Definir colores que se utilizarán en la imagen
 - 
int imagecolorallocate ( resource im, int red, int green, int blue ) int imagecolorallocatealpha ( resource image, int red, int green, int blue, int alpha )El primer color que definamos será el que se utilize para el fondo. Las funciones devuelven el color de manera que podremos utilizarlo en otras funciones. El valoralphava desde el 0 (completamente opaco) hasta 127 (completamente transparente).> <
dd> - Cargar una fuente para poder escribir en la imagen
 - 
int imageloadfont ( string archivo )Sólo carga fuentes de mapas de bits> <
dd> - Escribir un caracter o una string en horizontal o en vertical
 - 
bool imagechar ( int im, int font, int x, int y, string c, int col ) bool imagecharup ( resource im, int font, int x, int y, string c, int col ) int imagestring ( int im, int font, int x, int y, string s, int col ) int imagestringup ( int im, int font, int x, int y, string s, int col )Escribe el primer carácter de la string en la posición(x,y)con el colorcol. Las versiones acabadas enupescriben en vertical. La fuente tiene que ser una cargada conimageloadfont. Las coordenadas (0,0) implican el píxel de arriba a la izquierda.> <
dd> - Escribir en un ángulo determinado con fuentes TrueType
 - 
array imagettftext ( resource imagen, float tamanyo, float angulo, int x, int y, int color, string archivo_fuente, string texto )En este caso las coordenas (0,0) implican el píxel de abajo a la izquierda. Necesita tener instalada la bibliotecaFreeType.> <
dd> - Definir el color del trazo
 - 
bool imagesetthickness ( resource image, int thickness )Se utiliza al dibujar rectángulos, polígonos, eclipses, etc.> <
dd> - Definir el formato del trazo
 - 
bool imagesetstyle ( resource image, array style )stylees un array de píxels. Los píxels se pueden obtener medianteimagecoloallocate. Las líneas, polígonos, etc. que utilicemos usaránstylecomo patrón.> <
dd> - Dibujar lineas
 - 
bool imageline ( resource im, int x1, int y1, int x2, int y2, int col )Dibuja una línea desde las coordenadas(x1,y1)hasta las coordenadas(x2,y2)con el colorcol.> Mejor mirar en la documentación de php: <a href="http:
/www.php.net/manual/es/ref.image.php">http://www.php.net/manual/es/ref.image.php 
> <
dd>- Rellenar con un color o con un patrón (imagen que se copiará en mosaico)
 - Obtener el color de un píxel determinado
 - Inclinar fuentes para formar cursivas
 - Utilizar fuentes de tipo PostScript Type1
 - Dibujar otras formas: Rectángulos, polígonos, arcos, elipses,... tanto rellenas como sin rellenar.
 - ...
 
Guardar o publicar las imágenes
  Una vez que tenemos la imagen tal y como la queremos mostrar, tendremos que
  convertirla a un formato que pueda mostrarse en el navegador. Tenemos dos
  opciones, mostrarla directamente en pantalla o guardarla en un fichero al que
  luego accederemos con la etiqueta (x)html img.
En ambos casos se utilizan las mismas funciones. Existe una función para cada tipo de fichero. Si no se especifica fichero de destino se envía directamente a pantalla. Como ejemplo, para PNG o JPEG:
- Exportar la imagen a PNG
 - 
      
bool imagepng ( resource image [, string filename] ) - Exportar la imagen a JPEG. La calidad va de 0 (peor) a 100 (mejor).
 - 
      
bool imagejpeg ( resource image [, string filename [, int quality]] ) 
Para mostrar la imagen directamente en pantalla primero hay que enviar una cabecera http para especificar el tipo MIME del fichero. Como todas las cabeceras, tiene que enviarse antes que cualquier contenido.
  header("Content-type: image/png");
Fecha y Hora
En php las fechas se trabajan mejor con marcas de tiempo. Una marca de tiempo establece la cantidad de segundos que han pasado desde un momento determinado (en PHP5, desde el 1 de enero de 1900). La ventaja de utilizar marcas de tiempo es que se pueden sumar o restar fácilmente (para saber cuanto tiempo ha pasado entre dos fechas, para calcular la fecha de dento de 3 días, etc.)
- Obtener la marca de tiemp actual
 - 
int time ( void )
Devuelve un número entero que representa la cantidad de segundos que han pasado desde el Epoch de Unix.> <
dd> - Convertir un string en una marca de tiempo
 - 
int strtotime ( string hora [, int ahora] )
Está pensado para el idioma inglés, por lo que hay que tener cuidado con las fechas (en ingles el 15 de febrero del 2007 es 02/15/2007). Las fechas según el estándar iso (igual que en euskara) se leen bien: 2007/02/15> El parametro <code>ahora<
code> permite establecer fechas relativas a un tiempo. Si no se especifica se asume la fecha actual. La stringhoratiene que ser una fecha relativa con la sintaxis de fechas de GNU. Ejemplos:$tiempo = strtotime ("2007-02-15") $tiempo = strtotime ("2007/02/15") $tiempo = strtotime ("now"), "\n"; $tiempo = strtotime ("10 September 2000"), "\n"; $tiempo = strtotime ("+1 day"), "\n"; $tiempo = strtotime ("+1 week"), "\n"; $tiempo = strtotime ("+1 week 2 days 4 hours 2 seconds"), "\n"; $tiempo = strtotime ("next Thursday"), "\n"; $tiempo = strtotime ("last Monday"), "\n";> <
dd> - Obtener la marca de tiempo de una fecha específica
 - 
int mktime ( [int hora [, int minuto [, int segundo [, int mes [, int dia [, int año ]]]]]] )
Los parametros que no se especifiquen se obtienen del momento actual. Es muy flexible, corrige fechas automáticamente. Todos estos ejemplos dan la marca de tiempo de 1998-01-01:$fecha = mktime(0, 0, 0, 12, 32, 1997) $fecha = mktime(0, 0, 0, 13, 1, 1997) $fecha = mktime(0, 0, 0, 1, 1, 1998) $fecha = mktime(0, 0, 0, 1, 1, 98)
> <
dd> - Mostrar una marca de tiempo con una string
 - 
string date ( string formato [, int marca_de_tiempo] ) string strftime ( string formato [, int marca_de_tiempo] )
Las dos sirven para lo mismo. La segunda traduce el día de la semana o el nombre del mes al idioma actual (el elegido para gettext).> La string <code>formato<
code> es una plantilla en la que se especifica cómo queremos que se muestren las fechas. En la documentación de php de la función date y de la función strftime se pueden ver los valores posibles. Ejemplos:> <pre> $tiempo=mktime(13,25,20,3,5,2007) date("Y
m/d",$tiempo) == "2007/05/03" date("H:i:s",$tiempo) == "13:25:20" date("d-m-Y A",$tiempo) == "03/05/2007 Wednesday" strftime("d-m-Y A",$tiempo) == "03/05/2007 Jueves"> <
dd> - Comprobar si una fecha es válida
 - 
bool checkdate ( int mes, int dia, int año )
DevuelveTruesi la fechadia-mes-añoes válida. Tiene en cuenta los años bisiestos. Ejemplos:checkdate(12,31,1999) = True checkdate(2,29,2008) = True checkdate(2,29,2007) = False
 
PEAR y PECL
PHP Extension and Application Repository
PEAR es:
- una librería structurada de código fuente abierto para usuarios de php.
 - Un sistema de distribución de código y mantenimiento de paquetes
 - Una guía de estilo estandar para código escrito en PHP
 - La PECL (PHP Extension Community Library)
 
El código de PEAR está dividido en paquetes. Cada paquete es un proyecto independiente, con su propio grupo de desarrollo. Los paquetes se puede instalar fácilmente mediante el instalador de PEAR. Puede haber dependencias entre paquetes.
Existe un instalador de PEAR que descarga, instala, actualiza y desinstala paquetes automáticamente. Todos los paquetes se descargan de un servidor central (pear.php.net). Existen otros repositorios de pear mantenidos por terceras personas a los que también se puede acceder mediante el instalador; a cada uno de los diferentes repositorios se le llama un canal (channel).
Para saber qué paquetes existen, leer su documentación, acceder a la página oficial de cada paquete, etc. está la web http://pear.php.net/packages.php.
PECL es un proyecto separado para distribuir extensiones de PHP (código
programado en C, no en PHP). Tiene un sistema de gestión de paquetes parecido a
PEAR y se puede utilizar el instalador pecl.
Para instalar PEAR, en sistemas Debian GNU/Linux:
aptitude install php-pear
> <p> <code>pear<
code> es muy sencillo de utilizar, parecido a apt-get, pero sin gestionar automáticamente las dependencias. No tiene manual, pero tiene la ayuda integrada:$ pear help Commands: build Build an Extension From C Source bundle Unpacks a Pecl Package channel-add Add a Channel channel-alias Specify an alias to a channel name channel-delete Remove a Channel From the List channel-discover Initialize a Channel from its server channel-info Retrieve Information on a Channel channel-update Update an Existing Channel clear-cache Clear Web Services Cache config-create Create a Default configuration file config-get Show One Setting config-help Show Information About Setting config-set Change Setting config-show Show All Settings convert Convert a package.xml 1.0 to package.xml 2.0 format cvsdiff Run a "cvs diff" for all files in a package cvstag Set CVS Release Tag download Download Package download-all Downloads each available package from the default channel info Display information about a package install Install Package list List Installed Packages In The Default Channel list-all List All Packages list-channels List Available Channels list-files List Files In Installed Package list-upgrades List Available Upgrades login Connects and authenticates to remote server logout Logs out from the remote server makerpm Builds an RPM spec file from a PEAR package package Build Package package-dependencies Show package dependencies package-validate Validate Package Consistency pickle Build PECL Package remote-info Information About Remote Packages remote-list List Remote Packages run-scripts Run Post-Install Scripts bundled with a package run-tests Run Regression Tests search Search remote package database shell-test Shell Script Test sign Sign a package distribution file uninstall Un-install Package update-channels Update the Channel List upgrade Upgrade Package upgrade-all Upgrade All Packages Usage: pear [options] command [command-options] <parameters> Type "pear help options" to list all options. Type "pear help shortcuts" to list all command shortcuts. Type "pear help <command>" to get the help for the specified command.
Correo
Función integrada en PHP
Para que funcione la función interna de correo de php tiene que estar configurado el servicio smtp (sendmail o uno que funcione con los mismos parámetros: exim, postfix, etc.) del servidor. Si queremos utilizar un servidor smtp externo habrá que utilizar el módulo PEAR::Mail
  bool mail ( string para,
              string asunto,
              string mensaje
              [, string cabeceras_adicionales
              [, string parametros_adicionales]] )
  Devuelve true si el correo fue aceptado para enviarse. Con
  cabeceras_adicionales se pueden especificar cabeceras de correo; si hay
  más de una cabecera habrá que separarlas con CRLF ("\r\n"). Un listado de
  cabeceras se puede obtener de http://people.dsv.su.se/~jpalme/ietf/mail-headers/mail-headers.html
  El parámetro parametros_adicionales define los parámetros que se le
  pasarán al comando sendmail.
Ejemplo:
$receptores = "Ales Zabala <shagi@gisa-elkartea.org>, pisoni@gisa-elkartea.org" $asunto = "Queremos migrar a Debian GNU/Linux" $mensaje= <<FINDEMAIL Hola! Por fín nos habeís convencido. Vamos a migrar a Linux! En cuanto podais vamos a tomar unas cervezas y hablamos del tema FINDEMAIL $cabeceras="Bcc: secretaria@empresa.es\r\nRepply-To: contacto@empresa.es" mail($receptores,$asunto,$mensaje,$cabeceras)
> <a id="toc27" name="toc27"
>PEAR::Mail
Para enviar correos utilizando un servidor smtp externo. Es necesario instalar los paquetes Mail y Net_SMTP
pear install Mail pear install Net_SMTP
> <p> El paquete <i>Mail<
i> soporta diferentes backends para enviar correo, por lo que para utilizarlo habrá que seguir siempre dos pasos:- Crear una nueva instancia del objeto Mail-backend apropiado
 - Enviar correo con la función 
send()del objeto 
Soporta tres backends diferentes:
- 
      Utilizando la función 
mail()integrada en PHP - sendmail
 - 
      Mediante el comando 
sendmail - smtp
 - Enviando directamente el correo a un servidor SMTP
 
> <h4>Creando el objeto apropiado con la factoría<
h4>
    Hay que utilizar la función factory(). Hay que utilizarla estáticamente
    (aunque sea de dentro de un objeto, no lo instanciamos primero).
    object &factory (string $backend [, array $params = array()])
    La string backend tiene que ser una entre:
mailsendmailsmtp
    Según el backend que utilicemos tendremos que pasarle diferentes
    opciones. Las opciones irán todas dentro de un array. Miraremos sólo las de
    smtp, por ser las más interesantes:
$params["host"]- 
        El servidor al que nos vamos a conectar. Por defecto 
localhost. $params["port"]- 
        Puerto en el que está escuchando el servidor. Por defecto 
25. $params["auth"]- 
        Si se utilizará autenticación smtp. Por defecto 
False. $params["username"]- Usuario para conectarse al servidor.
 $params["password"]- Contraseña para conectarse al servidor.
 $params["localhost"]- 
        Nombre de la máquina con la que nos vamos a presentar (parámetro del
        comando 
HELOoEHLO). Por defectolocalhost. $params["timeout"]- Timeout de la conexión SMTP.
 $params["verp"]- 
        Utilizar VERP (Variable envelope return paths) o no. Por defecto
        
False. VERP es un sistema para poder identificar el emisor de un correo aunque este haya sido redirigido múltiples veces. Útil para listas de correo. $params["debug"]- 
        Modo debug. Por defecto 
False. $params["persist"]- 
        Hacer persistente la conexión SMTP entre diferentes comandos 
send(). Por defectoFalse. 
> <h4>Enviando el correo<
h4>
    El correo se envía con el método send() del objeto que hayamos conseguido
    con la factoría.
    mixed send (mixed $recipients, array $headers, string $body)
    recipients es un array o un string con la lista de destinatarios separada
    por comas.
    headers es un array asociativo de cabeceras de correo. El índice del
    array es el nombre de la cabecera y el contenido su valor.
    body es un string con el cuerpo del mensaje.
Todo junto
    require_once('Mail.php');
    $recipients = 'shagi@gisa-elkartea.org';
    $headers['To']      = 'shagi@gisa-elkartea.org';
    $headers['From']    = 'shagi@arkham-ii.dyndns.org';
    $headers['Subject'] = 'Test message';
    $body = 'Test message';
    $params["host"] = "arkham-ii.dyndns.org";
    $params["port"] = 25;
    $params["auth"] = False;
    $params["localhost"] = "refugio";
    $params["persist"] = False;
    // Create the mail object using the Mail::factory method
    $mail_object =& Mail::factory('smtp', $params);
    $mail_object->send($recipients, $headers, $body);
> <a id="toc28" name="toc28"
>Enviando adjuntos
  Para enviar adjuntos hay que componer el cuerpo del mensaje con MIME.
  Mediante MIME podemos enviar diferentes tipos de contenido en un único
  mensaje. Para ello hay que especificar el tipo de contenido es
  multipart/mixedy el identificador que se utilizará para separar entre
  diferentes contenidos. Después cada contenido podrá tener sus propias
  cabeceras para especificar su tipo.
  Como ejemplo, vamos a ver una función que envía varios ficheros adjuntos
  mediante la función mail() integrada en PHP.
  /*
  Codigo obtenido de http://www.theukwebdesigncompany.com/articles/php-file-attachments.php
  El unico parametro complicado es filename:
    $filename = array(0=>array('file'=>'/complete/path/to/myfile.jpg',
                                 'mimetype'=>'image/jpeg',
                                 'filename'=>'myfile.jpg'));
  */
  function mail_attached($to, $from, $subject,
                         $message, $filename, $headers = '') {
    $unique_sep = md5(uniqid(time()));
    $headers .= "From: $from\n";
    $headers .= "MIME-Version: 1.0\nContent-Type: multipart/mixed;boundary=\"$unique_sep\";\n";
    $headers .= "charset=\"iso-8859-1\"\nContent-Transfer-Encoding: 7bit\n\n";
    $headers .= "--$unique_sep\n";
    $headers .= "Content-Type: text/plain; charset=\"iso-8859-1\"\n";
    $headers .= "Content-Transfer-Encoding: 7bit\n\n";
    $headers .= $message."\n\n";
    if(is_array($filename)) {
      foreach($filename as $val) {
        if(file_exists($val['file'])) {
          $headers .= "--$unique_sep\n";
          $headers .= "Content-Type: {$val['mimetype']}; name=\"{$val['filename']}\"\n";
          $headers .= "Content-Transfer-Encoding: base64\n";
          $headers .= "Content-Disposition: attachment\n\n";
          $filedata = implode(file($val['file']), '');
          $headers .= chunk_split(base64_encode($filedata));
        } else {
          echo "Error: File doesn't exist.<BR>";
        }
      }
    } else {
      echo "Error: Invalid parameter passed.<BR>";
    }
    $headers .= "--$unique_sep--\n";
    if(!mail($to, $subject, $message, $headers)) {
      echo "Error: mail() function failed!<BR>";
    }
  }
> <p> Como alternativa, podemos utilizar la clase <a href="http:
/phpmailer.sourceforge.net/">PHPMailer
  require("class.phpmailer.php");
  $mail = new PHPMailer();
  $mail->IsSMTP();                                   // send via SMTP
  $mail->Host     = "smtp1.site.com;smtp2.site.com"; // SMTP servers
  $mail->SMTPAuth = true;     // turn on SMTP authentication
  $mail->Username = "jswan";  // SMTP username
  $mail->Password = "secret"; // SMTP password
  $mail->From     = "from@email.com";
  $mail->FromName = "Mailer";
  $mail->AddAddress("josh@site.com","Josh Adams"); 
  $mail->AddAddress("ellen@site.com");               // optional name
  $mail->AddReplyTo("info@site.com","Information");
  $mail->WordWrap = 50;                              // set word wrap
  $mail->AddAttachment("/var/tmp/file.tar.gz");      // attachment
  $mail->AddAttachment("/tmp/image.jpg", "new.jpg"); 
  $mail->IsHTML(true);                               // send as HTML
  $mail->Subject  =  "Here is the subject";
  $mail->Body     =  "This is the <b>HTML body</b>";
  $mail->AltBody  =  "This is the text-only body";
  if(!$mail->Send())
  {
     echo "Message was not sent <p>";
     echo "Mailer Error: " . $mail->ErrorInfo;
     exit;
  }
  echo "Message has been sent";
JpGraph
JpGraph es una librería de PHP para dibujar gráficos en 2D. Hay dos ramas de desarrollo diferentes: la 1.x está pensada para PHP4 y la 2.x para PHP5. La rama 1.x no funciona en PHP5 y viceversa.
La documentación no está disponible directamente desde la web, está incluída
con el código fuente. Me he basado en la versión 2.2 de JpGraph:
jpgraph-2.2/docs/html/toc.html
Está liberado con doble licencia:
- QPL 1.0 (Licencia libre QT) para uso no-comercial y didáctico y para software libre.
 - Professional License para uso comercial.
 
En el apt de Debian GNU/Linux está disponible la versión para PHP4. Para instalar a mano:
- Asegurarse de que en PHP hay soporte para GD (a ser posible la versión 2).
 - Asegurarse de que en PHP hay soporte para FreeType (1 o 2) y editar el fichero jpg-config.inc.php para especificar el directorio donde están las fuentes TTF.
 - Instalar los ficheros de jpgraph básicos: 
jpgraph.phpyjpg-config.inc.php. - Instalar los ficheros que necesitemos para los tipos de gráfico que nos
    interesen (Por ejemplo 
jpgraph_line.phpyjpgraph_pie.php). Lo habitual es instalar todos. 
El formato por defecto de JpGraph es PNG, que es el que recomiendan por dar mejores resultados de calidad y compresión. También se pueden utilizar GIF y JPEG.
Para evitar las diferentes caches de manera sencilla recomiendan que el script devuelva la imagen directamente, y que se le pase un parametro inutil que sea diferente cada vez:
echo '<img src="myimagescript.php?dummy='.time().'">';
> <p> Está diseñado completamente mediante programación orientada a objetos. El funcionamiento básico es el siguiente: <
p>- Crear el gráfico
 - Especificar la escala horizontal y vertical
 - Decoracion
    
- margenes
 - sombras
 - rejilla
 - título
 - imagen de fondo
 - ...
 
 - Crear un plot
 - Añadir el plot al gráfico
 - Renderizar el gráfico
 
El gráfico se puede renderizar a tres destinos diferentes:
- A pantalla
 - 
    El comportamiento por defecto.
  
> <
dd> - A un fichero
 - 
    Hay que pasarle un parámetro más a la función 
Stroke:> <code>$graph-> Stroke( "
usr/home/peter/images/result2002.png");> <
dd> - A una variable
 - 
    Se puede obtener el resource GD del gráfico para poder hacerle un
    tratamiento posterior:
  
> <code>$handle = $graph->Stroke(_IMG_HANDLER );<
code> 
> <p> Para especificar colores: <
p>- Por nombre
 - 
    Hay un montón de nombres de colores:
    
jpgraph-2.2/docs/html/482Availablenamedcolors.html#4_8_2. Se pueden conseguir variantes más claras o más oscuras del color:> <pre> SetColor ("khaki"); SetColor ("khaki:0.5" );
/ A darker version of "khaki" SetColor("yellow:1.2"); // A slightly lighter version of "yellow"> <
dd> - Como triplete RGB o por su valor exadecimal
 - 
SetColor(array(65, 100,176)); SetColor ("#A16BFF"); - Con transparencia
 - 
SetColor( "red@0.4"); // Rojo con un 40% de transparencia 
Un ejemplo completo
En este ejemplo mostraremos un gráfico lineal en el que se mostrará la cantidad de gente que ha acudido a una cita por cada día del mes.
  Se puede ver qué hace cada función en la referencia que biene con el código
  fuente jpgraph-2.2/docs/ref/index.html
  header("image/png");
  require('jpgraph/jpgraph.php');
  require('jpgraph/jpgraph_line.php');
  //Datos del grafico
  $data = array(89,72,27,65,69,6,55,44,74,39,29,34,12,100,40,62,35,28,26,5,4,71,16,66,62,45,92,74,30,38,96);
  //Fechas para mostrar en el eje X
  $fechas = array();
  $dia = mktime(0,0,0,1,1,2007); //uno de enero del 2007
  for ($x=0;$x<31;$x++) {
    $fechas[]=date("d/m/Y", $dia);
    $dia = strtotime("+1 day", $dia);
  }
  //Crear el grafico
  $graph = new Graph(600,400);
  //Especificar la escala
  $graph->SetScale("textlin",0,$max,0,0);
  //Mostrar un tick grande cada 5 valores, y uno pequeño cada unidad
  $graph->yscale->ticks->Set(5,1);
  //Mostrar Tanto el primer tick como el ultimo
  $graph->yscale->ticks->SupressFirst(False);
  $graph->yscale->ticks->SupressLast(False);
  //El margen del grafico y su color
  $graph->img->SetMargin(40,40,40,100);
  $graph->SetMarginColor('white');
  $graph->SetShadow();
  $graph->ygrid->Show(true,true);
  $graph->xgrid->Show(true,false);
  // Crear el plot lineal
  $lineplot=new LinePlot($data);
  // Añadir el plot al grafico
  $graph->Add($lineplot);
  //El titulo del grafico
  $graph->title->Set(_("Citas diarias")." - ".date(_('d/m/Y'),time()));
  //Los tipos de letra que se utilizaran
  $graph->title->SetFont(FF_FONT1,FS_BOLD);
  $graph->yaxis->title->SetFont(FF_FONT1,FS_BOLD);
  $graph->xaxis->title->SetFont(FF_FONT1,FS_BOLD);
  //El color del eje Y
  $graph->yaxis->SetColor("blue");
  //Los valores del eje X son las fechas, giradas 90 grados.
  $graph->xaxis->SetTickLabels($fechas);
  $graph->xaxis->SetLabelAngle(90);
  //mostrar el grafico en pantalla
  $graph->Stroke();
  El resultado:
  
  
Un ejemplo con pasteles
En este caso veremos un ejemplo con el mínimo código necesario. El gráfico mostrará el porcentaje de imágenes subidas por persona.
header('image/png');
require('jpgraph/jpgraph.php');
require('jpgraph/jpgraph_pie.php');
//construccion de los arrays de los ejes x e y
$citas_por_persona = array('ane'=>5,'benito'=>2,'diego'=>3);
foreach ($citas_por_persona as $persona=>$numero) {
	$data[] = $numero;
	$nombres[] = "$persona - $numero";
}
//Creamos un grafico vacio
$graph = new PieGraph(600,400);
//El titulo del grafico (en este caso con fuentes de mapas de bit, no se
//pueden poner acentos)
$graph->title->Set(_("Imagenes por persona")." - ".date(_('Y/m/d'),time()));
//Creamos el plot de tipo tarta
$p1 = new PiePlot($data);
//Especificamos las etiquetas de cada porcion de la tarta
$p1->SetLegends($nombres);
//Añadirmos el plot al grafico
$graph->Add($p1);
//mostramos el grafico en pantalla
$graph->Stroke();
El resultado:
  
  
Logs
Guardar un registro de acciones de una aplicación web permite:
- Detectar Ataques
 - Detectar Errores comunes
 - Notificación unificada de varias aplicaciones
 - Análisis forense
 
Si además guardamos el registro en un formato estándar podremos utilizar herramientas de análisis de logs con nuestra aplicación:
- Obtención de informes
 - Notificación automática de eventos
 - Estadísticas de uso de los clientes
 
> <p> En PEAR hay un módulo pensado para guardar registros: <b>PEAR::Log<
b>. Como los gestores de logs de otros lenguajes, permite guardar los registros en diferentes medios (backends). Esto significa que utilizaremos una función simple en la aplicación ($logger->log('Mensaje', Prioridad);) que se
encargará de almacenar los logs en el formato apropiado y en el destino que le
digamos: un fichero, una base de datos, aviso por correo, syslog, etc.
Los registros se clasifican en prioridades. La aplicación luego se configurará
para que muestre sólo los mensajes de unas prioridades determinadas. De este
modo podemos tener en la aplicación notificaciones de debug
(Conexión a la base de datos establecida), de información
(Nuevo usuario registrado en la web), de error
(No se puede conectar a la base de datos), etc. Dependieno de la
configuración se mostrarán más o menos mensajes.
El uso de básico de PEAR::Log:
- Crear un objeto de tipo 
Log - Definir las prioridades que se registrarán (opcional)
 - Registar eventos utilizando la función 
log()o alguna de sus alternativas 
Crear un objeto de tipo Log
  Las instancias de Log no se crean con el método habitual
  ($log = new  Log(...)). Se utiliza la función singleton(). Esta
  función recibe como parámetro el tipo de log que queremos y nos devuelve una
  instancia de ese tipo. En vez de crear siempre instancias nuevas, si ya
  existía de antes una instancia de esas características devolverá una
  referencia a ella.
Recibe entre uno y cinco parámetros:
$handler- 
      El tipo de log que se utilizará. Por ejemplo: 
'console','file','syslog', etc. $name = ''- 
      El nombre con el que se identificará a los logs generados. Su utilidad
      depende del tipo de log que escojamos. Por ejemplo: 
'cssgallery' $ident = ''- String que se incluirá en todos los registros.
 $conf = array()- Array en el que se le pasará la configuración necesaria para el tipo de log que hayamos elegido.
 $level = PEAR_LOG_DEBUG- Prioridad mínima que se registrará con en el log.
 
> <p> Ejemplos: <
p>
  //guardar los registros al syslog
  $logger = &Log::singleton('syslog', LOG_LOCAL0, 'ident');
  //avisar de por correo
  $conf = array('subject' => 'Important Log Events');
  $logger = &Log::singleton('mail', 'webmaster@example.com', 'ident', $conf);
  //guardar los registros en un fichero
  $conf = array('mode' => 0600, 'timeFormat' => '%X %x');
  $logger = &Log::singleton('file', 'out.log', 'ident', $conf);
> <a id="toc34" name="toc34"
>Definir las prioridades que se registrarán
En PEAR::Log hay definidas ocho prioridades diferentes:
- PEAR_LOG_EMERG
 - La aplicación ha caído y es inusable.
 - PEAR_LOG_ALERT
 - Es necesaria una acción inmediata
 - PEAR_LOG_CRIT
 - Error crítico
 - PEAR_LOG_ERR
 - Error
 - PEAR_LOG_WARNING
 - Errores poco importantes (warnings)
 - PEAR_LOG_NOTICE
 - No es un error, pero es significativo
 - PEAR_LOG_INFO
 - Información
 - PEAR_LOG_DEBUG
 - Mensajes de depuración
 
  Para especificar qué tipos de mensajes queremos registrar se puede definir
  una máscara de prioridades. Cada prioridad tiene su propia máscara y se
  pueden conbinar diferentes máscaras para obtener una que agrupe las
  prioridades que nos interesan. También están definidas las máscaras
  PEAR_LOG_ALL y PEAR_LOG_NONE para comenzar con una máscara que agrupe
  a todas las prioridades o a ninguna.
  Primero hay que crear las máscaras necesarias con la función MASK(),
  luego hay que convinarlas mediante operaciones de bit a bit y finalmente hay
  que asignarselas al objeto Log con la función setMask():
//avisar sólo de notificaciones y mensajes de depuracion $mask = Log::MASK(PEAR_LOG_NOTICE) | Log::MASK(PEAR_LOG_DEBUG); //avisar de todos los mensajes menos los de informacion $mask = Log::MASK(PEAR_LOG_ALL) ^ Log::MASK(PEAR_LOG_INFO); //establecer la mascara nueva $logger->setMask($mask);
> <p> Si lo que queremos es que se muestren sólo los mensajes de una prioridad determinada o mayor, se puede utilizar la función <code>UPTO()<
code>://avisar de todos los logs de prioridad INFO o superior $mask = Log::UPTO(PEAR_LOG_INFO); $logger->setMask($mask);
> <a id="toc35" name="toc35"
>Registrar eventos
  Los eventos se notifican mediante la función log(). Esta función recibe
  como parámetro el mensaje (mejor como una string, pero no es necesario) y la
  prioridad (por defecto PEAR_LOG_INFO).
También existen funciones específicas para cada tipo de prioridad:
emerg()- Prioridad PEAR_LOG_EMERG
 alert()- Prioridad PEAR_LOG_ALER
 crit()- Prioridad PEAR_LOG_CRIT
 err()- Prioridad PEAR_LOG_ERR
 warning()- Prioridad PEAR_LOG_WARNING
 notice()- Prioridad PEAR_LOG_NOTICE
 info()- Prioridad PEAR_LOG_INFO
 debug()- Prioridad PEAR_LOG_DEBUG
 
Algunos Backends
Display
Muestra los mensajes de error en el navegador.
Parámetros:
error_prepend- String que se incluirá al principio del mensaje.
 error_append- String que se incluirá al final del mensaje.
 
Ejemplo:
    $conf = array('error_prepend' => '<div class="error"><tt>',
                  'error_append'  => '</tt></div>');
    $logger = &Log::singleton('display', '', '', $conf, PEAR_LOG_DEBUG);
    for ($i = 0; $i < 10; $i++) {
        $logger->log("Log entry $i");
    }
> <h4>File<
h4>Almacena los mensajes en un fichero.
Parámetros:
append = True- Añadir las entradas a un fichero existente o sobreescribirlo.
 mode = 0644- Permisos con los que se generará el fichero.
 eol = DEPENDE DEL SO- Secuencia de carácteres para fin de línea.
 lineFormat = "%1$s %2$s [%3$s] %4$s"- Formato en el que se guardará el mensaje. %1 es la fecha, %2 el $ident, %3 la prioridad y %4 el mensaje.
 
Ejemplo:
  $conf = array('mode' => 0600, 'timeFormat' => '%X %x');
  $logger = &Log::singleton('file', 'out.log', 'ident', $conf);
  for ($i = 0; $i < 10; $i++) {
      $logger->log("Log entry $i");
  }
> <h4>Mail<
h4>
    Envia los mensajes con la función mail() de PHP.
Parámetros:
from- 
        Valor de la cabecera 
From: subject- Asunto del correo
 preamble- Texto que se insertará al principio del mensaje
 
Ejemplo:
  $conf = array('subject' => 'Important Log Events');
  $logger = &Log::singleton('mail', 'webmaster@example.com', 'ident', $conf);
  for ($i = 0; $i < 10; $i++) {
      $logger->log("Log entry $i");
  }
> <h4>Syslog<
h4>Envía los mensajes al syslog. Como $name hay que especificar la facility de syslog que se utilizará (generalmente LOG_LOCAL0 a //LOG_LOCAL7).
Ejemplo:
    $logger = &Log::singleton('syslog', LOG_LOCAL0, 'ident');
    for ($i = 0; $i < 10; $i++) {
        $logger->log("Log entry $i");
    }
> <h4>Composite<
h4>
    Para poder enviar un mensaje a más de un tipo de log. Primero se crean los
    logs necesarios de la manera habitual y después se añaden al objeto
    Composite.
Ejemplo:
    //enviar los mensajes al navegador
    $display = &Log::singleton('display', '', 'TEST');
    //enviar los mensajes a un fichero
    $file = &Log::singleton('file', 'out.log', 'TEST');
    //crear un composite
    $composite = &Log::singleton('composite');
    //añadir los log $display y $file
    $composite->addChild($display);
    $composite->addChild($file);
    //enviar un mensaje a ambos logs
    $composite->log('This event will be logged to both handlers.');
> <a id="toc37" name="toc37"
>Registrar los logs de PHP
  function errorHandler($code, $message, $file, $line)
  {
      global $logger;
      /* Map the PHP error to a Log priority. */
      switch ($code) {
      case E_WARNING:
      case E_USER_WARNING:
          $priority = PEAR_LOG_WARNING;
          break;
      case E_NOTICE:
      case E_USER_NOTICE:
          $priority = PEAR_LOG_NOTICE;
          break;
      case E_ERROR:
      case E_USER_ERROR:
          $priority = PEAR_LOG_ERR;
          break;
      default:
          $priotity = PEAR_LOG_INFO;
      }
      $logger->log($message . ' in ' . $file . ' at line ' . $line,
                   $priority);
  }
  set_error_handler('errorHandler');
xajax
Xajax es una librería de php que permite crear aplicaciones web que utilicen AJAX sin necesidad de saber casi nada de JavaScript, ya que xajax se encarga de generar todo el JavaScript necesario. Está licenciada con la LGPL
Características:
- Genera todo el JavaScript necesario.
 - Orientado a objetos
 - Funciona con la mayoría de navegadores: Mozilla, Firefox, Safari, Internet Explorer,...
 - Puede modificar tanto el contenido de un elemento como sus propiedades, las css, etc.
 - Puede validar las entradas de los formularios asíncronamente
 - Puede añadir código javascript
 - No modifica el contenido del html si no ha cambiado
 - Cada función de php que se utilice mediante xajax puede tener un request diferente.
 - Si no se especifica dirección para hacer la peticion se utiliza automáticamente el mismo script
 - Soporte de UTF8
 - La mayor parte del javascript se carga de un fichero externo, manteniendo más limpio el código html y ahorrando ancho de banda.
 - Se puede integrar con Smarty, un sistema de plantillas para php.
 
Más información:
- Página oficial
 - http://www.xajaxproject.org/
 - Documentación
 - http://wiki.xajaxproject.org/
 - Referencia
 - http://wiki.xajaxproject.org/Documentation
 - Ejemplos
 - En el código fuente de xajax se incluye una carpeta examples con varios ejemplos. Más abajo veremos dos de ellos: hola mundo y signup.
 
Funcionamiento básico
- Incluir la clase xajax y crear una instancia nueva
 - 
require_once("xajax.inc.php"); $xajax = new xajax(); - Registrar la función a la que se llamará mediante xajax y escribirla
 - 
$xajax->registerFunction("miFuncion"); function miFuncion($arg) { // realizar algo basado en $arg, como una consulta a una // base de datos, y volcar el valor a una variable como $nuevoContenido // Inicializar el objeto xajaxResponse $objRespuesta = new xajaxResponse(); // añadir un comando a la respuesta para asignar al atributo innerHTML // del elemento con id="IdDeAlgunElemento" qualquier cosa que sea $nuevoContenido $objRespuesta ->addAssign("IdDeAlgunElemento","innerHTML", $nuevoContenido); //si queremos cambiar una propiedad css: $objRespuesta ->addAssign("IdDeAlgunElemento","style.color", "red"); //devuelve la respuesta XML generada por el objeto xajaxResponse return $objRespuesta ; } - Antes de que haya salida al navegado, hacer que xajax se encarge de las peticiones
 - 
$xajax->processRequests();
 - Llamar a la función mediante javascript
 - 
<div id="IdDeAlgunElemento">Aqui se metera la respuesta de la funcion</div> <button onclick="xajax_miFuncion("AlgunArgumento");"> 
La clase xajaxResponse
La auténtica potencia de xajax está en el objeto xajaxResponse. Se encarga
de escribir el código javascript necesario para gestionar la respuesta y
modificar el código html. Ofrece varias funciones diferentes:
- addAsign
 - 
  Reemplaza el contenido de un elemento
$objResponse->addAssign("contentDiv", "innerHTML", "Some Text"); - addAppend
 - 
  Añade contenido a un elemento al final
$objResponse->addAppend("contentDiv", "innerHTML", "Some New Text"); - addPrepend
 - 
  Añade contenido a un elemento al principio
$objResponse->addPrepend("contentDiv", "innerHTML", "Some Starting Text"); - addClear
 - 
  Borra el contenido de un elemento
$objResponse->addClear("contentDiv", "innerHTML"); - addAlert
 - 
  Ejecuta el comando javascript 
alert()$objResponse->addAlert("This is important information"); - addRedirect
 - 
  Redirige a otra web
$objResponse->addRedirect("http://www.xajaxproject.org");> <
dd> - etc. etc. etc.
 - 
  Se puede mirar la referencia de 
xajaxResponseen http://wiki.xajaxproject.org/Documentation:xajaxResponse.inc.php 
Ejemplos
Hola mundo
<?php
// helloworld.php demonstrates a very basic xajax implementation
// using xajax version 0.1 beta4
// http://xajax.sourceforge.net
require ('../xajax.inc.php');
function helloWorld($isCaps)
{
	if ($isCaps)
		$text = "HELLO WORLD!";
	else
		$text = "Hello World!";
		
	$objResponse = new xajaxResponse();
	$objResponse->addAssign("div1","innerHTML",$text);
	
	return $objResponse;
}
function setColor($sColor)
{
	$objResponse = new xajaxResponse();
	$objResponse->addAssign("div1","style.color", $sColor);
	
	return $objResponse;
}
// Instantiate the xajax object.  No parameters defaults requestURI to this page, method to POST, and debug to off
$xajax = new xajax(); 
//$xajax->debugOn(); // Uncomment this line to turn debugging on
// Specify the PHP functions to wrap. The JavaScript wrappers will be named xajax_functionname
$xajax->registerFunction("helloWorld");
$xajax->registerFunction("setColor");
// Process any requests.  Because our requestURI is the same as our html page,
// this must be called before any headers or HTML output have been sent
$xajax->processRequests();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
	<title>xajax example</title>
	<?php $xajax->printJavascript('../'); // output the xajax javascript. This must be called between the head tags ?>
</head>
<body style="text-align:center;">
	<div id="div1" name="div1"> </div>
	<br/>
	
	<button onclick="xajax_helloWorld(0)" >Click Me</button>
	<button onclick="xajax_helloWorld(1)" >CLICK ME</button>
	<select id="colorselect" name="colorselect" onchange="xajax_setColor(document.getElementById('colorselect').value);">
		<option value="black" selected="selected">Black</option>
		<option value="red">Red</option>
		<option value="green">Green</option>
		<option value="blue">Blue</option>
	</select>
	<script type="text/javascript">
	xajax_helloWorld(0); // call the helloWorld function to populate the div on load
	xajax_setColor(document.getElementById('colorselect').value); // call the setColor function on load
	</script>
</body>
</html>
Modificar un formulario mediante consulta a base de datos
<?php
//incluímos la clase ajax
require_once ('xajax/xajax.inc.php');
//instanciamos el objeto de la clase xajax
$xajax = new xajax();
$enlace = mysql_connect('localhost', 'curso', 'curso')
	  or die('No pudo conectarse : ' . mysql_error());
mysql_select_db('curso') or die('No pudo seleccionarse la BD.');
function sus_libros($entrada){
	$consulta  = "SELECT id,titulo FROM libros WHERE propietario='$entrada'";
	$resultado = mysql_query($consulta) or die('La consulta falló: ' . mysql_error());
	$salida = "";
	while ($linea = mysql_fetch_array($resultado)) {
		$salida .= "<option value=\"{$linea[0]}\">{$linea[1]}</option>";
	}
	//instanciamos el objeto para generar la respuesta con ajax
	$respuesta = new xajaxResponse();
	//escribimos en la capa con id="respuesta" el texto que aparece en $salida
	$respuesta->addAssign("libros","innerHTML",$salida);
	//tenemos que devolver la instanciación del objeto xajaxResponse
	return $respuesta;
}
//asociamos la función creada anteriormente al objeto xajax
$xajax->registerFunction("sus_libros");
//El objeto xajax tiene que procesar cualquier petición
$xajax->processRequests();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <title>Libros de cada uno</title>
<?php
  //En el <head> indicamos al objeto xajax se encargue de generar el javascript necesario
  $xajax->printJavascript("xajax/");
?>
</head>
<body>
  <form name="formulario">
    <select name="usuario" onchange="xajax_sus_libros(this.value);">
      <option value="-1">Elige una persona</option>
      <option value="1">Fulano</option>
      <option value="2">Mengano</option>
      <option value="3">Zutano</option>
    </select>
    <select name="libros" id="libros">
    </select>
  </form>
</body>
</html> 
Comprobar los valores de un formulario
- signup.common.php
 - 
<?php // signup.php, signup.common.php, signup.server.php // demonstrate a a simple implementation of a multipage signup form // using xajax version 0.2 // http://xajaxproject.org require_once ("../../xajax.inc.php"); session_start(); $xajax = new xajax("signup.server.php"); $xajax->registerFunction("processForm"); ?> - signup.php
 - 
<?php // signup.php, signup.common.php, signup.server.php // demonstrate a a simple implementation of a multipage signup form // using xajax version 0.2 // http://xajaxproject.org require_once('signup.common.php'); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <?php $xajax->printJavascript('../../'); ?> <style type="text/css"> #formWrapper{ color: rgb(255,255,255); background-color: rgb(149,67,97); width: 200px; } #title{ text-align: center; background-color: rgb(0,0,0); } #formDiv{ padding: 25px; } .submitDiv{ margin-top: 10px; text-align: center; } </style> <script type="text/javascript"> function submitSignup() { xajax.$('submitButton').disabled=true; xajax.$('submitButton').value="please wait..."; xajax_processForm(xajax.getFormValues("signupForm")); return false; } </script> </head> <body> <div id="formWrapper"> <div id="title">Create a New Account</div> <div id="formDiv"> <form id="signupForm" action="javascript:void(null);" onsubmit="submitSignup();"> <div>Username:</div><div><input type="text" name="username" /></div> <div>Password:</div><div><input type="password" name="newPass1" /></div> <div>Confirm Password:</div><div><input type="password" name="newPass2" /></div> <div class="submitDiv"><input id="submitButton" type="submit" value="continue ->"/></div> </form> </div> </div> <div id="outputDiv"> </div> </body> </html> - signup.server.php
 - 
<?php // signup.php, signup.common.php, signup.server.php // demonstrate a a simple implementation of a multipage signup form // using xajax version 0.2 // http://xajaxproject.org require_once ("signup.common.php"); function processForm($aFormValues) { if (array_key_exists("username",$aFormValues)) { return processAccountData($aFormValues); } else if (array_key_exists("firstName",$aFormValues)) { return processPersonalData($aFormValues); } } function processAccountData($aFormValues) { $objResponse = new xajaxResponse(); $bError = false; if (trim($aFormValues['username']) == "") { $objResponse->addAlert("Please enter a username."); $bError = true; } if (trim($aFormValues['newPass1']) == "") { $objResponse->addAlert("You may not have a blank password."); $bError = true; } if ($aFormValues['newPass1'] != $aFormValues['newPass2']) { $objResponse->addAlert("Passwords do not match. Try again."); $bError = true; } if (!$bError) { $_SESSION = array(); $_SESSION['newaccount']['username'] = trim($aFormValues['username']); $_SESSION['newaccount']['password'] = trim($aFormValues['newPass1']); $sForm = "<form id=\"signupForm\" action=\"javascript:void(null);\" onsubmit=\"submitSignup();\">"; $sForm .="<div>First Name:</div><div><input type=\"text\" name=\"firstName\" /></div>"; $sForm .="<div>Last Name:</div><div><input type=\"text\" name=\"lastName\" /></div>"; $sForm .="<div>Email:</div><div><input type=\"text\" name=\"email\" /></div>"; $sForm .="<div class=\"submitDiv\"><input id=\"submitButton\" type=\"submit\" value=\"done\"/></div>"; $sForm .="</form>"; $objResponse->addAssign("formDiv","innerHTML",$sForm); $objResponse->addAssign("formWrapper","style.backgroundColor", "rgb(67,149,97)"); $objResponse->addAssign("outputDiv","innerHTML","\$_SESSION:<pre>".var_export($_SESSION,true)."</pre>"); } else { $objResponse->addAssign("submitButton","value","continue ->"); $objResponse->addAssign("submitButton","disabled",false); } return $objResponse; } function processPersonalData($aFormValues) { $objResponse = new xajaxResponse(); $bError = false; if (trim($aFormValues['firstName']) == "") { $objResponse->addAlert("Please enter your first name."); $bError = true; } if (trim($aFormValues['lastName']) == "") { $objResponse->addAlert("Please enter your last name."); $bError = true; } if (!eregi("^[a-zA-Z0-9]+[_a-zA-Z0-9-]*(\.[_a-z0-9-]+)*@[a-z??????0-9]+(-[a-z??????0-9]+)*(\.[a-z??????0-9-]+)*(\.[a-z]{2,4})$", $aFormValues['email'])) { $objResponse->addAlert("Please enter a valid email address."); $bError = true; } if (!$bError) { $_SESSION['newaccount']['firstname'] = $aFormValues['firstName']; $_SESSION['newaccount']['lastname'] = $aFormValues['lastName']; $_SESSION['newaccount']['email'] = $aFormValues['email']; $objResponse->addAssign("formDiv","style.textAlign","center"); $sForm = "Account created.<br />Thank you."; $objResponse->addAssign("formDiv","innerHTML",$sForm); $objResponse->addAssign("formWrapper","style.backgroundColor", "rgb(67,97,149)"); $objResponse->addAssign("outputDiv","innerHTML","\$_SESSION:<pre>".var_export($_SESSION,true)."</pre>"); } else { $objResponse->addAssign("submitButton","value","done"); $objResponse->addAssign("submitButton","disabled",false); } return $objResponse; } $xajax->processRequests(); ?> 
Seguridad
PHP es un lenguaje de programación muy versátil: permite acceder y modificar archivos, abrir conexiones de red, acceder a bases de datos, ejecutar comandos, etc. Todas estas capacidades pueden volverse en nuestra contra en una aplicación insegura: posibilidad de mostrar el contenido de ficheros protegidos, injección de código SQL y JavaScript, utilizar nuestras máquinas como zombies, etc.
Con la configuración apropiada y con unas prácticas de programación adecuadas se pueden evitar la gran mayoría de problemas de seguridad.
Instalado como módulo de Apache
Cuando PHP está instalado como módulo de Apache, se ejecuta como el usuario de Apache (www-data en Debian GNU/Linux) por lo que obtendrá todos los permisos que tenga ese usuario. Si ese usuario puede acceder a la base de datos automáticamente (por ejemplo con bases de datos sqlite o bdb) un script de PHP podría modificar entradas en la base de datos sin necesidad de autenticarse. Para forzar la autenticación se puede utilizar la autenticación de apache (con ficheros .htaccess, por ejemplo) o incluir código de autenticación en los scripts de php.
Un usuario de Apache con demasiados permisos (por ejemplo ejecutándolo como
root) pondría en riesgo todo el sistema; por ejemplo se podría ejecutar
rm -rf / o shutdown -h now.
PHP tiene la opcion de configuracion open_basedir con la que podremos
especificar qué directorios se pueden abrir desde php. Por defecto permite
acceder a cualquier fichero.
open_basedir = /var/www/cssgallery/:/usr/share/php/
> <a id="toc44" name="toc44"
>Seguridad de sistema de ficheros
El acceso a los ficheros se controla con la seguridad de sistema de ficheros del sitema. Esto significa que si cualquier usuario del sistema puede acceder a la impresora un scrip de PHP podría enviar miles de trabajos de impresión, que puede leer el contenido de /etc/passwd, que puede escribir en /tmp (y posiblemente llenar el disco duro...), etc.
También hay que tener en cuenta que todos los usuarios de la web tienen los mismos permisos en el sistema de ficheros (los de www-data) por lo que todos podrán acceder a los mismos ficheros.
Un posible ataque de sistema de ficheros:
// eliminar un archivo del directorio personal del usuario
$nombre_usuario = $_POST['nombre_enviado_por_el_usuario'];
$directorio     = "/home/$nombre_usuario";
$archivo_a_eliminar = "$archivo_de_usuario";
unlink ("$directorio/$archivo_de_usuario");
echo "El archivo $archivo_a_eliminar ha sido eliminado";
//Supongamos que decimos que nuestro usuario es '../etc' y nuestro fichero
//'passwd'
//O si tenemos los permisos correctamente configurados, todavía se podría poner
//como usuario a '../otrousuario' y borrar los ficheros de otra persona...
Solucion: Siempre que vayamos a construir una dirección de ficheros en un
script de PHP hay que comprobar que no se utilizan carácteres peligrosos (como
el .. o permitir que pongan direcciones absolutas que empiecen por /).
Seguridad de bases de datos
Hoy en día todas las aplicaciones web utilizan bases de datos. Generalmente suelen tener información a la que es necesario proibir el acceso. PHP no controla la seguridad de estas bases de datos, como es natural, por lo que tendremos que aseguranos de que la base de datos está bien configurada y de que las consultas que le hagamos no son maliciosas.
La seguridad de una base de datos comienza con su creación y sus usuarios.
No es buena idea conectarse a la base de datos como administrador, ni como
algún usuario que tenga control completo de la base de datos. Se pueden crear
usuarios que sólo tengan permiso de lectura (sólo ejecutar select) o que
sólo tengan acceso a ciertas tablas, etc.
En nuestros scripts de PHP podemos utilizar diferentes usuarios de base de datos dependiendo de lo que queramos conseguir: La parte pública de la web podría utilizar un usuario que sólo tenga acceso a las tablas públicas y además como sólo lectura, y la parte privada podría tener acceso para modificar las tablas, por ejemplo.
También es interesante utilizar los mecanismos de la base de datos: vistas, reglas, triggers, etc. De este modo, además de aprobecharnos de la seguridad de la propia base de datos podremos tener parte del camino hecho cuando nos haga falta programar otra interfaz (por ejemplo una apliación en GTK en vez de web).
La conexión a la base de datos también merece una ojeada. Si la base de datos va a estar en el mismo servidor que la aplicación web, no tiene sentido permitir conexiones de red desde la base de datos para direcciones IP diferentes de localhost. Si la base de datos estará en otro servidor, será interesante cifrar las conexiones.
El contenido de la base de datos puede ser accedido por otros métodos
además de la interfaz web. Tendremos que cuidar cómo están almacenados. Por
ejemplo, las contraseñas de usuarios podrían no estar almacenadas en la base de
datos, guardando únicamente la salida de la función md5():
echo "password: ";
echo md5('password');
//escribe en pantalla: "password: 5f4dcc3b5aa765d61d8327deb882cf99"
$sql = "SELECT password FROM usuarios WHERE usuario = 'jon'";
$res = mysql_query($sql);
$linea = mysql_fetch_array($res);
if (linea['password'] == md5($pass)) {
  echo "has entrado";
} else {
  echo "contraseña incorrecta";
}
> <p> En la documentación de PHP se pueden ver varios ejemplos de ataques de injección de SQL: <a href="http:
/www.php.net/manual/es/security.database.sql-injection.php">http://www.php.net/manual/es/security.database.sql-injection.phpReporte de errores y depuración
Toda la información que es útil para el desarrollador es también útil para el que quiera reventar una web. Cuando una web pase a un entorno de producción, hay que tener cuidado de limpiar la información de depuración:
- Quitar todos los 
echo(),printf()etc. que hayamos puesto para depurar errores. - Configurar para que PHP no muestre los errores en el navegador con
    
error_reporting(0);o con la directiva de configuracióndisplay_errors = False. - Desinstalar los módulos de depuración de PHP (como por ejemplo 
dbg). - Borrar todos los ficheros 
prueba.php,test.php,phpinfo.php, etc. que hayamos creado para comprobaciones puntuales. - ...
 
El peligro de Register Globals
La directiva register_globals estuvo activada por defecto hasta PHP 4.2.0.
Ahora está desactivada por razones de seguridad.
Register Globals permitía que los datos enviados mediante un formulario estuvieran accesibles directamente como variables. Partiendo de este formulario:
<form action="test.php"> <input type="text" name="usuario" /> </form>
El fichero test.php podía acceder a lo que el usuario hubiera especificado
accediendo directamente a la variable $usuario:
<p>Hola <?php echo $usuario ?>!</p>
> <p> Como se puede ver en el siguiente ejemplo, esto puede resultar en un fallo de seguridad: <
p>
// definir $autorizado = true solo si el usuario ha sido autenticado
if (usuario_autenticado()) {
    $autorizado = true;
}
// Ya que no inicializamos $autorizado como false, esta podria estar
// definida a traves de register_globals, como en el caso de GET
// auth.php?autorizado=1
// De modo que cualquier persona podria verse como autenticada!
if ($autorizado) {
    include "/datos/muy/importantes.php";
}
> <a id="toc48" name="toc48"
>Los datos introducidos por el usuario
No nos podemos fiar de los datos introducidos por el usuario. Incluso aunque la aplicación vaya a utilizarse en un entorno de confianza absoluta (no me lo creo, pero bueno) puede haber diferentes razones por las que las entradas del usuario son incorrectas:
- No se ha leído lo que pone en la web y ha escrito lo que CREE que hay que poner.
 - Se ha equivocado al teclear.
 - El usuario, su mascota, su mochila, o lo que sea se ha apoyado en el teclado escribiendo texto sin sentido (como la leyenda urbana del administrador de sistemas y las tetas grandes...)
 - Un usuario está intentando entrar como si fuera otro (Pacooo, mírame a ver si tengo correo nuevoooo!)
 - ...
 
En general siempre que utilicemos lo que haya tecleado un usuario para realizar alguna acción (o sea, casi siempre...) habrá que validar el texto introducido para comprobar que no es perjudicial.
Tampoco está de más guardar un log con las actividades de los usuarios para cuando algo hay ido mal. Si el sistema dejó de funcionar el sábado por la noche, será interesante mirar qué hicieron los usuarios el sábado por la tarde...
Ocultando PHP
Lo primero que tenemos que tener en cuenta es que está más que demostrado que la seguridad por ocultación no garantiza nada. La información se puede filtrar de muchas maneras (desde trabajos de ingeniería inversa o ingeniería social hasta trabajadores cabreados).
Sin embargo, no cuesta mucho trabajo y puede aumentar un poco la seguridad del sistema.
Con Apache se puede ocultar el tipo de fichero de las páginas web:
# Hacer que el codigo PHP parezca como otro tipo de codigo AddType application/x-httpd-php .asp .py .pl # Hacer que el codigo PHP parezca como de tipos desconocidos AddType application/x-httpd-php .bop .foo .133t # Hacer que todo el codigo PHP luzca como HTML AddType application/x-httpd-php .htm .html
> <p> También se puede ocultar la información que dá Apache: <
p># Minimize 'Server' header information ServerTokens Prod # Disable server signature on server generated pages ServerSignature Off
> <a id="toc50" name="toc50"
>Varias ideas a tener en cuenta
- NO EXISTE la aplicación totalmente segura
 - Un exceso de controles de seguridad puede ser perjudicial: Los usuarios estarán incomodos e intentarán saltarse las protecciones, y el código se complicará demasiado.
 - Un sistema es apenas tan bueno como el eslabón más débil de una cadena.
 - Nunca hay que asumir qué es lo que van a hacer los usuarios. Un gato encima del teclado puede rellenar un formulario, un 'listo' puede intentar manipular las cookies en vez de utilizar las herramientas de configuración, algún cracker puede estar jugeteando con nuestra web, etc.
 
> <a id="toc51" name="toc51"
>Bibliografia y Licencias
- Manual de PHP
 - 
    
- Enlace
 - http://www.php.net/docs.php
 - Licencia
 - OPL: http://www.opencontent.org/openpub/
 - Autores
 - http://www.php.net/manual/es/preface.php#contributors
 
 - JpGraph
 - 
    
- Enlace
 - Documentación no disponible diréctamente en internet. Se puede obtener descargándose JpGrap desde http://www.aditus.nu/jpgraph/jpdownload.php
 - Licencia
 - Licencial de la documentación no especificada. El código fuente tiene licencia dual: QPL 1.0 para uso no-comercial, de software libre o educacional y JpGraph Professional License para uso comercial.
 - Autores
 - Aditus Consulting (http://www.aditus.nu/jpgraph/about.php)
 
 - PEAR::Log
 - 
    
- Enlace
 - http://www.csh.rit.edu/~jon/projects/pear/Log/guide.html
 - Licencia
 - Licencial de la documentación no especificada. El código fuente tiene licencia PHP License
 - Autores
 - Jon Parise
 
 - PEAR::Mail
 - 
    
- Enlace
 - http://pear.php.net/manual/en/package.mail.mail.php
 - Licencia
 - Licencia de la documentación no especificada. El código fuente tiene licencia PHP License / BSD
 - Autores
 - No especificados. Los del código fuente se pueden ver en http://pear.php.net/package/Mail
 
 - PHPMailer
 - 
    
- Enlace
 - http://phpmailer.sourceforge.net/
 - Licencia
 - Licencial de la documentación no especificada. El código fuente tiene licencia LGPL
 - Autores
 - No especificados. Los del código fuente se pueden ver en http://phpmailer.sourceforge.net/
 
 - xajax
 - 
    
- Enlace
 - http://wiki.xajaxproject.org/Main_Page
 - Licencia
 - GNU Free Documentation License 1.2
 - Autores
 - Es una wiki, no parece tener autores principales.
 
 
