6. SOAP arrays

Em SOAP também podemos usar arrays como parâmetros de entrada ou como resultado de um método. Estes arrays são não associativos, são apenas listas com uma indexação numérica semelhantes aos que existem em linguagens como C ou FORTRAN. O acesso aos elementos nestas estruturas será feito por índice numérico a começar em 0.

A título de exemplo, vamos criar um serviço com várias funcionalidades sobre listas de inteiros. Comecemos por um serviço que duplica o valor de todos os elementos de uma lista devolvendo a nova lista.

O servidor poderia ser codificado da seguinte forma:

Exemplo 2.22. Servidor para processar listas: o dobro

<?php
require_once('../lib/nusoap.php');

$server = new soap_server;

$server->register( 'dobro', 
                   array('l'=>'ListaInteiros'),
                   array('res'=>'ListaInteiros' ),
                   'uri:ws.di.uminho.pt/Listas',
                   'uri:ws.di.uminho.pt/Listas/dobro',
                   'rpc',    
                   'encoded');

function dobro($l) 
{    
  for ($i = 0; $i < count($l); $i++) 
    {
      $retval[$i] = 2 * $l[$i];
    }
  return $retval;
}

$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>

Vejamos agora um cliente para este serviço.

Exemplo 2.23. Cliente do processador de listas: o dobro

<?php
require_once('../lib/nusoap.php');

$location = 'http://localhost:8888/nusoap-0.9.5/SOAP-arrays/serv-listas.php';

$client = new nusoap_client( $location, false);
$namespace = 'uri:ws.di.uminho.pt/Listas';
$soapaction = 'uri:ws.di.uminho.pt/Listas/dobro';

$err = $client->getError();
if ($err) 
  {
    echo '<p><b>Erro na criação do cliente: ' 
         . $err . '</b></p>';
  }
else
  {
    $res1 = $client->call('dobro', 
                          array('n1' => array( 1, 2, 3, 4, 5)), 
                          $namespace, $soapaction );

    if ($client->fault) 
      {
        echo '<p><b>Fault: ';
        print_r($res1);
        echo '</b></p>';
      } 
    else 
      {
        $err = $client->getError();
        if ($err) 
          {
            echo '<p><b>Erro: ' . $err . '</b></p>';
          } 
        else 
          {
            print_r($res1);
          }
      }
  }
?>

Para efeitos de teste, a lista passada foi fixada com os valores inteiros de 1 a 5. Na figura seguinte mostra-se o resultado da chamada a este cliente.

Figura 2.6. Chamada do método dobro para a lista [1,2,3,4,5]

Chamada do método dobro para a lista [1,2,3,4,5]

Note que a função print_r(estrutura) do PHP permite imprimir de forma amigável o conteúdo de qualquer estrutura de dados, neste caso, está a imprimir um array.

Vejamos agora o pedido desta chamada para vermos como valores deste tipo são passados.

Exemplo 2.24. Pedido SOAP para o método dobro

POST /nusoap-0.9.5/SOAP-arrays/serv-listas.php HTTP/1.0
Host: localhost:8888
User-Agent: NuSOAP/0.9.5 (1.123)
Content-Type: text/xml; charset=ISO-8859-1
SOAPAction: "uri:ws.di.uminho.pt/Listas/dobro"
Content-Length: 700

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <ns7632:dobro xmlns:ns7632="uri:ws.di.uminho.pt/Listas">
      <n1 xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:int[5]">
        <item xsi:type="xsd:int">1</item>
        <item xsi:type="xsd:int">2</item>
        <item xsi:type="xsd:int">3</item>
        <item xsi:type="xsd:int">4</item>
        <item xsi:type="xsd:int">5</item>
      </n1>
    </ns7632:dobro>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Na resposta, a lista de valores é retornada na resposta da mesma forma, ou seja, um valor do tipo arrayType="xsd:int[5].

Suponhamos que na lista original que continha os números inteiros 1, 2, 3, 4 e 5, substituíamos o 3 por 3.5, tranformando a lista de inteiros numa lista híbrida de inteiros e reais. O pedido enviado teria a seguinte forma.

Exemplo 2.25. Pedido SOAP: dobro de uma lista híbrida

POST /nusoap-0.9.5/SOAP-arrays/serv-listas.php HTTP/1.0
Host: localhost:8888
User-Agent: NuSOAP/0.9.5 (1.123)
Content-Type: text/xml; charset=ISO-8859-1
SOAPAction: "uri:ws.di.uminho.pt/Listas/dobro"
Content-Length: 708

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <ns5641:dobro xmlns:ns5641="uri:ws.di.uminho.pt/Listas">
      <n1 xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:anyType[5]">
        <item xsi:type="xsd:int">1</item>
        <item xsi:type="xsd:int">2</item>
        <item xsi:type="xsd:float">3.5</item>
        <item xsi:type="xsd:int">4</item>
        <item xsi:type="xsd:int">5</item>
      </n1>
    </ns5641:dobro>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Como se pode ver, a classe NuSOAP adaptou-se indicando agora que o arrayType é do tipo xsd:anyType, ou seja, os seus elementos são de vários tipos diferentes.

