Manipulando XML em grande escala com PHP

by Vicente Russo Neto on May 23, 2009

Existem vários métodos para manipular arquivos XML grandes. Quando digo grandes, são realmente grandes, 10, 20, 30 mega. Se fossemos abrir um arquivo desses em um servidor comum, certamente a operação ia terminar com um “time-out”. Isso porque geralmente usamos as funcoes da extensão SimpleXML. Esta extensão tem um “Tree-based parser”, assim como o “DOM Parser”. Funcionam perfeitamente em arquivos pequenos. Estes jogam o conteúdo do XML em memória, e dali você manipula. Mas quando os arquivos são muito grandes, o negócio é procurar um Stream-based Parser. São mais eficientes pois fazem a leitura do arquivo sob demanda, é mais rápido e não mastiga a memória do servidor.

Dentre os Stream-based Parsers, temos o SAX e o XMLReader. Vou demostrar como fazer a leitura de um XML utilizando o XMLReader, pois é mais fácil de implementar e de execução mais rápida, como podem acompanhar neste link.

O XMLReader é uma extensão habilitada e incluída por padrão a partir da versao 5.1 do PHP, surgiu através da derivação da API do XmlTextReader em C# e é baseada na biblioteca libxml2. Antes disso, a extensão XMLReader era disponível apenas na PECL. O XMLReader suporta namespaces e validações, incluindo DTD e Relax NG (REgular LAnguage for XML Next Generation)

Bom, vamos ao código. Meu XML de exemplo tem a seguinte estrutura:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<xml>
  <vendedor codigo="000001">
    <nome>João Da Silva</nome>
    <endereco>Avenida São Paulo</endereco>
    <bairro>Centro</bairro>
    <cidade>São Paulo</cidade>
    <cep>xx.xx-xx</cep>
    <telefone>(11) 1234-4321</telefone>
    <cpf>12345678901</cpf>
    <rg>1234567890</rg>
  </vendedor>
  <vendedor>
.
.
.
  </vendedor>
</xml>

Código em PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$vendedores = new XMLReader();
$vendedores->open('vendedores.xml');
while ($vendedores->read()) {
     switch ($vendedores->nodeType) {
          case (XMLReader::ELEMENT):
               if ($vendedores->localName == "vendedor") {
             $node = $vendedores->expand();
             $dom = new DomDocument();
             $n = $dom->importNode($node,true);
             $dom->appendChild($n);
             $simple_xml = simplexml_import_dom($n);
             $codigo = $simple_xml['codigo'];
             $nome = $simple_xml->nome;
             $endereco = $simple_xml->endereco;
                     // Código customizado... insert, update, etc.
               }
     }
}

 

Percebam que em um determinado momento eu transformo o bloco de leitura atual, ou seja, a tag vendedores em um objeto SimpleXML, tornando a leitira extremamente fácil. Desta maneira voce pode tranquilamente trabalhar com arquivos XML de 5, 10, 50 mega sem detonar a memória do servidor. Meu próximo passo será melhorar essa rotina e transforma-la em uma library pro framework CodeIgniter. A quem possa interessar, toda ajuda é bem vinda!

Did you like this? Share it:

{ 8 comments… read them below or add one }

lucia May 20, 2010 at 8:03 pm

Prezado Vicente,

Tentei executar o codigo acima de deu o seguinte erro:
Notice: Undefined variable: reader in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs….. on line 9.

Ou seja está dando erro na linha 9 com relação $reader->explode.

A versão do meu php é 5.2

Qual será a razão?

Muito grata Lucia

Vicente Russo Neto May 21, 2010 at 2:06 pm

Olá Lucia,

Acredito que houve um equívoco no seu comentario, pois nao existe o métido “explode”, e sim “expand”. Caso o erro seja mesmo no expand, verifique se há instalado no servidor a extensão XMLReader e SimpleXML.

Embora o erro seja no XMLReader, não imagino que seja um erro de falta de extensão, pois daria erro ao tentar instanciar um novo XMLReader. Poderia postar seu código?

Lucia Terra June 24, 2010 at 11:52 am

Olá vicente,

Aquele comentario sobre o expand já foi resolvido. Meu dico PHP está funcionando bem. os arquivos xml que tenho trabalhado são da ordem de 26mega. Por exemplo leio o arquivo e gero um gráfico. Em algumas situações chego a ler 6 arquivos um de cada vez, para então gerar o gráfico. Percebo que o processo fica um pouco demorado, mas funciona. Pelo que entendi do seu post a forma mais rápida e eficiente de ler grandes arquivos é o XML reader. Existe alguma forma de fortná-lo mais rápido?

Vicente Russo Neto June 24, 2010 at 2:40 pm

Olá Lucia

Até onde eu sei, o XMLReader é de fato o mais rápido. Vale lembrar que um arquivo de 26 mega, por mais rapido que o método seja, sempre vai levar muito mais tempo do que um arquivo com a metade do tamanho, por exemplo…

Paulo C Faccioli October 1, 2010 at 11:43 am

Lembrando que algumas manipulações de large files em PHP precisam de uma configuração diferenciada no php.ini. Ex para carregar arquivos de 100Mb:

; Maximum size of POST data that PHP will accept.
post_max_size = 100M

; Maximum allowed size for uploaded files.
upload_max_filesize = 100M

Vicente Russo Neto October 1, 2010 at 6:50 pm

Exatamente Paulo, no entanto este é um problema paralelo, neste post o arquivo a ser manipulado já está no servidor

Filippe Brito August 15, 2011 at 10:03 am

O problema do “expand” nesse exemplo é simples, a variável “$reader” não existe, era pra ser usada no seu lugar “$vendedores” pois essa variável é quem está recebendo a instância do construtor “XMLReader”. Ao trocar aqui no meu pc funcionou.

Vicente, muito bom o tutorial, peço para efetuar essa correção para futuras consultas.

Vicente Russo Neto August 15, 2011 at 10:43 am

Corrigido, valeu!

Leave a Comment