Fork me on GitHub

Grupo de desenvolvedores de PHP do estado de São Paulo
Próximos encontros e eventos? Visite a página do PHPSP no Meetup

[React PHP] Streams e Sockets

Por em em Artigos

Dando continuidade ao primeiro artigo publicado sobre o tema este texto irá discutir sobre estas duas ferramentas incríveis e facilitadores presentes no pacote React PHP: streamsockets.

 

Visão Geral

Como vimos anteriormente, o EventLoop de React PHP é capaz de organizar quais funções serão executadas no loop e também de trabalhar com streams.

Streams são um tipo especial de resource no php que abstraem operações com arquivos, compressão de dados, rede e afins. (Vide manual)

Os pontos principais a se saber sobre um stream são: (1) ele não depende da aplicação em que é chamado para funcionar e (2) são passíveis de leitura e/ou gravação.

Um bom exemplo de stream é o resource retornado pela função fopen().

 

Nota interessante

O pacote React\Stream, até o momento em que escrevo este texto, trabalha somente com resources do tipo stream. Isto pode ser conferido através da função get_resource_type().

A título de curiosidade, há uma tabela com todos os tipos de resource existentes em PHP.

 

A que veio React\Stream

Em php, streams não são estruturas muito simples de trabalhar quando queremos tratar de paralelismo: um stream quando criado em php, por padrão, é bloqueante. Trabalhar com eles de outra maneira pode ficar um tanto confuso. Note o exemplo utilizando stream_select().

Neste exemplo temos $process como resultado de proc_open(), cuja principal funcionalidade é executar um subprocesso independente da aplicação PHP. Como comando definimos a string ‘sleep 2; echo “nawarian”;’, que se executada em linha de comando deverá imprimir na tela o texto “nawarian” após 2 segundos de seu início, porém durante a sua execução a linha de comando ficaria inutilizada.

Através da função stream_set_blocking() dizemos que tanto o STDIN quanto STDOUT deste processo não serão bloqueantes. A partir deste momento a aplicação PHP torna sua execução independente do andamento de $process.

Para mostrar como o script php está independente temos um while (true) que, a cada iteração, imprime um ponto (“.”) na tela. Neste laço também verificamos, utilizando stream_select() se a linha de comando que iniciamos gerou alguma saída e, quando gerar, obtemos seu conteúdo e quebra o laço infinito.

centenas de pontos e um string(9) "nawarian\n" ao fim.

Resultado esperado do código acima

 

React\Stream

Felizmente o pacote React\Stream nos traz uma ótima abstração a ser utilizada em conjunto com o EventLoop.

Sua principal classe é React\Stream\Stream, que recebe em seu construtor um resource do tipo stream e uma instância de React\EventLoop\LoopInterface.

O exemplo acima seria reescrito da seguinte maneira:

Desta maneira não há motivo para se preocupar em definir os streams como não bloqueantes ou em como controlar a prontidão para leitura do STDOUT do subprocesso criado, React\Stream já lida com tudo para você.

De maneira semelhante conseguimos tratar de escrita em resources. Voltemos ao bom e velho fopen() para exemplificar:

Sendo $writer a saída padrão, todo conteúdo que recebermos de fopen() será enviado à saída padrão. É claro que existe uma forma muito mais simples para se fazer isto:

Onde React\Stream\Stream::pipe() direciona todo conteúdo recebido da instância, escrevendo-o em seu stream de argumento. Tudo o que recebemos de $reader foi escrito em $writer. E, como ao final foi emitido um evento end no stream $reader, o mesmo foi devidamente propagado ao $writer.

 

React\Socket

React\Socket é uma saída para facilitar o que vimos neste exemplo do texto anterior. Este pacote se utiliza do pacote React\Stream para abstrair as leituras e escritas do socket.

O mesmo exemplo apresentado lá seria representado desta menira:

 

Não há muito o que se explorar por aqui além da sua imaginação, visto que React\Socket\Server nada mais é que um React\Stream\Stream especializado em trabalhar com resources criados a partir da função stream_socket_server(). Portanto é um pacote especializado em trabalhar como um servidor TCP/IP.

A partir dele foram criadas diversas outras ferramentas, como o React\Http (componente servidor que segue o protocolo HTTP, portanto um servidor HTTP) e a famosa biblioteca Ratchet.

 

Conclusão

React\Stream é a implementação mais fundamental – depois de React\EventLoop – dos pacotes React. Atavés dela é possível lidar com resources de arquivos, subprocessos, rede e quaisquer outros que sejam do tipo stream. Isto possibilitou a criação de outros pacotes como React\Socket e React\ChildProcess.

Através destas abstrações somos capazes de utilizar diversas ferramentas externas à nossa aplicação de forma facilitada e controlada, aproveitando tudo o que o padrão reactor pode nos oferecer.

Tags: , , , , ,

Sobre Níckolas Silva

Desenvolvedor php há aproximadamente 4 anos, Níckolas é tecnólogo em Análise e Desenvolvimento de Sistemas, pós graduando em Engenharia de Software com foco em SOA e já esteve envolvido com desenvolvimento de sistemas de telecom e de gestão. Hoje dedica horas de seus dias ao estudo de tecnologias, práticas e técnicas sobre sistemas distribuídos, procurando integrar todas as áreas de atuação através destes estudos. Além disso está presente em todos eventos PHPSP+Pub, pois acredita que conversas, pessoas e cerveja são a chave para aprender e fortalecer conceitos.

Mais posts de .

  • Sebastiao Marcos

    Por favor Nickolas, sei que já passou um pouco do tempo, mas tenho aqui uma situação um pouco embaraçosa, minha aplicação deve sincronizar com um serviço Soap a cada 1 hora, e não deve ter fim, também quando ela receber o comando sync via http ela deve tentar rodar essa sincronização. De fato eu até já sei por onde começar, mas queria saber se você tem alguma luz para mim com essa lib. Obrigado, Bom artigo!