Portabilidade de códigos em PHP

Artigo que aborda a questão da portabilidade em códigos PHP e algumas instruções de como trarar casos em que é necessário utilizar recursos específicos ou recursos equivalentes.

portabilidade
Introdução

PHP, no contexto da Web, é uma linguagem portável entre diferentes servidores HTTP, entre diferentes sistemas operacionais, e entre diferentes arquiteturas de hardware. No entanto, alguns recursos específicos nem sempre estão disponíveis de forma tão ampla e podem requerer um servidor HTTP, S.O. ou hardware específico. Neste artigo vamos ver por que isso ocorre e, quando possível, como solucionar ou evitar este problema.


Por que existem recursos específicos em PHP?

A resposta para esta pergunta está na estrutura como a linguagem PHP foi desenvolvida. Para quem não sabe, PHP é uma linguagem criada (compilada) com a linguagem C. Em outras palavras, PHP é um "programa" em C que recebe um texto (um código escrito em PHP), interpreta este texto e executa as instruções. Além disso, PHP foi planejado para suportar o acoplamento de módulos extras, também escritos em C (já falamos sobre isso no artigo "Instalação de Módulos Pear e Pecl"), que podem ser nativos da linguagem, disponíveis em repositórios externos, ou simplesmente desenvolvidos em carater particular.

E como isso explica alguns recursos serem específicos? Alguns destes módulos escritos em C, na verdade não fazem todo o trabalho árduo propostos por eles. Eles simplesmente utilizam algum recurso externo (uma biblioteca .dll ou .so) e oferecem uma interface PHP entre este recurso e a linguagem PHP. Algumas bibliotecas podem utilizar recursos específicos de um servidor HTTP, S.O. ou de um hardware, por isso eles não são totalmente portáveis em PHP.

Um exemplo de módulo que na verdade é a "tradução" de uma biblioteca C é o módulo GD. A biblioteca GD é um recurso desenvolvido nativamente para ser utilizado por programas em C e possui um conjunto de funções para serem usados em C. Porém, uma equipe desenvolveu um módulo do PHP que requer a biblioteca GD instalada no servidor para funcionar. O módulo GD para PHP, segue as especificações para criação de módulos em PHP (é como um "framework" de PHP), e oferece funcionalidades semelhantes às oferecidas pela biblioteca GD nativa, inclusive com nomes de funções semelhantes. Logo, o módulo fica preso à portabilidade da biblioteca GD.

Alguns módulos de PHP usam a estratégia de omitir funções que não são portáveis. Ou seja, se uma função utiliza um recurso que só é disponível em Linux, o módulo oferece a função se for instalado num servidor Linux, e omite apenas esta função, se estiver instalando num servidor diferente.

Também existem casos de códigos PHP não portáveis. Nestes casos, ou o código utiliza um módulo do PHP (escrito em C) que não é portável, ou realmente utiliza recursos específicos. Por exemplo, se um código PHP consulta informações no diretório "/etc", sabemos que nem todo S.O. utiliza este diretório para guardar arquivos de configuração, logo, o módulo não funcionará adequadamente em todos sistemas. Se for uma função, existe a possibilidade de oferecer o recurso apenas para determinados S.O., mas se for uma classe, fica um pouco mais complicado oferecer um método ou não dinamicamente.


Determinando se um Recurso é Portável

Antes de mais nada, quando desenvolvemos qualquer código PHP, precisamos estar cientes do que é um recurso nativo de PHP e do que faz parte de um módulo extra. Ao utilizar qualquer função ou classe que não foi escrita em PHP e incluída, saiba que ela faz parte de algum módulo. Alguns módulos, fazem parte do Core do PHP, outros não; alguns estão disponíveis para todas versões do PHP, outros não; alguns estão disponíveis para todos S.O., outros não.

Para saber se uma função é de um módulo que faz parte do Core do PHP, procure-a no manual do PHP. Por exemplo, se queremos saber se a função strlen faz parte de um módulo do Core, primeiro procuramos ela no manual e chegamos à documentação da função strlen. Nesta página, são mostradas em que versões do PHP a função está disponível (PHP 4, PHP 5). Ao lado esquerdo, vemos a estrutura hierárquica até chegar à função, que é: (i) "PHP Manual" » (ii) "Function Reference" » (iii) "Text Processing" » (iv) "Strings" » (v) "String Functions". O nível (i) é o próprio manual do PHP; o nível (ii) é a seção do manual onde estão as referências de funções; o nível (iii) é uma seção das referências de funções que tratam de extensões que trabalham com processamento de texto; o nível (iv) é o nome do módulo onde está a função; e o nível (v) é a seção da documentação do módulo que mostra a lista de funções disponíveis. Se voltarmos para o nível (iv), ou seja, a documentação do módulo "Strings", veremos que ela apresenta introdução, instalação/configuração, constantes pré-definidas, e a lista de funções (outros módulos podem apresentar seções extras, como "exemplos"). Lendo toda seção "Instalação/Configuração", veremos que este módulo faz parte do Core do PHP, já que é apresentada a informação:

