Características Autenticación HTTP con PHP Es posible usar la función header() para enviar un mensaje "Authentication Required" al navegador del cliente causando que se abra una ventana para ingresar usuario y password. Una vez se ha llenado el usuario y password, la URL contenida dentro del script PHP será llamada nuevamente con las variables predefinidas PHP_AUTH_USER, PHP_AUTH_PW, y AUTH_TYPE puestas por el nombre del usuario, password y el tipo de autenticación respectivamente. Esas variables predefinidas son encontradas en los arrays $_SERVER y $HTTP_SERVER_VARS. Ambos métodos de autenticación "Basic" y "Digest" (desde PHP 5.1.0) son soportados. Ver la función header() para más información. Nota: Nota de la versión de PHP Superglobals, como $_SERVER, están disponibles en PHP » 4.1.0. Un fragmento de ejemplo de un script el cual podría forzar la autenticación en una página es el siguiente: Ejemplo #1 Ejemplo de Autenticación HTTP Basic <?php if (!isset($_SERVER['PHP_AUTH_USER'])) { header('WWW-Authenticate: Basic realm="My Realm"'); header('HTTP/1.0 401 Unauthorized'); echo 'Texto a enviar si el usuario pulsa el botón Cancelar'; exit; } else { echo "<p>Hola {$_SERVER['PHP_AUTH_USER']}.</p>"; echo "<p>Tu ingresaste {$_SERVER['PHP_AUTH_PW']} como tu password.</p>"; } ?> Ejemplo #2 Ejemplo de Autenticación HTTP Digest Este ejemplo muestra como implementar un script PHP de autenticación Digest. Para más información leer » RFC 2617. <?php $realm = 'Area restringida'; //user => password $users = array('admin' => 'mypass', 'guest' => 'guest'); if (empty($_SERVER['PHP_AUTH_DIGEST'])) { header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Digest realm="'.$realm. '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"'); die('Texto a enviar si el usuario pulsa el botón Cancelar'); } // analiza la variable PHP_AUTH_DIGEST if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])) die('Datos Erroneos!'); // Generando una respuesta valida $A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]); $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']); $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop']. ':'.$A2); if ($data['response'] != $valid_response) die('Datos Erroneos!'); // ok, usuario & password validos echo 'Estas logueado como: ' . $data['username']; // function to parse the http auth header function http_digest_parse($txt) { // proteger contra datos perdidos $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 're sponse'=>1); $data = array(); $keys = implode('|', array_keys($needed_parts)); preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ ORDER); foreach ($matches as $m) { $data[$m[1]] = $m[3] ? $m[3] : $m[4]; unset($needed_parts[$m[1]]); } return $needed_parts ? false : $data; } ?> Nota: Nota de Compatibilidad Hay que ser cuidadoso cuando se programan las líneas del HTTP header. Para garantizar la mayor compatibilidad con todos los clientes, la palabra "Basic" debe ser escrita con mayúsculas "B", el string real debe ser encerrado en comillas dobles (no simples), y exactamente un espacio debe preceder el código401 en la línea del header HTTP/1.0 401. Los parámetros de autenticación deben ser separados por comas como se vió en el ejemplo resumido anterior. En lugar de imprimir simplemente PHP_AUTH_USER y PHP_AUTH_PW, como se hizo en el ejemplo anterior, se debería chequear el usuario y password para validar. Talvez enviando una consulta a una base de datos, o buscando el usuario en un archivo dbm. Cuidado con errores con el Internet Explorer. Parece ser muy quisquilloso con el orden de los headers. Enviando el header WWW-Authenticate antes que el header HTTP/1.0 401 parece ser un truco por ahora. A partir de PHP 4.3.0, en orden de prevenir que alguien escriba un script el cual revele el password para una página que fué autenticada con un mecanismo externo tradicional, las variables PHP_AUTH no deberán ser colocadas si la autenticación externa esta habilitada para esa página en particular y si safe modeesta habilitado. Independientemente, REMOTE_USER puede ser usado para identificar al usuario autenticado externamente. Así, se podrá usar$_SERVER['REMOTE_USER']. Nota: Nota de configuración PHP usa la presencia de una directiva AuthType para determinar si una autenticación externa esta en uso. Nótese, sin embargo, que lo anterior no impide que alguien quien controle una URL no autenticada pueda robar passwords de URL's autenticadas en el mismo servidor. Tanto Netscape Navigator e Internet Explorer borrarán el caché de la ventana de autenticación del navegador local después de recibr una respuesta 401. Esto puede "desloguear" efectivamente a un usuario, forzandolo a reingresar su usuario y password. Algunas personas usan esto para "hacer esperar" logueos, o proveer un botón de "deslogueo". Ejemplo #3 Ejemplo de Autenticación HTTP forzando a un nuevo usuario/password <?php function authenticate() { header('WWW-Authenticate: Basic realm="Test Authentication System"'); header('HTTP/1.0 401 Unauthorized'); echo "Debes ingresar un login ID y password validos para accesar a este recurso\n"; exit; } if (!isset($_SERVER['PHP_AUTH_USER']) || ($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) { authenticate(); } else { echo "<p>Bienvenido: " . htmlspecialchars($_SERVER['PHP_AUTH_USER']) . "<br />"; echo "Anterior: " . htmlspecialchars($_REQUEST['OldAuth']); echo "<form action='' method='post'>\n"; echo "<input type='hidden' name='SeenBefore' value='1' />\n"; echo "<input type='hidden' name='OldAuth' value=\"" . htmlspecialchars($_SERVER['PHP_AUTH_USER ']) . "\" />\n"; echo "<input type='submit' value='Re Authenticate' />\n"; echo "</form></p>\n"; } ?> Este comportamiento no es requerido por la autenticación HTTP Basic estandar, así que se debería depender de esto. Probando con Lynx mostrará que Lynxno limpia las credenciales de autenticación con una respuesta 401 del servidor, asi que presionando back y luego forward abrirá el recurso nuevamente si estos no han cambiado. Sin embarogo, el usuario puede presionar la tecla '_' para limpiar su información de autenticación. También notese que hasta PHP 4.3.3, la autenticación HTTP no trabajaba usando Microsoft IIS con la versión CGI de PHP, una limitación de IIS. Para hacer funcionar PHP 4.3.3 o mayor, se debe editar la configuracion de IIS "Directory Security". Hacer click en "Edit" y solo chequear "Anonymous Access", todos los demas campos dejarlos sin chequear. Otra limitación si se esta usando el módulo IIS (ISAPI) y PHP 4, no se debería usar las variables PHP_AUTH_* pero en su lugar, la variableHTTP_AUTHORIZATION esta disponible. Por ejemplo, considerar el siguiente código: list($user, $pw) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6))); Manejo de XForms » XForms define una variación de los webforms tradicionales los cuales permiten ser usados en una gran variedad de plataformas y navegadores o inclusive en medios no tradicionales como los documentos PDF. La primera diferencia clave con XForms es la forma en que los formularios son enviados al cliente. » XForms for HTML Authors contiene una descripción detallada de como crear XForms, para el fin de este tutorial únicamente veremos un ejemplo simple. Ejemplo #1 Un formulario simple de búsqueda con XForms <h:html xmlns:h="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/2002/xforms"> <h:head> <h:title>Search</h:title> <model> <submission action="http://example.com/search" method="post" id="s"/> </model> </h:head> <h:body> <h:p> <input ref="q"><label>Find</label></input> <submit submission="s"><label>Go</label></submit> </h:p> </h:body> </h:html> El formulario anterior muestra una caja de texto (llamada q), y un botón submit. Cuando el botón submit es presionado, el formulario será enviado a la página referenciada por action. Aquí es donde comienza a verse diferente desde el punto de vista de la aplicación web. En un formulario normal HTML, los datos serían enviados comoapplication/x-www-form-urlencoded, en el mundo XForms, esta información es enviada como datos formateados enXML. Si se ha elegido trabajar con XForms, entonces probablemente se quiera esos datos como XML, en ese caso, ver en $HTTP_RAW_POST_DATA donde se puede encontrar el XML generado por el navegador el cual se puede pasar en el motor XSLT favorito o parseador. Si no se esta interesado en formatear y solo se desea que la data sea cargada en la variable tradicional $_POST, se puede instruir al navegador del cliente para enviarlos como application/xwww-form-urlencoded cambiando el atributo method a urlencoded-post. Ejemplo #2 Usando un Xform para rellenar $_POST <h:html xmlns:h="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/2002/xforms"> <h:head> <h:title>Busqueda</h:title> <model> <submission action="http://example.com/search" method="urlencoded-post" id="s"/> </model> </h:head> <h:body> <h:p> <input ref="q"><label>Buscar</label></input> <submit submission="s"><label>Ir</label></submit> </h:p> </h:body> </h:html> Manejando la carga de archivos Carga con el método POST Esta característica permite que los usuarios envien tanto archivos de texto como binarios. Con la autenticación de PHP y las funciones de manipulación de archivos, se tiene completo control sobre quién está autorizado a cargar y que hay que hacer con el archivo una vez que se ha cargado. PHP es capaz de recibir cargas de archivos de cualquier navegador compatible con RFC-1867. Nota: Configuraciones Relacionadas Ver también las directivas file_uploads, upload_max_filesize, upload_tmp_dir, post_max_size y max_input_time en p hp.ini PHP también soporta el método PUT para la carga como lo utilizan los clientes Netscape Composer y Amaya del W3C. Ver el soporte del método PUT para más detalles. Ejemplo #1 Formulario para la carga de archivos Una página de carga de archivos puede ser construida mediante la creación de un formulario especial el cual se vería algo como esto: <!-- El tipo de codificación de datos, enctype, se DEBE especificar como a continuación --> <form enctype="multipart/form-data" action="__URL__" method="POST"> <!-- MAX_FILE_SIZE debe preceder el campo de entrada de archivo --> <input type="hidden" name="MAX_FILE_SIZE" value="30000" /> <!-- El nombre del elemento de entrada determina el nombre en el array $_FILES --> Enviar este archivo: <input name="userfile" type="file" /> <input type="submit" value="Send File" /> </form> El __URL__ en el ejemplo anterior se debe sustituir y apuntar a un archivo PHP. El campo oculto MAX_FILE_SIZE (medido en bytes) debe preceder al campo de entrada de archivo y su valor es el tamaño máximo de archivo aceptado por PHP. Este elemento del formulario se debe usar siempre, ya que evita a los usuarios la molestia de esperar a que un gran archivo sea transferido sólo para descubrir que era demasiado grande y falló la transferencia. Tener en cuenta: engañar a esta configuración en el lado del navegador es muy fácil, así que nunca se debe confiar en que archivos con un tamaño mayor serán bloqueados por esta característica. Es simplemente una característica de conveniencia para los usuarios en el lado cliente de la aplicación. Sin embargo, la configuración de PHP (en el lado del servidor) para un máximo de tamaño, no puede ser engañada. Nota: Asegúrese de que el formulario de subida de archivos tiene el atributo enctype="multipart/formdata" de lo contrario la carga de archivos no funcionará. El $_FILES global existe a partir de PHP 4.1.0 (Usar $HTTP_POST_FILES en su lugar si se utiliza una versión anterior). Este array contendrá toda la información sobre el archivo cargado. El contenido de $_FILES del formulario de ejemplo es el siguiente. Tenga en cuenta que esto asume la utilización del nombre del archivo cargado userfile, tal como se utiliza en el script de ejemplo anterior. Este puede ser cualquier nombre. $_FILES['userfile']['name'] El nombre original del archivo en la máquina cliente. $_FILES['userfile']['type'] El tipo mime del archivo, si el navegador proporciona esta información. Un ejemplo podría ser "image/gif". Este tipo mime, sin embargo no se verifica en el lado de PHP y por lo tanto no se garantiza su valor. $_FILES['userfile']['size'] El tamaño, en bytes, del archivo subido. $_FILES['userfile']['tmp_name'] El nombre temporal del archivo en el cual se almacena el archivo cargado en el servidor. $_FILES['userfile']['error'] El código de error asociado a esta carga de archivo. Este elemento fue añadido en PHP 4.2.0 Los archivos, por defecto se almacenan en el directorio temporal por defecto del servidor, a menos que otro lugar haya sido dado con la directivaupload_tmp_dir en php.ini. El directorio por defecto del servidor puede ser cambiado mediante el establecimiento de la variable de entorno TMPDIR en el entorno en el cual se ejecuta PHP. Configurarlo usando putenv() desde un script PHP no funcionará. Esta variable de entorno también se puede utilizar para asegurarse de que las demás operaciones están trabajando sobre los archivos cargados. Ejemplo #2 Validación de la carga de archivos Ver también las entradas para las funciones is_uploaded_file() y move_uploaded_file() para más información. El siguiente ejemplo procesaría la carga de archivo que vendría de un formulario. <?php // En versiones de PHP anteriores a 4.1.0, $HTTP_POST_FILES debe utilizarse en lugar // de $_FILES. $uploaddir = '/var/www/uploads/'; $uploadfile = $uploaddir . basename($_FILES['userfile']['name']); echo '<pre>'; if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) { echo "El archivo es válido y fue cargado exitosamente.\n"; } else { echo "¡Posible ataque de carga de archivos!\n"; } echo 'Aquí hay más información de depurado:'; print_r($_FILES); print "</pre>"; ?> El script PHP que recibe el archivo cargado, debe implementar cualquier lógica que sea necesaria para determinar qué se debe hacer con el archivo subido. Se puede, por ejemplo, utilizar la variable $_FILES['userfile']['size'] para descartar cualquier archivo que sea demasiado pequeño o demasiado grande. Se podría utilizar la variable $_FILES['userfile']['type'] para descartar cualquier archivo que no corresponda con un cierto criterio de tipo, pero usando esto sólo como la primera de una serie de verificaciones, debido a que este valor está completamente bajo el control del cliente y no se comprueba en el lado de PHP. A partir de PHP 4.2.0, se puede usar $_FILES['userfile']['error'] y el planear la lógica de acuerdo con los códigos de error. Cualquiera que sea la lógica, se debe borrar el archivo del directorio temporal o moverlo a otra parte. Si ningún archivo es seleccionado para realizar la carga en el formulario, PHP devolverá $_FILES['userfile']['size'] como 0, y $_FILES['userfile']['tmp_name']como ninguno. El archivo será borrado del directorio temporal al final de la solicitud si no se ha movido o renombrado. Ejemplo #3 Cargando un array de archivos PHP soporta las funcionalidades array de HTML incluso con archivos. <form action="" method="post" enctype="multipart/form-data"> <p>Pictures: <input type="file" name="pictures[]" /> <input type="file" name="pictures[]" /> <input type="file" name="pictures[]" /> <input type="submit" value="Send" /> </p> </form> <?php foreach ($_FILES["pictures"]["error"] as $key => $error) { if ($error == UPLOAD_ERR_OK) { $tmp_name = $_FILES["pictures"]["tmp_name"][$key]; $name = $_FILES["pictures"]["name"][$key]; move_uploaded_file($tmp_name, "data/$name"); } } ?> Una barra de progreso de carga de archivos puede ser implementada mediante Session Upload Progress. Explicación de los mensajes de error A partir de PHP 4.2.0, PHP devuelve un código de error apropiado, junto con el array del archivo. El código de error se puede encontrar en el segmento errordel array del archivo que PHP crea durante la subida del archivo. En otras palabras, el error podría encontrarse en $_FILES['userfile']['error']. UPLOAD_ERR_OK Valor: 0; No hay error, archivo subido con éxito. UPLOAD_ERR_INI_SIZE Valor: 1; El archivo subido excede la directiva upload_max_filesize en php.ini. UPLOAD_ERR_FORM_SIZE Valor: 2; El archivo subido excede la directiva MAX_FILE_SIZE que fue especificada en el formulario HTML. UPLOAD_ERR_PARTIAL Valor: 3; El archivo subido fue sólo parcialmente cargado. UPLOAD_ERR_NO_FILE Valor: 4; Ningún archivo fue subido. UPLOAD_ERR_NO_TMP_DIR Valor: 6; Falta la carpeta temporal. Introducido en PHP 4.3.10 y PHP 5.0.3. UPLOAD_ERR_CANT_WRITE Valor: 7; No se pudo escribir el archivo en el disco. Introducido en PHP 5.1.0. UPLOAD_ERR_EXTENSION Valor: 8; Una extensión de PHP detuvo la carga de archivos. PHP no proporciona una forma de determinar cual extensión causó la parada de la subida de archivos; el examen de la lista de extensiones cargadas con phpinfo() puede ayudar. Introducido en PHP 5.2.0. Dificultades comunes El item MAX_FILE_SIZE no puede especificar un tamaño de archivo mayor que el que ha sido configurado en el upload_max_filesize en el archivo php.ini. Por defecto es 2 megabytes. Si hay un límite de memoria activado, un memory_limit más grande puede ser necesario. Asegurarse de configurar un memory_limit lo suficientemente grande. Si el max_execution_time es demasiado pequeño, la ejecución del script puede excederse de este valor. Asegurarse de configurar un max_execution_time lo suficientemente grande. Nota: max_execution_time sólo afecta al plazo de ejecución del propio script. Todo el tiempo gastado en actividades que tengan lugar por fuera de la ejecución del script, tales como las llamadas al sistema usando system(), la función sleep(), las consultas a base de datos, el tiempo que tarda el proceso de subida de archivos, etc., no se incluye cuando se determina el tiempo máximo que el script ha estado funcionando. Advertencia max_input_time establece el tiempo máximo, en segundos, al script se le permite recibir información, esto incluye la subida de archivos. Para archivos grandes o múltiples, o usuarios con conexiones más lentas, el valor predeterminado de 60 segundos puede ser excedido. Si post_max_size se establece demasiado pequeño, los archivos grandes no pueden ser cargados. Asegurarse de configurar post_max_size lo suficientemente grande. A partir de PHP 5.2.12, la configuración max_file_uploads controla el número máximo de archivos que se pueden cargar en una petición. Si más archivos que ese límite son subidos, entonces $_FILES parará de procesar archivos una vez se alcanza el límite. Por ejemplo, si max_file_uploads se establece en 10, entonces $_FILES nunca contendrá más de 10 elementos. No validar sobre cual archivo se opera puede significar que los usuarios pueden acceder a información sensible en otros directorios. Por favor tener en cuenta que el CERN httpd parece quitar todo lo que empieza con primer espacio en blanco en la cabecera de tipo de contenido mime que recibe desde el cliente. Mientras este sea el caso, el CERN httpd no soportará la funcionalidad de carga de archivos. Debido a la gran cantidad de estilos de lista de directorios, no podemos garantizar que los archivos con nombres exóticos (como el que contiene espacios en blanco) se manejen adecuadamente. Un desarrollador no debe mezclar los campos input normales con los de carga de archivos en la misma variable de formulario (mediante un nombre de inputcomo foo[]). Subida de múltiples archivos Múltiples archivos pueden ser subidos utilizando diferentes name para los input. También es posible subir múltiples archivos simultáneamente y tener la información organizada automáticamente en arrays. Para ello, es necesario utilizar la misma sintaxis de sumisión de array en el formulario HTML como se hace con múltiples selects y checkboxes: Ejemplo #1 Subida de múltiples archivos <form action="file-upload.php" method="post" enctype="multipart/form-data"> Enviar estos archivos:<br /> <input name="userfile[]" type="file" /><br /> <input name="userfile[]" type="file" /><br /> <input type="submit" value="Send files" /> </form> Cuando el formulario de arriba se remite, los arrays $_FILES['userfile'], $_FILES['userfile']['name'] y $_FILES['userfile']['size'] serán inicializados (así como en$HTTP_POST_FILES para las versiones de PHP anteriores a 4.1.0). Cuando register_globals está activado, globales para los archivos subidos también se inicializan. Cada uno de estos será un array indexado numéricamente de los valores correspondientes a los archivos remitidos. Por ejemplo, suponga que los nombres de archivo /home/test/review.html y /home/test/xwp.out son remitidos. En este caso,$_FILES['userfile']['name'][0]contendría el valor review.html, y $_FILES['userfile']['name'][1] contendría el valor xwp.out. De manera similar, $_FILES['userfile']['size'][0] contendría el tamaño del archivo review.html y así sucesivamente. $_FILES['userfile']['name'][0], $_FILES['userfile']['tmp_name'][0], $_FILES['userfile']['size'][0], y $_FILES['userfile']['type'][0] también son establecidos. Advertencia A partir de PHP 5.2.12, la configuración max_file_uploads actúa como un límite en el número de archivos que se pueden subir en una petición. Se necesita asegurar que el formulario no intenta cargar más archivos que este límite en una petición. Soporte del método PUT PHP ofrece soporte para el método HTTP PUT utilizado por algunos clientes para almacenar archivos en un servidor. Las peticiones PUT son mucho más simples que una carga de archivos mediante solicitudes POST y se ven algo como esto: PUT /path/filename.html HTTP/1.1 Esto normalmente significa que el cliente remoto quiere guardar el contenido que sigue como: /path/filename.html en el árbol web. Obviamente no es una buena idea para Apache o PHP dejar automáticamente a todo el mundo que pueda sobrescribir cualquier archivo del árbol web. Para manejar esta solicitud se debe primero decir al servidor web que se desea que cierto script de PHP maneje la petición. En Apache se hace esto con la directiva de Script. Se puede colocar casi en cualquier parte del archivo de configuración de Apache. Un lugar común es dentro de un bloque <Directory> o tal vez dentro de un bloque<VirtualHost>. Una línea como ésta haría el truco: Script PUT /put.php Esto le dice a Apache que envíe todas peticiones PUT para URIs que coincidan con el contexto en el cual se pone esta línea en el script put.php. Esto asume, por supuesto, que se tiene habilitado PHP para la extensión .php y que PHP está activo. El recurso de destino para todas las solicitudes PUT a este script tiene que ser en propio script, el archivo subido no debe tener un nombre de archivo. Con PHP entonces se haría algo como lo siguiente en el put.php. Esto copiaría el contenido del archivo subido al archivo myputfile.ext en el servidor. Es probable que se deseen realizar algunas verificaciones y/o autenticar al usuario antes de realizar esta copia de archivo. Ejemplo #1 Guardando archivos HTTP PUT <?php /* datos PUT vienen en en el flujo de entrada estándar $putdata = fopen("php://input", "r"); */ /* Abre un archivo para escribir */ $fp = fopen("myputfile.ext", "w"); /* Leer los datos de 1 KB a la vez y escribir en el archivo */ while ($data = fread($putdata, 1024)) fwrite($fp, $data); /* Cerrar los flujos */ fclose($fp); fclose($putdata); ?> Usando archivos remotos Siempre que allow_url_fopen este habilitado en php.ini, se pueden usar URLs HTTP y FTP con la mayoría de las funciones que toman un nombre de archivo como parámetro. Además, las URLs pueden ser usadas con las declaraciones include, include_once, require y require_once (desde PHP 5.2.0,allow_url_include debe ser habilitado para esto). Ver Protocolos y Envolturas soportados para más información de los protocolos soportados por PHP. Nota: En PHP 4.0.3 y anteriores, con el fin de usar capas de URL, se requirió de configurar PHP usando la opción de configuración --enable-url-fopen-wrapper . Nota: Las versiones Windows de PHP más nuevas a la PHP 4.3 no soportan acceso remoto a los archivos por las siguientes funciones: include, include_once,require, require_once, y las funciones imagecreatefromXXX en la extensión Funciones de GD e Imágenes Por ejemplo, se puede usar esto para abrir un archivo en un web server remoto, analizar la salida de los datos que se quieren, y entonces usar esos datos en una consulta a la base de datos, o simplemente para mostrarlos en un estilo que coincida con el resto del sitio web. Ejemplo #1 Obteniendo el titulo de una página remota <?php $file = fopen ("http://www.example.com/", "r"); if (!$file) { echo "<p>Imposible abrir el archivo remoto.\n"; exit; } while (!feof ($file)) { $line = fgets ($file, 1024); /* Esto solo trabaja si el titulo y sus tags estan en una línea */ if (preg_match ("@\<title\>(.*)\</title\>@i", $line, $out)) { $title = $out[1]; break; } } fclose($file); ?> También se pueden escribir archivos en un servidor FTP (considerando que se esta conectado como un usuario con los permisos de acceso correctos). Se pueden crear únicamente archivos nuevos usando este método. Si se intenta sobreescribir un archivo que ya existe, la llamada a la función fopen() fallará. Para conectarse como un usuario diferente a 'anonymous', se necesita especificar el usuario (y posiblemente el password) con la URL, tal como 'ftp://user:[email protected]/path/to/file'. (Se puede usar la misma sintaxis para accesar archivos vía HTTP cuando se requiere autenticación básica). Ejemplo #2 Almacenando datos en un servidor remoto <?php $file = fopen ("ftp://ftp.example.com/incoming/outputfile", "w"); if (!$file) { echo "<p>Imposible abrir el archivo remoto para escritura.\n"; exit; } /* Escribir los datos aqui. */ fwrite ($file, $_SERVER['HTTP_USER_AGENT'] . "\n"); fclose ($file); ?> Manejo de Conexiones Internamente en PHP se mantiene un estatus de la conexión. Hay 3 posibles estados: 0 - NORMAL 1 - ABORTED 2 - TIMEOUT Cuando un script en PHP esta ejecutándose, normalmente esta activo el estado NORMAL. Si el cliente remoto se desconecta, el flag ABORTED es activado. Un cliente remoto se desconecta usualmente porque el usuario presiona su botón STOP. Si el tiempo limite PHP-imposed (ver set_time_limit()) es activado, el flag TIMEOUT se activa. Se puede decidir si se desea que un cliente se desconecte o no a causa de que se aborte el script. Algunas veces es útil que los scripts se ejecuten inclusive si ya no hay un navegador recibiendo la salida. El comportamiento por defecto es que el script sea abortado cuando el cliente remoto se desconecte. Este comportamiento puede ser establecido a través de la directiva ignore_user_abort en php.ini así como a través de la directiva correspondiente de Apache enhttpd.conf php_value ignore_user_abort o con la función ignore_user_abort(). Si se decide no decirle a PHP que ignore abortar al usuario y el usuario aborta, el script terminará. La única excepción es si se tiene registrada una función de cierre usando register_shutdown_function(). Con una función de cierre, cuando el usuario remoto activa el botón STOP, la próxima vez que el script intente mostrar algo, PHP detectará que la conexión fue abortada y la función de cierre es llamada. Esta función de cierre también es llamada al final del script cuando termina normalmente, así que para hacer algo diferente en el caso de que un cliente se desconecte usar la función connection_aborted(). Esta función retornará TRUE si la conexión fue abortada. El script puede ser terminado también por el temporizador incorporado en los scripts. El tiempo por defecto es de 30 segundos. Puede ser cambiado usando la directiva max_execution_time de php.ini o la correspondiente directiva php_value max_execution_time de Apache httpd.conf así como con la funciónset_time_limit(). Cuando el temporizador expira el script será abortado y así como el caso del cliente anterior que se desconecto, si la función de cierre ha sido registrada ésta será llamada. Dentro de esta función de cierre se puede comprobar para ver si el timeout causa la función de cierre llamando a la funciónconnection_status(). Esta función retornará 2 si el timeout causo la llamada a la función de cierre. Una cosa a notar es que ambos estados ABORTED y TIMEOUT pueden ser activados al mismo tiempo. Esto es posible si se le dice a PHP que ignore el aborto del usuario. PHP notará de hecho que un usuario podría haber roto la conexión, pero el script se mantendrá ejecutándose. Si este activa el limite de tiempo será abortado y la función de cierre, si existe, será llamada. A este punto, se encontrará que connection_status() retorna 3. Conexiones Persistentes a Bases de Datos Las conexiones persistentes son enlaces que no se cierran cuando la ejecución del script termina. Cuando una conexión persistente es solicitada, PHP chequea si ya existe una conexión persistente idéntica (que fuera abierta antes) - y si existe, la usa. Si no existe, crea el enlace. Una conexión "Idéntica" es una conexión que fue abierta por el mismo host, con el mismo usuario y el mismo password (donde sea aplicable). Las personas que no estan completamente familiarizadas con como trabajan los web servers y la distribución de carga podrían equivocarse en usar conexiones persistentes para lo que no son. En particular, no le da la habilidad de abrir "sesiones de usuario" en el mismo enlace, no le da la habilidad de construir una transacción eficiente, pero no hacen muchas otras cosas más. De hecho, para ser extremadamente claros acerca de esto, las conexiones persistentes no dancualquier otra funcionalidad que no fuera posible hacerse con sus hermanas no-persistentes. ¿Por qué? Esto tiene que ver con la forma en que los web servers trabajan. Hay 3 formas en las cuales el web server puede generar paginas web usando PHP. El primer método es usar PHP como una "capa" CGI. Cuando se ejecuta de esta forma, una instancia del interprete PHP es creado y destruido por cada solicitud de la página (para una página PHP) al web server. Porque es destruido después de cada solicitud, cualquier recurso que se necesite (como un enlace a un servidor de base de datos SQL) son cerradas cuando son destruidas. En este caso, no se gana nada intentando usar conexiones persistentes -- simplemente no persisten. La segunda, y mas popular, es ejecutar PHP como un modulo en un servidor multiproceso, el cual actualmente solo incluye a Apache. Un servidor multiproceso normalmente tiene un proceso (el padre) el cual coordina un grupo de procesos (los hijos) los cuales no trabajan sirviendo páginas web. Cuando una solicitud viene desde el cliente, es manejada por uno de los hijos el cual no este sirviendo a otro cliente. Esto significa que cuando el mismo cliente hace una segunda solicitud al servidor, podría ser servido por un proceso hijo diferente a la primera vez. Cuando se abre una conexión persistente, cada petición siguiente de servicios SQL puede reusar la misma conexión establecida al servidor SQL. El último método es usar PHP como un plug-in para un servidor web multihilos. Actualmente PHP 4 tiene soporte para ISAPI, WSAPI, y NSAPI (en Windows), los cuales permiten usar PHP como un plug-in multihilo en servidores como Nestcape FastTrack (iPlanet), Microsoft Internet Information Server (IIS), y O'Reilly's WebSite Pro. El comportamiento es esencialmente el mismo para el modelo multiproceso descrito antes. Si las conexiones persistentes no tienen ninguna funcionalidad adicional, ¿Para que son buenas? La respuesta es extremadamente simple -- eficiencia. Las conexiones persistentes son buenas si la sobrecarga para crear enlaces al servidor SQL son altas. Que hallan o no sobrecargas depende de muchos factores. Como, cual base de datos se usa, que sea o no la misma computadora en que esta el servidor web, así como la carga de la maquina que tiene el servidor SQL y así por el estilo. Lo esencial es que si la sobrecarga de conexiones es alta, las conexiones persistentes ayudan considerablemente. Podrían causar que los procesos hijos únicamente se conecten una vez en todo lo que duran, en lugar de que se haga cada vez que se procese una página que se conecte a un servidor SQL. Esto significa que por cada hijo que abrió una conexión persistente mantendrá una conexión persistente al servidor. Por ejemplo, si se tienen 20 procesos hijos diferentes que ejecutaran un script que hace una conexión persistente al servidor SQL, se tendrían 20 conexiones diferentes al servidor SQL, una por cada hijo. Nótese, sin embargo, que esto puede tener algunos inconvenientes si se esta usando una base de datos con un limite de conexiones que sean excedidas por las conexiones persistentes hijas. Si la base de datos tiene un limite de 16 conexiones simultaneas, y en el curso de una sesión ocupada del servidor, 17 hilos intentan conectarse, uno no sera posible de hacerse. Si hay bugs en los scripts los cuales no contemplen los cierres de las conexiones (como un loop infinito), la base de datos con los 16 conexiones podría rápidamente hundida. Chequear la documentación de la base de datos para obtener información de como manejar conexiones abandonadas u ociosas. Advertencia Hay un par de advertencias mas que tener en mente cuando se usan conexiones persistentes. Una es que cuando se usan bloqueos de tablas con una conexión persistente, si el script por alguna razón no puede soltar el bloqueo, entonces los scripts subsecuentes que usan la misma conexión serán bloqueadas indefinidamente y podría ser necesario reiniciar el servidor httpd o el servidor de la base de datos. Otra cosa es que cuando se usan transacciones, una transacción bloqueada también podría llevar al siguiente script el cual usa la conexión si la ejecución del script termina antes que el bloqueo lo haga. En ese caso, se puede usar register_shutdown_function() para registrar una función de limpieza para desbloquear las tablas o deshacer la transacción. Mejor aún es evitar este problema por completo no usando conexiones persistentes en scripts los cuales usan bloqueo de tablas o transacciones (se pueden usar en otros lugares). Un resumen importante. Las conexiones persistentes fueron diseñadas para tener conexiones normales uno a uno. Esto significa siempre que se podría ser capaz de reemplazar conexiones persistentes con conexiones no-persistentes, y no cambiaría la forma en que este se comporte. Esto podría (y probablemente lo hará) cambiar la eficiencia del script, pero no su comportamiento!. Ver también fbsql_pconnect(), ibase_pconnect(), ifx_pconnect(), ingres_pconnect(), msql_pconnect(), m ssql_pconnect(), mysql_pconnect(), ociplogon(),odbc_pconnect(), oci_pconnect(), pfsockopen(), pg_ pconnect(), and sybase_pconnect().