martes, 3 de noviembre de 2015

Warning al Crear un WebService con NuSoap en PHP

Al crear un webservice para que responda a peticiones de un cliente utilizando la librería nusoap, se genera un Warning en el log de errores del Apache como se puede observar en la siguiente línea.

[Mon Nov 02 12:10:39 2015] [error] [client 192.168.xxx.xxx] PHP Warning:  get_class() expects parameter 1 to be object, string given in /xxx/xxx/nusoap/lib/nusoap.php on line 4022

Este Warning es generado en el archivo nusoap.php ubicado en ./nusoap/lib/nusoap.php y se puede solucionar remplazando la línea 4022 que originalmente esta escrita como se puede observar en la siguiente línea.

if (isset($this->methodreturn) && ((get_class($this->methodreturn) == 'soap_fault') || (get_class($this->methodreturn) == 'nusoap_fault'))) {

Por una línea que fuerce la variable $this-methodreturn a un objeto, de manera que se pueda utilizar la función get_class() sin problemas, obteniendo como resultado la siguiente línea.

if (isset($this->methodreturn) && ((get_class((object)$this->methodreturn) == 'soap_fault') || (get_class((object)$this->methodreturn) == 'nusoap_fault'))) {


La función get_class() de PHP debe recibir como parámetro un object y la variable $this->methodreturn no podemos asegurar que sea un object, por lo tanto, se debe forzar la variable para convertirla en el tipo de variable.

Referencia:
http://forums.devshed.com/php-development-5/nusoap-simple-example-681714.html

http://php.net/manual/es/function.get-class.php

"Gracias, por compartir tus conocimientos"

viernes, 15 de mayo de 2015

Ajax: Uploader un Archivo con AJAX sin Jquery

Almacenar un archivo a través de una pagina Web utilizando los formularios de HTML es bastante común, sin embargo realizar esta operación utilizando AJAX sin JQuery fue un poco más difícil encontrar la información de como se realizaba esta operación.

Lo primero que se debe realizar es crear un formulario con HTML que contenga solo el campo de tipo file que se utilizará para enviar el archivo, si en este formulario se agregan más campos estos pasarán junto con el archivo y luego habrá que limpiar el archivo para poder almacenarlo como corresponde. Si necesitas almacenar recibir más campos en el formulario puedes crear dos formularios, primero pasas el archivo a través de la función Ajax y una vez que eso termino correctamente puedes pasar el resto de los campos a través de otra función Ajax.

A continuación se presenta el código de un formulario HTML que permite recuperar el archivo desde una pagina web.

<form id="frm_subir_archivo" name="frm_subir_archivo" method="POST" enctype="multipart/form-data" accept-charset="utf-8">
<input type='file' id='fle_archivo>' name='fle_archivo' />
</form>
<br />
<input id='btn_guardar' type='button' value='Guardar' onClick='cargarArchivo();' />


Al observar se puede ver que el botón Guardar no está dentro del formulario, porque como se mencionó anteriormente, si estuviera incluido en el formulario el archivo contendría algunas lineas que representarían el valor que tiene el botón dentro del formulario.

Una vez que existe el formulario se debe crear la función encargada de recuperar el archivo y enviarlo al Servidor que lo tiene que almacenar. En este caso la función se llama cargarArchivo() y es llamada una vez que se presiona el botón Guardar.

A continuación se presenta el código de la función JavaScript que utiliza el ajax que envía el archivo al Servidor.

function cargarArchivo(){
    var archivo = $('#fle_archivo').val().trim();

    if(archivo == ""){
        alert('Debe seleccionar un archivo');
        return false;
    }

    //obtenemos un array con los datos del archivo
    archivo = $("#fle_archivo")[0].files[0];
    //obtenemos el nombre del archivo
    var archivo_nombre = archivo.name;
    //obtenemos la extensión del archivo
    var archivo_extension = archivo_nombre.substring(archivo_nombre.lastIndexOf('.') + 1);
    //obtenemos el tamaño del archivo
    var archivo_tamano = archivo.size;
    //obtenemos el tipo de archivo image/png ejemplo
    var archivo_tipo = archivo.type;

    var objeto = objetoAjax();
    //objeto.open("POST", "ajax.php", true);
    objeto.open("PUT", "ajax.php", true);
    objeto.onreadystatechange = function() {
        if (objeto.readyState === 0) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Preparando datos...');
        } else if (objeto.readyState === 1) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Procesando consulta...');
        } else if (objeto.readyState === 2) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Esperando respuesta...');
        } else if (objeto.readyState === 3) {
            div_msg.html('<img src'+'='+'"./ajax-loader.gif"/> Recibiendo respuesta...');
        } else if (objeto.readyState === 4) {
            var response = objeto.responseText.tratarResponseText();
            var estado = response.substring(0, 2);
            response = response.substring(2);
            if(estado == 'OK'){
                alert(response);
            }else if(estado == 'FL'){
                alert(response);
            }else if(estado == 'WN'){
                alert(response);
            }else if(estado == 'AL'){
                alert(response);
            }else{
            }
        }
    };
    //objeto.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    objeto.setRequestHeader("Content-Type", "multipart/form-data");
    objeto.setRequestHeader("Content-length", file_size);
    objeto.setRequestHeader("X_FILENAME", file_name);
    var variables = archivo;
    objeto.send(variables);
}


