Skip to content

Instantly share code, notes, and snippets.

@emersoncoder
Forked from alganet/php_pratico_streams.md
Created February 21, 2013 15:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emersoncoder/5005345 to your computer and use it in GitHub Desktop.
Save emersoncoder/5005345 to your computer and use it in GitHub Desktop.

PHP Prático: Streams

A palavra stream significa corrente. Em geral, qualquer conexão de rede é uma stream, e existem vários tipos de protocolos para streams. Esses protocolos definem como os dados fluem na corrente.

No PHP, vários protocolos são suportados de forma transparente:

<?php

$arquivoDoFtp   = file_get_contents('ftp://example.com/my/file.txt');
$arquivoDoDisco = file_get_contents('/my/file.txt');
$arquivoDoHttp  = file_get_contents('http://example.com/my/file.txt');

No exemplo acima usamos a mesma função do PHP, a file_get_contents para obter o conteúdo de três arquivos em lugares diferentes suportados por protocolos diferentes. O PHP decide qual protocolo de stream usar pelo identificador de esquema algo://, se ele for omitido, cai para o padrão file://.

O mais legal disso é que todas as funções de arquivos e streams do PHP funcionam com todos os protocolos. Vejamos um exemplo mais legal:

<?php

$pastaDoFtp   = scandir('ftp://example.com/my/images/folder');
$pastaDoDisco = scandir('/my/images/folder');
$pastaDoGz    = scandir('zlib:///my/images/folder.gz');

A função scandir é uma das funções de arquivo, portanto ela é capaz de abstrair streams e varrer diretórios tanto locais quanto de um FTP remoto ou arquivo zipado.

Alguns protocolos suportam contextos. Eles são necessários pra trafegar mais informação sobre uma stream. O contexto pra HTTP por exemplo, entre os diversos recursos que ele suporta, permite definir cabeçalhos:

<?php

$contexto      = stream_context_create(array('http' => array(
    'user_agent' => 'My PHP Bot.'
)));
$arquivoDoHttp = file_get_contents(
    'http://exemplo.com/my/file.txt', 
    null, 
    $contexto
);

O exemplo acima obtém um arquivo de um HTTP mas identifica a conexão com um cabeçalho User-Agent. É possível enviar cabeçalhos de autenticação, cache, mudar o método para POST, DELETE ou qualquer outro e definir proxies.

Além disso, o PHP permite que você crie seus próprios protocolos e os registre. Pessoas já criaram vários como esses pra mexer com arquivos no Amazon S3 e repositórios Git com funções simples como file_get_contents e scandir.

Outra coisa bastante surpreendente no suporte a streams é a auto-cópia. No exemplo abaixo, o $arquivoHttp é baixado inteiro para a memória antes de ser salvo:

<?php
$arquivoHttp = file_get_contents('http://example.com/my/file.txt');
file_put_contents('/arquivos/salvos/file.txt', $arquivoHttp);

Contudo, no exemplo abaixo, o arquivo nunca vai inteiro para a memória, permitindo que a stream flua livremente do HTTP direto pro disco:

<?php
file_put_contents(
    '/arquivos/salvos/file.txt', 
    file_get_contents('http://example.com/my/file.txt')
);

Isso acontece porque se você passa uma função de stream como parâmetro de outra, o PHP automaticamente aplica a função stream_copy_to_stream. Você poderia fazer isso manualmente também:

<?php
stream_copy_to_stream(
    fopen('http://example.com/my/file.txt', 'r'), 
    fopen('/arquivos/salvos/file.txt', 'w+')
);

Isso permite que você mexa com arquivos de 2GB tendo apenas 28MB de limite de memória no PHP, porque ele nunca terá o arquivo inteiro na memória, só um pequeno buffer que ele usa pra passar a corrente entre duas streams!

E se você quiser, é possível ir ainda mais longe com os filtros de stream. No exemplo acima nós copiamos um arquivo do HTTP direto pro disco, mas não é possível ver seu conteúdo. Com filtros de stream isso é possível. O exemplo abaixo baixa um arquivo de um FTP, converte tudo para maiúsculas e salva em um disco local:

<?php
$arquivoFtpStream   = fopen('ftp://example.com/file.txt', 'r');
$arquivoLocalStream = fopen('/my/file.txt', 'w+');

stream_filter_append($arquivoLocalStream, "string.toupper", STREAM_FILTER_WRITE); 

stream_copy_to_stream($arquivoFtpStream, $arquivoLocalStream);

Usei o filtro string.toupper, que é um dos padrões, mas você pode registrar os seus próprios.

Além de tudo, existem algumas streams específicas do PHP. O exemplo abaixo lê uma imagem gigante do disco e exibe ela no navegador sem carregá-la inteira na memória:

<?php

header('Content-Type: image/png');
fpassthru(fopen('/meu/arquivo/de/imagem/gigante.png', 'r'));

Além do fpassthru, é possível usar uma stream explícita:

<?php

header('Content-Type: image/png');
stream_copy_to_stream(
    fopen('/meu/arquivo/de/imagem/gigante.png', 'r'), 
    fopen('php://output', 'w+')
);

Além do php://output também existem outras streams mágicas do php.

E finalmente, streams são a única forma de fazer o PHP trabalhar de forma assíncrona! Isso é feito com a função stream_select, que se for usada em conexões não-bloqueantes, permite que as streams sejam coletadas de forma assíncrona.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment