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.php
pasa 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:
<?php
y?>
(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
-
True
yFalse
A 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-10
El 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ínea
Tanto las cadenas delimitadas por comillas dobles como mediante HEREDOC procesan el contenido de la cadena. Los\t
se convierten en tabuladores, los\n
se 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 dimensiones
Para 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
coche
se 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
coche
tendrá 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
coche
solo se puede cambiar mediante los métodosacelerar
yfrenar
; 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
coche
y el objetomoto
tendrá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
coche
como el objetomoto
heredará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:
__construct
y__destruct
- Para inicializar y finalizar un objeto
__call
,__set
,__get
,__isset
y__unset
- Para sobrecarga de métodos y atributos
__sleep
y__wakeup
- Para serialización de objetos
__toString
-
Para cuando se haga un
echo
del 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
,UPDATE
yDELETE
.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 altura
para 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. Sipct
es 0 no se realiza ninguna acción, sipct
es 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 valoralpha
va 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 enup
escriben 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 )
style
es un array de píxels. Los píxels se pueden obtener medianteimagecoloallocate
. Las líneas, polígonos, etc. que utilicemos usaránstyle
como 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 stringhora
tiene 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 )
DevuelveTrue
si la fechadia
-mes
-año
es 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:
mail
sendmail
smtp
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
HELO
oEHLO
). 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/mixed
y 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.php
yjpg-config.inc.php
. - Instalar los ficheros que necesitemos para los tipos de gráfico que nos
interesen (Por ejemplo
jpgraph_line.php
yjpgraph_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
xajaxResponse
en 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.