Back-End

8 out, 2012

Rachaduras na fundação do PHP

Publicidade

O PHP tem estado por aí há muito tempo e está começando a mostrar sua idade. Do início ao fim, a linguagem possui articulações que rangem. Resolvi dar uma olhada em como as coisas chegaram a este ponto e o que pode ser (e está sendo) feito em relação a isso. Pode parecer sombrio no começo, mas tenha paciência; prometo que depois melhora!

No início, havia o Apache e CGI

E havia muita alegria!

Em 1994, Rasmus Lerdorf criou o “Personal Home Page Tools”, um conjunto de binários CGI escritos em C. Estas ferramentas não se pareciam em quase nada como o PHP que conhecemos hoje. Incorporado nos comentários HTML, e usando uma sintaxe sem possuir nenhuma semelhança com C, eles ainda contribuem com um princípio fundamental para o PHP moderno. Ele simplesmente funcionou!

O PHP é uma linguagem de conveniência. Ele foi construído com a ideia de que qualquer um poderia lançar algumas linhas de código em conjunto e ter um script CGI que funcionasse, sem ter que se preocupar com a interface do servidor, a sintaxe enigmática do Perl ou as armadilhas de C. Em teoria é uma ótima ideia e para a maioria tem funcionado muito bem na prática.

Infelizmente, nada é perfeito – incluindo o PHP. Ao longo do tempo, ele sofreu de tudo: desde falhas de segurança até decisões ruins de design. Alguns desses problemas eram evitáveis, mas outros não.

A compatibilidade com versões anteriores é algo ruim

Compatibilidade com versões anteriores (ou “Backward Compatibility” – BC) é a desgraça de cada biblioteca e de um escritor de app. Ela abafa as melhorias, atrasa a inovação, promove práticas inseguras, frustra os usuários e retarda o desenvolvimento. Por ser uma linguagem destinada a iniciantes, o PHP sofre mais do que a maioria.

Toda vez que a BC falha, o app também falha. Os fornecedores de sistemas operacionais que fornecem a versão nova e melhorada do PHP tendem a ser criticados por possuir um sistema quebrado, ainda que não tenham feito nada de errado. Com mais frequência, os escritores de aplicativos são vilipendiados por não fornecer o software funcionando, apesar de não terem feito nada além de seguir o manual.

Às vezes, a origem do problema é identificada corretamente e os desenvolvedores de PHP são repreendidos por tentar fazer uma linguagem melhor. Não importa quem leva a culpa, porém, uma coisa continua constante: os usuários raramente entendem algo além de “está quebrado!”. Eles não se importam se a nova versão é melhor. A antiga funcionou. E eles querem que continue funcionando. É uma expectativa sensata.

Infelizmente, quando se trata de uma linguagem de programação, muitas vezes é impossível atender a essa expectativa sem sacrificar os recursos, a segurança ou a velocidade – geralmente mais de um.

O PHP 4.4 foi lançado para corrigir um bug que causava a corrupção de memória quando as referências eram mal utilizadas. A correção mudou a API interna, forçando a reconstrução de cada módulo de extensão. Infelizmente, a reconstrução de extensões pode ser um processo árduo em alguns ambientes. Algumas extensões não vêm com o código fonte. Outras são antigas e o código que conseguiu avançar com dificuldade finalmente para de compilar de forma limpa. Os vendedores (como aqueles que oferecem várias versões do Linux) que vendem pacotes têm de reconstruir e testar não apenas o PHP em si, mas também toda a extensão que enviam antes de empurrar para seus repositórios.

Os resultados disto tudo são duplicados. Primeiro, quase todo mundo mantém distância da atualização da nova versão para evitar o trabalho e os custos envolvidos para consertar os problemas que surgem, deixando que todos executem uma versão com bugs de corrupção de memória descritos publicamente. Segundo, o próprio PHP é desencorajado a fazer alterações semelhantes no futuro para que a adoção de correções compatíveis seja reduzida. Ele só fica pior quando a mudança quebra a compatibilidade de fonte, forçando os escritores de app a mudar seu código mesmo depois dos fornecedores atualizarem.

Tudo isso é ruim o suficiente quando a mudança é essencial por qualquer razão. É muito pior quando a quebra de compatibilidade é o resultado de escolhas erradas ou de um mau planejamento.

As inovações são os brinquedos do Diabo

Existem vários exemplos na história do PHP de mudanças que foram feitas pelos desenvolvedores bem-intencionados, com visão de futuro e que estavam tentando tornar o PHP melhor, apenas para serem vaiados por causa do problema que isso causaria para implementá-los. Ou até pior, alguns realmente fizeram a diferença e sofreram com o caos que se seguiu, pois eles não perceberam o quão longe os efeitos poderiam alcançar.

