Você está na página 1de 7

Integrao de Sistemas em PHP e Delphi

usando WebServices

Saulo Arruda
(http://sauloarruda.eti.br)

Esta semana tive uma experincia nova em se tratando de integrao de


sistemas. O caso envolvia um sistema de gerenciamento da empresa do
cliente, desenvolvido internamente em Delphi com Banco de Dados Microsoft
SQL Server e um site desenvolvido em PHP & MySQL. No sistema em Delphi,
que a partir de agora vou me referir apenas como ERP, os colaboradores da
empresa registram solicitaes de servios. O cliente queria disponibilizar um
espao no site para que o cliente possa acompanhar o andamento da sua
solicitao.

Arquitetura

Esse um cenrio muito comum de vrios clientes para os quais j trabalhei.


A questo interessante desse caso so as tecnologias envolvidas. As primeiras
idias para a integrao entre os sistemas era conectar diretamente no Banco
SQL Server e mostrar as informaes no site. Essa soluo se mostrou invivel
devido ao nvel de segurana exigido para as informaes deste banco, no
permitindo acesso externo. Neste caso, os desenvolvedores da equipe do
cliente sugeriram fazer uma sincronizao de dados do ERP para o site.

Muito simples, eu pensei, vamos utilizar WebServices e SOAP. Logo a


arquitetura da aplicao segue o esquema do diagrama abaixo:
O site fornece um servio que expe os mtodos necessrios para a
sincronizao dos dados do ERP. A comunicao feita usando o protocolo
HTTPS garantindo a integridade dos dados e antes de qualquer operao.
Questes de autenticao sero discutidas mais adiante neste artigo.

Usando WebServices e SOAP possvel que o prprio servidor que o site est
hospedado, geralmente Apache + PHP, fornea o servio pela porta 443
eliminando a necessidade de abertura de portas em firewalls tanto do cliente
quanto do servidor.

Para demonstrar a implementao da soluo e o sigilo das informaes do


cliente, vou usar um exemplo mais simples. Porm, todas as dificuldades
encontradas no desenvolvimento sero apresentadas.

SOAP Server

Desenvolvi a primeira verso do servio usando a extenso PHP_SOAP que


nativa para o PHP 5 e me pareceu bastante confivel e simples de usar.

Criei uma classe chamada SolicitacaoService, o arquivo WSDL e sem


nenhuma dificuldade registrei o servio usando o cdigo abaixo:

<?php
class SolicitacaoService {
// Definio da classe...
}

$server = new SoapServer("solicitacaoService.wsdl");


$server->setClass("SolicitacaoService");
$server->handle();
?>

Devo admitir que tive vrios problemas para escrever o arquivo WSDL da
forma correta, j que nunca tinha feito isso manualmente.

Depois, criei uma aplicao em Delphi usei o "WSDL Importer" para criar uma
unit a partir do arquivo WSDL. Esse um recurso bastante prtico, pois as
interfaces dos mtodos e definio dos tipos j so criados automaticamente.

Aps isto, basta criar um form e um boto e escrever o cdigo abaixo:


procedure TForm1.btnLoginClick(Sender: TObject);
var arr : ArrayOfstring;
begin
solicitacaoService := GetSolicitacaoServicePortType();
// Definio do array com os parmetros...
ShowMessage(solicitacaoService.gravar(arr));
end;

Em ambiente de desenvolvimento, o problema da integrao j est resolvido.


Porm, o servidor de produo usava a verso 4.2.2 do PHP que no suporta a
extenso PHP_SOAP. Alm disso, essa verso do PHP tambm no suporta
todos os recursos de Orientao a Objetos que eu havia utilizado.

Neste momento resolvi usar uma implementao do protocolo SOAP para PHP
bastante simples chamada NuSOAP. Agora consegui a compatibilidade com o
PHP4 e melhor, sem necessidade de instalao de nenhuma extenso do PHP
no servidor de produo.

Usando o NuSOAP, o cdigo sofreu vrias modificaes e ficou assim:

<?php

function gravar($arrAttr) {
return "ACK";
}

// Definio do servio
$server = new soap_server();
$server->configureWSDL("SolicitacaoService",
"urn:UsuarioService");

// Registra o tipo ArrayOfstring


$server->wsdl->addComplexType("ArrayOfstring",
"complexType","array", "", "SOAP-ENC:Array",
array(), array(
array("ref"=>"SOAP-ENC:arrayType",
"wsdl:arrayType"=>"string[]")
), "xsd:string");

// Registra o mtodo gravar


$server->register("gravar",
array("arrAttr" => "tns:ArrayOfstring"),
array("return" => "xsd:string"),
"urn:SolicitacaoService",
"gravar",
"rpc",
"encoded");

// Executa a requisio
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA)
? $HTTP_RAW_POST_DATA : "";
$server->service($HTTP_RAW_POST_DATA);
?>

Neste caso, necessrio registrar cada mtodo que ser acessado


remotamente, porm o NuSOAP gera automaticamente o arquivo WSDL, o que
em minha opinio, mais vantagem do que escrever somente:

$server = new SoapServer("solicitacaoService.wsdl");


$server->setClass("SolicitacaoService");

e ter de escrever o WSDL, que no uma tarefa muito divertida. Acredite.

Por outro lado, no foi possvel utilizar classes, pois o registro do mtodo
gravar usando SolicitacaoService.gravar gerava um WSDL com nomes
de mtodos no muito agradveis, ento preferi usar funes mesmo.