There is no installation needed to use these functions; they are part of the PHP core.

Traduzindo:

Não há nenhuma instalação necessária para utilizar estas funções, elas fazem parte do núcleo do PHP.

Como o módulo Strings faz parte do núcleo do PHP, a função está disponível nas versões 4 e 5 de PHP, e não existe nenhuma observação (nota) na página de documentação da função, então podemos usá-la sem problemas nas versões 4 e 5, independente de S.O. ou hardware.

Note que alguns módulos podem ser melhorados e novas funções incorporadas. Então, pode ocorrer de um módulo fazer parte do núcleo, mas uma função só ter surgido em uma versão específica do PHP, por exemplo, o método setAccessible da classe ReflectionMethod do módulo Reflection. Note que ele está disponível no PHP 5 a partir da versão 5.3.2 (PHP 5 >= 5.3.2).


Lidando com a Portabilidade

Ao implementar um código PHP que utiliza um módulo que não é totalmente portável, ou que pode ser desativado, podemos determinar se um recurso está disponível utilizando algumas estratégias:

  • Verificar se o módulo está instalado através da função extension_loaded.
  • Verificar se uma função está disponível através da função function_exists.
  • Verificar em qual S.O. o servidor está através da constante PHP_OS.
  • Verificar qual a versão do PHP através da constante PHP_VERSION e compará-la com outra versão através da função version_compare.

Não é necessário utilizar todos estas estratégias sempre, nem verificar se a função está disponível toda vez que ela é chamada. Cada uma pode ser útil em uma situação específica. Por exemplo, podemos verificar se o servidor está configurado com todas as extensões necessárias durante a instalação do sistema. Ou então, no construtor de uma classe, checar se a extensão foi carregada, ou se uma determinada função está disponível, etc.

É importante prevenir o uso de um recurso específico pois se ele for utilizado sem estar disponível, é gerado um erro fatal (função desconhecida, ou classe inexistente, etc.), toda a execução do script é interrompida, e o usuário final não fica nem sabendo o que ocorreu. Verificando a disponibilidade do recurso antes, é possível, ao menos, informar ao usuário que um recurso necessário para realizar a operação desejada por ele não está disponível no servidor e, opcionalmente, que ele pode contactar os administradores do sistema para solucionarem o problema.

Caso seja possível, ofereça formas alternativas que realizam a mesma operação. Por exemplo, vamos ver um trecho de código cujo objetivo é montar uma parte de um e-mail que possui vários arquivos embutidos:

...
    if (function_exists('quoted_printable_encode')) {
        $header = 'Content-Type: text/plain; charset=UTF-8'.$eol.
                  'Content-Transfer-Encoding: quoted-printable'.$eol.
                  'Content-Disposition: inline'.$eol;
        $parte = quoted_printable_encode($mensagem).$eol;
    } elseif (function_exists('imap_8bit')) {
        $header = 'Content-Type: text/plain; charset=UTF-8'.$eol.
                  'Content-Transfer-Encoding: quoted-printable'.$eol.
                  'Content-Disposition: inline'.$eol;
        $parte = imap_8bit($mensagem).$eol;
    } else {
        $header = 'Content-Type: text/plain; charset=UTF-8'.$eol.
                  'Content-Transfer-Encoding: base64'.$eol.
                  'Content-Disposition: inline'.$eol;
        $parte = chunk_split(base64_encode($mensagem), 76, $eol).$eol;
    }
...

Para codificar o conteúdo de um e-mail, podemos usar quoted-printable ou base64. A primeira alternativa deixa o e-mail menor, então é mais recomendada, mas, caso a função quoted_printable_encode e a função imap_8bit (que realizam a codificação quoted-printable) não estejam disponíveis, sabemos que a função base64_encode em conjunto com chunk_split provavelmente estarão, já que fazem parte do Core do PHP. Desta forma, o código realiza o propósito dentro de suas capacidades.

0 comentários