No último exemplo, fomos um pouco mais além. Acrescentamos um método para processar listas de listas. Como a ideia era testar a passagem de argumentos de tipos estruturados complexos, o método é bem simples: recebe uma lista de listas de inteiros e dá como resultado uma lista de inteiros resultante dos somatórios das várias sublistas.

No exemplo seguinte apresentam-se as alterações ao servidor.

Exemplo 2.26. Servidor de listas: listas de listas

...
$server->register( 'mapSomaLista', 
                   array('l'=>'ListaListaInteiros'),
                   array('res'=>'ListaInteiros' ),
                   'uri:ws.di.uminho.pt/Listas',
                   'uri:ws.di.uminho.pt/Listas/mapSomaLista',
                   'rpc',    
                   'encoded');
...
function mapSomaLista($l)
{
  for ($i = 0; $i < count($l); $i++) 
    {
      $rlista[$i] = somaLista($l[$i]);
    }
  return $rlista;
}
...

No exemplo seguinte, mostra-se o cliente alterado para invocar este novo método.

Exemplo 2.27. Cliente de listas: listas de listas

...
$res1 = $client->call('mapSomaLista', 
                      array('n1' => array( array(1,2,3), array(2,3,4), 
                                           array(3,4,5), array(4), array(5,6,7,8,9))), 
                            $namespace, $soapaction );
...

A ideia era testar de que forma a classe NuSOAP lida com tipos estruturados com mais do que um nível. Verificou-se que consegue trabalhar bem com estruturas aninhadas serializando-as em XML como se demonstra no pedido originado a partir daquela chamada do cliente.

Exemplo 2.28. Pedido SOAP para a chamada com listas de listas

POST /nusoap-0.9.5/SOAP-arrays/serv-listas.php HTTP/1.0
Host: localhost:8888
User-Agent: NuSOAP/0.9.5 (1.123)
Content-Type: text/xml; charset=ISO-8859-1
SOAPAction: "uri:ws.di.uminho.pt/Listas/mapSomaLista"
Content-Length: 1406

<?xml version="1.0" encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope 
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <ns9727:mapSomaLista xmlns:ns9727="uri:ws.di.uminho.pt/Listas">
      <n1 xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="SOAP-ENC:Array[5]">
        <item xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:int[3]">
          <item xsi:type="xsd:int">1</item>
          <item xsi:type="xsd:int">2</item>
          <item xsi:type="xsd:int">3</item>
        </item>
        <item xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:int[3]">
          <item xsi:type="xsd:int">2</item>
          <item xsi:type="xsd:int">3</item>
          <item xsi:type="xsd:int">4</item>
        </item>
        <item xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:int[3]">
          <item xsi:type="xsd:int">3</item>
          <item xsi:type="xsd:int">4</item>
          <item xsi:type="xsd:int">5</item>
        </item>
        <item xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:int[1]">
          <item xsi:type="xsd:int">4</item>
        </item>
        <item xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="xsd:int[5]">
          <item xsi:type="xsd:int">5</item>
          <item xsi:type="xsd:int">6</item>
          <item xsi:type="xsd:int">7</item>
          <item xsi:type="xsd:int">8</item>
          <item xsi:type="xsd:int">9</item>
        </item>
      </n1>
    </ns9727:mapSomaLista>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Como se pode ver o tipo dos elementos da lista principal é agora Array[5]. Mais uma vez fica aqui patente de que podemos criar serviços que recebem qualquer tipo de parâmetro e devolvem qualquer tipo de resultado desde que serviços e clientes saibam que tipo de informação estão a trocar e a processem como deve ser.

Podemos sempre personalizar o tipo do resultado com a criação de um soapval. Neste último exemplo, vamos fazê-lo a título de demonstração.

Exemplo 2.29. Servidor de listas de listas: soapval

...
function mapSomaLista($l)
{
  for ($i = 0; $i < count($l); $i++) 
    {
      $rlista[$i] = somaLista($l[$i]);
    }
  return new soapval('res', 'ListaInteiros', $rlista, 
                     false, 'uri:ws.di.uminho.pt/Listas');
}
...

Com esta alteração a resposta já vem personalizada.

Exemplo 2.30. Resposta do mapSomaLista com soapval

...
<ns1:mapSomaListaResponse xmlns:ns1="uri:ws.di.uminho.pt/Listas">
  <res xmlns:ns4776="uri:ws.di.uminho.pt/Listas" xsi:type="ns4776:ListaInteiros">
    <item xsi:type="xsd:int">6</item>
    <item xsi:type="xsd:int">9</item>
    <item xsi:type="xsd:int">12</item>
    <item xsi:type="xsd:int">4</item>
    <item xsi:type="xsd:int">35</item>
  </res>
</ns1:mapSomaListaResponse>
...

6.1. Exercícios

O exemplo apresentado corresponde a uma operação de map, transforma os elementos de uma lista e devolve a nova lista, codifique agora um ou dois métodos de fold, que processam a lista e devolvem um valor: somaLista, máximo, mínimo, etc.