Skip to content

Instantly share code, notes, and snippets.

@alganet
Last active August 15, 2021 20:41
Show Gist options
  • Save alganet/5004278 to your computer and use it in GitHub Desktop.
Save alganet/5004278 to your computer and use it in GitHub Desktop.
PHP Prático: Streams

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.

@NandoKstroNet
Copy link

Muito bom!

@CauanCabral
Copy link

Sempre revisito esse snippet pra lembrar ou aprender alguma coisa, vlw Gaigalas

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