Perceba tambm que a declarao do tipo complexo ArrayOfstring


bastante confusa, mas nada que no se possa achar pronto na Internet.

No cliente em Delphi, bastou gerar novamente a unit correspondente ao


servio usando o WSDL Importer, usando o nome do script PHP com o
parmetro wsdl (Ex.: http://sauloarruda.eti.br/service/solicitacao.php?wsdl).

Naturalmente a implementao da funo gravar deve abrir uma conexo


com o banco de dados MySQL e gravar um registro na tabela de solicitao.
Para maior simplicidade do nosso exemplo esse cdigo foi omitido e a funo
retorna simplesmente o texto ACK que, por conveno, significa que a
chamada foi processada com sucesso.

Esse cliente em Delphi tambm dever ser incorporado no ERP sendo que cada
vez que uma solicitao for gravada, uma chamada WebService ser feita para
sincroniz-la. Alm disso, ser necessrio tratar erros implementando uma fila
de chamadas do servio que falharam para que seja feita uma nova tentativa.
Autenticao

Todos ns sabemos que um WebService disponibilizado na Web e por isso,


qualquer pessoa pode usar um WSDL Importer da vida e sair fazendo chamadas
do seu servio.

Como no isso que queremos nesse caso, precisamos implementar algum


tipo de autenticao. A primeira idia era usar autenticao HTTP. Mas antes
de implementar algo em PHP, resolvi testar os componentes em Delphi para
ver se possvel fazer isso. Achei um artigo que pareceu funcionar, mas no
fez diferena alguma.

A segunda opo era implementar um mtodo login que retorna um ticket


usando uma idia parecida com sistemas de Single Sing On. Na primeira
chamada, o cliente chama um mtodo login passando usurio e senha e
recebe um ticket que ser passado como parmetro nas prximas chamadas.

Desta forma, o mtodo login deve ser definido e registrado conforme o


cdigo abaixo:

<?php
function login($usuario, $senha) {
if ($usuario == "service" && $senha == "secret") {
$ticket = md5(uniqid());
session_name($ticket);
session_start();
$_SESSION["login"] = true;
return $ticket;
} else {
return "401: Usurio ou senha invlidos";
}
}

// Registra o mtodo login


$server->register("login",
array("usuario" => "xsd:string",
"senha" => "xsd:string"),
array("return" => "xsd:string"),
"urn:SolicitacaoService",
"login",
"rpc",
"encoded");
?>
O ticket gerado usando a funo uniqid, que garante a gerao de um
identificador nico, e aplicando um hash MD5 ao id gerado. Esse ticket
armazenado em uma varivel da sesso.

Um detalhe importante da implementao da funo login usar a funo


session_name($ticket) para nomear a sesso com o ticket gerado. Desta
forma no existe risco de falhas de segurana, pois cada ticket gerado cria
uma nova sesso.

Para terminar, a funo abaixo valida se o ticket passado por parmetro


vlido:

<?php
function checkLogin($ticket) {
session_name($ticket);
session_start();
return isset($_SESSION["login"]);
}
?>

A implementao da funo gravar ter sua assinatura alterada para receber


o ticket como parmetro e valid-lo, conforme o cdigo abaixo:

<?php
function gravar($arrAttr) {
if (!checkLogin($ticket)) {
return "401: Autenticao no encontrada";
}
return "ACK";
}

// Registra o mtodo gravar


$server->register("gravar",
array("ticket" => "xsd:string",
"arrAttr" => "tns:ArrayOfstring"),
array("return" => "xsd:string"),
"urn:SolicitacaoService",
"gravar",
"rpc",
"encoded");
?>
No cliente, novamente devemos usar o WSDL Importer para gerar a unit do
servio e alterar o cdigo para chamar primeiro o mtodo login, conforme o
cdigo abaixo:

procedure TForm1.btnLoginClick(Sender: TObject);


var arr : ArrayOfstring;
ticket: string;
begin
solicitacaoService := GetSolicitacaoServicePortType();
ticket := solicitacaoService.login('service',
'secret');
// Definio do array com os parmetros...
ShowMessage(solicitacaoService.gravar(ticket, arr));
end;

Concluses

A integrao entre sistemas em plataformas diferentes nem sempre uma


tarefa trivial. Muitas vezes, por limitao da tecnologia, uso de verses
antigas da plataforma (PHP 4 e Delphi 7) e pouca documentao disponvel
esse trabalho acaba tomando muito mais tempo que o previsto.

Nestes casos, eu considero de suma importncia trocar idias com colegas de


trabalho ou amigos que j possam ter passado pela mesma situao. Inclusive,
agradeo a valiosa ajuda dos colegas Bruno Freitas e Rodrigo Toledo que me
ajudaram com timas idias para a resoluo deste problema.

O cdigo-fonte em Delphi e PHP deste artigo est disponvel para download no


endereo: http://sauloarruda.eti.br/artigos/webservice.zip.

Qualquer dvida, crtica ou sugesto sobre o tema deste artigo pode ser
enviada para o e-mail contato@sauloarruda.eti.br ou via comentrios deste
artigo publicado no endereo http://sauloarruda.eti.br/.

Licena

Este artigo est licenciado sob a licena Creative Commons


Attribution-ShareAlike 2.5 License.