Um exemplo recente é uma mudança no comportamento da função is_a () entre as versões 5.3.6 e 5.3.7. is_a () usadas para permitir que um parâmetro de string seja o primeiro argumento. Ela foi alterada para que chamasse o autoloader quando passasse uma string referindo-se a uma classe que não existe. O novo comportamento foi tecnicamente correto e coerente com a função is_subclass_of (), mas chamou o autoloader quando, anteriormente, ele não tinha causado uma grande quantidade de código de trabalho para quebrar. Vários pacotes PEAR começaram jogando exceções do autoloader, devido a classes supostamente desaparecidas. Corrigir esses pacotes requer a adição de um is_object extra () de verificação para cada is_a() que foi utilizado.

Infelizmente, no momento em que a extensão do problema foi percebida e uma solução foi acordada, uma versão adicional da série 5.3, 5.3.8, tinha sido lançada, com o novo comportamento intacto. Reverter o comportamento naquele ponto teria criado uma quebra de BC totalmente nova e exigido uma segunda série de alterações no código. Até o momento, a situação foi resolvida com um patch para a tree 5,4 e uma reversão em 5.3.9. Um relatório CVE havia sido introduzido para o novo comportamento e tópicos de discussão sobre várias listas de cobertura de testes e falta de procedimentos para a quebra do BC atingiram um tamanho impressionante.

Tudo isso poderia ter sido evitado se um teste de unidade tivesse pegado o intervalo, ou um processo relativo às quebras de BC estivesse no local para impedir a mudança. O autor original da mudança não pode ser responsável por consertar o bug e corrigir o comportamento incoerente, mas em um ambiente onde consertos e correções podem ter tais consequências de grande alcance, a culpa tende a ser atribuída (isso felizmente não aconteceu neste caso), e as pessoas ficam menos dispostas a consertar as coisas.

Em uma nota um pouco menos excessiva, por muito tempo, houve queixas sobre a nomeação inconsistente de funções array. Seria muito bom se, por exemplo, a família de funções [uka] [r] sort () tivesse sido nomeado array_ [Uka] [R] sort (), mas enquanto os novos nomes das funções podem ser adicionados, os antigos não puderam ser removidos durante um bom tempo – pelo menos nas duas versões principais do PHP. Com essa limitação em mente, parece inútil adicionar os novos nomes. Assim, os antigos permanecem – o resultado de uma decisão de projeto num passado distante do PHP, razoável no momento (nomes mais curtos de função eram mais fáceis de compreender, lembrar e usar).

Quando perguntam o que deu errado

Se no passado alguém tivesse me perguntado o que havia de errado com o PHP, eu provavelmente teria dito algo como “apatia do desenvolvedor”. No início de 2009, tomei a iniciativa de mover o código fonte do PHP do repositório CVS para um brilhante novo repositório SVN. Descobri que os indivíduos com conhecimento específico, como se um módulo especial precisava ser salvo ou não, eram fontes abundantes, mas me pareceu que havia uma falta de pessoas para ajudar com o processo global.

Que a história seja meu juíz a esse respeito. Pode ter sido que as ofertas de ajuda tenham sido feitas, que eu entendi mal ou preferi ignorar. Se assim for, peço desculpas a todos os que fizeram o esforço e foram rejeitados. Eu, entretanto, mantenho a minha afirmação de apatia como um problema, a falha do PHP 6 é um bom exemplo.

Ainda que aceitemos essa percepção como válida, certamente não será assim por mais tempo. Tem muito mais coisa com PHP agora do que no início de 2009. Se perguntado qual é o problema hoje, eu diria, “nenhum projeto e nenhum plano”.

O PHP sempre foi uma evolução quase orgânica da linguagem. Ele foi reescrito de baixo para cima pelo menos quatro vezes, com enormes mudanças internas no mecanismo, pelo menos, mais duas vezes. Por meio de todas essas mutações, no entanto, sua interface externa – a própria linguagem – permaneceu bastante semelhante por muito tempo. Quase tudo o que pode ser apontado como diferente entre PHP 3 e PHP 5.4 é uma adição ou extensão da linguagem e não uma mudança de comportamento existente. Há exceções, como o novo modelo de objeto, mas em grande parte, um programador PHP olhando código PHP 5 será capaz de fazer total sentido do PHP 3, e vice-versa. Todas essas versões compartilham uma falha: não há uma única especificação da linguagem!

Tokenizers externos devem ser implementados pela leitura re2c Zend Engine e arquivos de entrada Bison. Reimplementações do PHP têm que fazer constantemente referência à implementação Zend para entender as peculiaridades do mecanismo. O comportamento da linguagem é inconsistente e muitas vezes parece desajeitado. Em particular, as expressões não reduzem de forma recursiva a valores como se poderia esperar. O (new SomeClass)->methodReturningClosureReturningArray()()[5] causa um erro de interpretação, por exemplo, apesar de uma construção semelhante em Objective-C, o[[SomeClass alloc] init] methodReturningBlockReturningArray]()[5] funcionar bem.