Es necesario aclarar que el código utiliza Jquery para obtener el valor de los campos, pero no lo utiliza para realizar el envío de datos a través de Ajax.

Lo primero es verificar que el campo que contiene el archivo no venga vació, si está vacío se envía una alerta explicando el error y se de tiene el script. Si el campo es distinto de vacío se obtienen los diferentes valores que entrega un campo de tipo archivo, como son el nombre, la extensión, el tamaño y el tipo de archivo, sin olvidar la variable archivo que contiene el cuerpo del archivo.

Después se crea el objetoAjax, se abre la conexión con el servidor y se indica de que modo se enviarán los datos. Los métodos típicos para enviar datos desde un formulario HTML a un servidor son GET y POST, pero para este caso se utiliza PUT que se utiliza para almacenar archivos en un servidor.

Después se obtienen los diferentes cambios de estado de la conexión Ajax hasta llegar al estado 4 en el momento que el Script Ajax retorna la respuesta de la solicitud. El string de respuesta lo dividimos en los dos primeros caracteres que indican la condición de retorno OK (operación exitosa), FL (operación fallo), WN (peligro en la operación), AL (alerta en la operación) y se envía una alerta con una descripción de la respuesta.

Se agrega un header indicando el tipo de formulario que se envía al servidor, el tamaño del archivo y el nombre del archivo, para ser recibidos en el servidor, por último se envían los argumentos hacia el servidor, en este caso el archivo adjunto.

En la función cargarArchivo() se utiliza la función objetoAjax para crear un objeto que sea capaz de enviar la información obtenida al Servidor y la función tratarResponseText que permite recuperar la respuesta entregada por el servidor a la petición realizada desde el JavaScript. Ambas funciones se presentan en el siguiente código fuente.

// JavaScript Document  Objeto Ajax
String.prototype.tratarResponseText=function(){
    var pat=/<script[^>]*>([\S\s]*?)<\/script[^>]*>/ig;
    var pat2=/\b\s+src=[^>\s]+\b/g;
    var elementos = this.match(pat) || [];
    for(i=0;i<elementos.length;i++) {
        var nuevoScript = document.createElement('script');
        nuevoScript.type = 'text/javascript';
        var tienesrc=elementos[i].match(pat2) || [];
        if(tienesrc.length){
            nuevoScript.src=tienesrc[0].split("'").join('').split('"').join('').split('src=').join('').split(' ').join('');
        }else{
            var elemento = elementos[i].replace(pat,'$1');
            nuevoScript.text = elemento;
        }
        document.getElementsByTagName('body')[0].appendChild(nuevoScript);
    }
    return this.replace(pat,'');
}

function objetoAjax(){
    var xmlhttp=false;
    try {
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        try {
           xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (E) {
            xmlhttp = false;
        }
    }

    if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
        xmlhttp = new XMLHttpRequest();
    }
    return xmlhttp;
}


Ahora que se ha explicado todos los códigos desde el lado del cliente, se puede ver como se recupera el archivo en el servidor. El código para recuperar el archivo y almacenar el archivo se presenta a continuación.

if (isset($_SERVER['HTTP_X_FILENAME'])) {
    $archivo_nombre = $_SERVER['HTTP_X_FILENAME'];
    $archivo_nombre = date('YmdHis')."_".$archivo_nombre;
    file_put_contents(
        '/tmp/'.$archivo_nombre,
        file_get_contents('php://input')
    );
    echo 'OK'.$archivo_nombre;
}else{
    echo 'FLEl archivo no fue almacenado.';
}


Se pregunta si existe el nombre del archivo, de no ser así se devuelve un error indicando que el archivo no fue almacenado. Si existe el nombre de archivo se recupera en una variable a la cual se agrega la fecha para que no se borren los archivos subidos con el mismo nombre, después se recupera el contenido del archivo y se pasa a una string con la función file_get_contents(). Con la función file_put_contents que escribe el contenido de un string a un archivo ubicado en la dirección indicada como primer parámetro.

Con estos códigos se puede recuperar un archivo desde la interfaz web del cliente y enviar a un servidor a través de Ajax, sin utilizar Jquery para enviar este archivo.

Referencia:

