Programación Web, Tricks PHP

SOAP: looks like we got no XML document

Siguiendo con los errores que aparecen durante el desarrollo de un proyecto web cualquiera, a continuación os detallo una nueva historia para no dormir. En esta ocasión se trata de la respuesta errónea que recibía un cliente web al realizar una llamada a nuestro servidor, vía SOAP.

Ya de entrada, y como pista os diré que era un problema causado por un despiste de un programador durante la fase de «debugging» del proyecto.

¿Tenemos claro cómo funciona PHP? ¿Y SOAP?

En otros posts, si os interesa, puedo explicaros en qué consiste exactamente SOAP y cómo podéis usarlo adecuadamente (y de forma muy sencilla) en vuestros proyectos. Pero por ahora lo importante es saber que las respuestas SOAP, en PHP, se envían como parte del cuerpo de respuesta de nuestro ‘documento’ de salida. Este documento, en el caso de SOAP, viajará en formato XML hacia el cliente.

Si tenemos en cuenta que PHP ‘escupe’ todo lo que nosotros intentamos mostrar vía ECHO, PRINT, PRINT_R, etc. e incluso todo aquello que se ‘escupe’ automáticamente como resultado de un error de programación (si tenemos activa la salida de errores), no parece descabellado pensar que un simple comando echo «por aquí paso» puede destrozar completamente nuestro resultado XML al pasar el texto «por aquí paso» a formar parte del contenido de dicho XML.

Por lo tanto, ojo con los despistes al programar haciendo uso de ‘echo’s para realizar un «seguimiento» rápido del flujo del código.

Otro motivo puede ser un fallo en el propio proveedor del servicio web, el cual incluya datos erróneos dentro del resultado.

Tras un buen rato de buceo por el código para ver qué ocurría y donde se producía el error, en nuestro caso era efectivamente un «echo» olvidado. Así pues les he recomendado a los programadores NO HACER MÁS USO de echo, print_r,  etc. y, en su lugar, usar una clase muy sencilla que programé hace ya mucho tiempo y que os paso a continuación:

<?php
class ConsoleOutput {
    var $isCommandLine;

    function __construct() {
        $this->isCommandLine = (defined('STDIN')) || isset($argc);
    }

    /**
    * EN- Writes $text to current console or document
    * ES- Escribe $text en la consola o documento activo
    *
    * @param String $txt Text to write to current console/document / Texto a mostrar
    * @param Boolean $addCR Adds line feed / Añade un salto de línea
    function out($txt, $addCR = true) {
        if (defined('IS_AVOID_VERBOSE')) {
            return;
        }
        if (is_array($txt)) {
            $txt = print_r($txt, true);
        }

        echo $txt;
        if ($addCR) {
            if ($this->isCommandLine) {
                echo "\n";
            } else {
                echo "<br/>";
            }
        }
    }

    // EN - Class wrapper for a single and quick use in small code
    // If you need to use several calls in the whole project it's better to create
    // a global instance of ConsoleOutput class instead (better performance).
    //
    // ES - Emboltorio para uso rápido de esta clase sin instanciar un objeto manualmente
    // Si se va a usar esta clase de forma intensiva dentro de un proyecto es mejor
    // crear una instancia única (global) y usarla durante el ciclo de vida de la sesión
    static function simpleOut($txt, $addCR = true) {
        $c = new ConsoleOutput();
        $c->out($txt, $addCR);
        unset($c);
    }
}
?>

Cosas a comentar sobre el código.

De entrada, si os fijáis, en el supuesto en que esté definida la constante IS_AVOID_VERBOSE dentro de vuestro proyecto, se anulará automáticamente la salida. Por lo tanto, como es lógico pensar, en cualquier proyecto SOAP o AJAX, lo primero que haremos será declarar dicha constante y listos :). De esta forma, si nuestro código lo invocamos desde un entorno de prueba (no SOAP) y no tenemos dicha constante definida, la salida funcionará correctamente mientras que en un entorno real SOAP, al tener la constante definida, no hará nada con lo que nunca corromperá el resultado enviado al cliente.

¿Cómo la usamos?

De forma rápida, con el «wrapper» simple:

require_once('ConsoleOutput.php');
ConsoleOutput::simpleOut('mi texto');

Otros pequeños detalles que pueden hacer de esta clase, una buena aliada para mostrar textos en consola, o en el documento HTML actual, en lugar de usar «echo»s:

  • Si estamos ejecutando nuestro script PHP desde consola (vía comando php miscript.php) se añadirá un salto de línea \n mientras que si lo estamos ejecutando vía navegador (http://localhost/miscript.php) se añadirá un <br>.
  • Si el texto que queremos mostrar es un array, automáticamente se realizará un print_r().

En fin, aunque esto no es, ni pretende ser, un sistema de LOG, ha venido a colación del MAL uso que había hecho un programador del comando ECHO para «debugar» una pieza de código.

Es bueno para poder encontrar este tipo de errores usar algún cliente SOAP de los muchos que existen para los navegadores, como por ejemplo «SOA Client» para Firefox. Con él podréis analizar qué está retornando exactamente la llamada SOAP.

Espero que este post os sirva para un par de cosas, para no usar más ‘echo’s 🙂 y para saber por qué se puede producir un error «looks like we got no XML document»

NOTA: Si a pesar de usar esta clase (o haber cambiado/eliminado todos los posibles «echo» dentro del código, sigue fallando, os recomiendo llamar a vuestro proveedor de Internet. Es posible que sea un fallo suyo. Y si sois vosotros los encargados del servidor web, tal vez el problema lo tengáis con algún plugin instalado en el mismo).

3 comentarios en “SOAP: looks like we got no XML document

  1. Hola Alex
    A pesar de que hago uso de lo que aqui comentas sigue apareciendome el mismo error. Este es mi código:

    nombredemifuncion(array(‘userID’ => $dni,’userPassword’ => $buf1, ‘appKey’ => $appKey ));
    }
    catch (Exception $e)
    {
    ConsoleOutput::simpleOut(‘ Capturada Excepcion!’);
    ConsoleOutput::simpleOut($e->getMessage());
    }
    ?>

    Me gusta

    1. Hola Alberto,

      El error puede deberse a un «echo» (u otro método de salida invocada) presente en CUALQUIER parte de tu código, no necesariamente en la función que detallas.

      Espero que dieras con el problema, pues me doy cuenta que tu mensaje tiene ya algún tiempo!!

      Me gusta

      1. Hola Alex

        La verdad es que hace tiempo ya que lo resolví. Creo recordar que el error era porque el servidor tenía algún fichero mal. No estoy muy seguro. Aún así gracias por responder. Un saludo

        Me gusta

Deja un comentario