Existem inúmeras razões por que esse comportamento faz parte da linguagem PHP, porém eles se resumem a dois pontos principais. Primeiro não há especificação que diz como a linguagem deve funcionar, não tem nada para comparar e dizer: “isso é errado” ou “isso está errado”. Segundo lugar, seria necessário uma reescrita do parser para corrigir problemas como estes de uma forma completa e duradoura; e isso significa reimplementar todo a linguagem de forma diferente. Uma “falha no BC” nem mesmo começar a cobrir isso.

Eu posso ver claramente agora

Eu já falei muito sobre os problemas do PHP. Já mencionei algumas soluções para eles, mas eu não disse muito sobre o que está realmente acontecendo. Então, aqui está a situação, e não é tão ruim quanto eu possa ter feito parecer.

Rupturas do BC

Uma nova versão do processo foi aprovada em junho de 2011, esclarecendo o cronograma de lançamentos, incluindo os momentos adequados para as mudanças que rompem o BC. Ela também colocou o PHP em uma trilha para versões mais regulares em geral, o que é uma ajuda significativa para os fornecedores que agrupam PHP.

Os problemas de comunicação

O PHP nunca teve problemas com ninguém fora da equipe principal que sabia o que estava acontecendo. Nos últimos meses, tem havido uma comunicação consideravelmente maior com os fornecedores de sistemas operacionais e outras pessoas afetadas por mudanças e o calendário de lançamento. Alguns deles vieram até nós, mas em outros casos nós fomos até eles.

Falta de especificação e padronização

A falta de uma especificação da linguagem continua sendo um problema significativo, mas, no mínimo, a consciência para problema aumentou. Uma iniciativa de documentar o comportamento da linguagem em um formato como o EBNF foi sugerida.

Testes de unidade ignorados e quebrados

Este problema em particular tornou público um bug de segurança importante no lançamento 5.3.7, que foi pego – e ignorado – por um teste de unidade. Esse bug foi consertado logo em seguida. Desde aquela época, um grande número de testes falhos foi corrigido, e os gerentes de lançamento agora prestam muito mais atenção ao conjunto de testes.

A apatia

A apatia do desenvolvedor na comunidade PHP praticamente desapareceu. A participação e discussão estão consideravelmente melhores. O desenvolvimento está agora ativo no branch 5.4, a linha de desenvolvimento que surgiu do esforço do PHP 6, e já está no status RC como escrito.

Infelizmente, o cruft da API da Zend Engine continua sendo um ponto de discórdia. A API é complicada, principalmente em situação irregular, e completamente intuitiva. O gerenciamento de referências zval vem à mente. Os esforços para documentar a API estagnaram novamente. A notícia não é de toda sombria, porém, um RFC está em discussão para separar completamente a API interna e criar uma nova API externa limpa.

Bit Rot (código antigo atrasando coisas novas)

O processo da nova versão facilita isso um pouco. Agora é seguro dizer que vai haver uma nova versão em algum ponto que não terá medo de romper o BC. Isso vem do simples fato de que as pessoas afetadas pelas rupturas do BC terão agora um alerta de que isso vai acontecer. A desaprovação em lançamentos pontuais é uma forma ruim de lidar com mudanças futuras. Dar a expectativa de grandes mudanças em um ponto definido no futuro é um bom caminho.

No unicode

A falta de suporte a unicode no PHP continua a ser um problema sério, mas pelo menos não estamos mais cuidando de um animal agonizante (PHP 6), na esperança vã de fazê-lo funcionar quando toda a situação ao seu redor mudou. Isso abre as portas para novas ideias sobre como corrigir o problema.
Para saber mais sobre a história do PHP 6, veja este excelente conjunto de slides do Andrei Zmievski.

Um pouco de bobagem de vez em quando é apreciado pelos homens mais sábios

É seguro dizer que PHP ainda tem um longo caminho a percorrer para ser a melhor linguagem, mas ele resistiu ao teste do tempo melhor do que qualquer outra linguagem de sua espécie, e vê uso diário através de milhões de servidores.
Nada que há de errado com o PHP ou com o seu desenvolvimento é insolúvel, e um tremendo progresso foi feito no ano passado. Não importa quão duro eu possa ter soado durante algumas partes deste artigo, estou orgulhoso de fazer parte do PHP e eu espero continuar no futuro.

Presente do desenvolvedor

Um presente que eu recomendo para qualquer desenvolvedor é uma cópia de um livro abrangente sobre arquitetura de hardware e design de sistema operacional, como o “Inside the Machine” e o “Operating Systems Design and Implementation”. Há muito tempo a crença de que as habilidades de qualquer desenvolvedor – se eles estão escrevendo código de máquina RAW ou PHP ou qualquer coisa – pode ser melhorada através de uma compreensão clara e abrangente das próprias máquinas.

***

Artigo original disponível em: http://phpadvent.org/2011/cracks-in-the-foundation-by-gwynne-raskind