http://uno-de-piera.com/subir-imagenes-con-php-y-jquery/
http://www.eduardocasas.com/es/blog/10-11-2012/xmlhttprequest-2-subiendo-archivos-al-servidor-por-medio-de-ajax
http://aprende-web.net/progra/ajax/ajax_11.php
https://github.com/FineUploader/fine-uploader/issues/61

"Gracias, por compartir tus conocimientos"

martes, 3 de marzo de 2015

CSS: SPAN con ancho fijo

El validador W3C de HTML muestra como error agregar un <div> dentro del elemento <td> de una tabla, sin embargo, al agregar un elemento <span> dentro del elemento <td> de una tabla no presenta ningún inconveniente para el validador. Esto provocó que en algunos segmentos de mis archivos HTML prefiera utilizar etiquetas <span> en lugar de <div>, pero me encontré con un problema al definir el ancho de un elemento <span>, esto me llevo a encontrar la siguiente solución en Internet.

Las etiquetas HTML tiene por omisión una disposición en bloque (block) o en línea (inline). Algunos ejemplos de elementos dispuestos en bloque <div>, <p>, <h1>, <form> y <li>; mientras que los que se muestran en línea pueden ser <span>, <a>, <label>, <strong>, <b> y <em>. Una de las características de los elementos div y span es que se le puede cambiar el ancho (width).

span { width: 100px; }

En teoría, esto hace que la caja que genera el <span> tenga 100 pixels de largo, pero lamentablemente en los navegadores el <span> sólo tendrá el largo de los caracteres que aloje. Para solucionar este problema se puede utilizar el siguiente estilo:

span {
width: 100px;
display:block;
}

Esto hace que el <span> se convierta en un elemento en bloque, pero también causará que el siguiente elemento en línea salte a la línea siguiente. Para evitar esto se puede agregar float:left al css.

Referencias:

http://web-ar.blogspot.com/2007/01/css-span-con-ancho-fijo.html


"Gracias, por compartir tus conocimientos"

martes, 27 de enero de 2015

Linux: Eliminar directorios .svn de todos los subdirectorios


En varias ocasiones he instalado el controlador de versiones Subversion (SVN), el cual es muy útil para trabajar en equipo sin dañar las modificaciones que otro miembro del equipo ha realizado al sistema. Este sistema crea un directorio oculto en cada uno de los subdirectorios del proyecto en que fue instalado y en algunos momentos es necesario eliminar estos directorios.

Eliminar cada uno de estos directorios no es mayor problema cuando el proyecto tiene solo un directorio, pero cuando el proyecto tiene muchos directorios y además utiliza framework que contienen muchos directorios anidados, la tarea puede ser interminable.

Una buena solución es utilizar la propiedad de ejecutar comandos con find en Linux.

find . -name “.svn” -type d -exec rm -rf {} \;

ó

find . -name .svn -exec /bin/rm -fR '{}' \;

Para entender un poco mejor el funcionamiento de estos comandos se describe que acción realiza cada uno de ellos.

  • find: comando de Linux utilizado para buscara archivos en directorios jerárquicos.
  • .: indica que la búsqueda debe comenzar en el directorio actual y seguir con los subdirectorios que el directorio actual contiene.
  • -name nombre_archivo: indica que se debe realizar la búsqueda de los archivos que coinciden con el nombre_archivo ingresado o con parte de ese nombre. Al no utilizar caracteres comodines como *, ? o [], el comando buscará un nombre de archivo que coincida completamente con el nombre ingresado, si utiliza caracteres comodines se buscarán los archivos que coincidan con la expresión regular generada por el nombre de los archivos más los comodines. Para utilizar comodines el nombre del archivo debe ir entre comillas dobles “.
  • -type tipo_archivo: indica el tipo de archivo que se está buscando, los tipos son:
    • b: bloque especial (buffered)
    • c: carácter especial (unbuffered)
    • d: directorio
    • p: tubería con nombre (FIFO)
    • f: archivo regular
    • l: link simbólico. Este nunca es verdadero si la opción -L o la opción -follow están en efecto, amenos que se rompa el link simbólico. Si tu deseas buscar link simbólicos cuando -L está en efecto, usa -xtype.
    • s: socket
    • D: puerta (Solaris)
  • -exec comando: Ejecuta un comando.. Es verdadero si el estado retornado es 0. Todos los argumentos siguientes a find son tomados para ser argumentos del comando hasta un argumento que consiste en ; se encuentre. La cadena '{}' es remplazada por el nombre del archivo actual siendo procesado en todas partes donde se encuentre en el argumento para el comando, no solo en argumentos donde esta solo, como en algunas versiones de find. Ambas de estas construcciones pueden ser escapadas (con un '\') o citado para protegerlos de la expansión por la shell. El comando especificado esta corriendo una vez por cada archivo encontrado. El comando es ejecutado en el comienzo del directorio. Hay inevitables problemas de seguridad circundantes al uso de la acción -exec; tu deberías usar la opción -execdir en su lugar.