Usando os novos recursos de linguagem do PHP V5, a capacidade de manutenção e a confiabilidade do código podem ser significativamente melhoradas. Ao ler este artigo, você aprenderá como aproveitar esses novos recursos para migrar código desenvolvido em PHP V4 para PHP V5.
O PHP V5 fez melhorias significativas com base no PHP V4. Novos recursos de linguagem tornam mais fácil construir bibliotecas de classes confiáveis e mantê-las. Além disso, reescrever a biblioteca padrão ajudou a alinhar o PHP com outros idiomas da Web, como a linguagem de programação Java™. Vamos dar uma olhada em alguns dos novos recursos orientados a objetos do PHP e aprender como migrar o código existente do PHP V4 para o PHP V5.
Primeiro, vamos ver como os novos recursos da linguagem e o criador do PHP mudaram a forma como os objetos são criados com o PHP V4. A ideia do V5 era criar uma linguagem de força industrial para o desenvolvimento de aplicações Web. Isso significa compreender as limitações do PHP V4, extrair arquiteturas de linguagem boas e conhecidas de outras linguagens (como Java, C#, C++, Ruby e Perl) e incorporá-las ao PHP.
O primeiro e mais importante novo recurso é a proteção de acesso para métodos de classe e variáveis de instância – as palavras-chave públicas, protegidas e privadas. Esse novo recurso permite que os designers de classe mantenham o controle sobre as propriedades intrínsecas de uma classe enquanto informam aos usuários da classe quais classes são acessíveis e quais não são.
No PHP V4, todo o código é público. No PHP V5, os designers de classes podem declarar qual código é visível para o mundo externo (público) e qual código é visível apenas dentro da classe (privado) ou apenas para subclasses da classe (protegido). Sem esses controles de acesso, o desenvolvimento de código em uma equipe grande ou a distribuição de código como uma biblioteca é prejudicado porque os usuários dessas classes provavelmente usarão métodos errados ou acessarão códigos que deveriam ser variáveis de membros privados.
Outra grande novidade são as palavras-chave interface e abstract, que permitem a programação de contratos. Programação contratual significa que uma classe fornece um contrato para outra classe - em outras palavras: "Isso é o que vou fazer e você não precisa saber como é feito." Todas as classes que implementam a interface aderem a este contrato. Todos os usuários de uma interface concordam em usar apenas os métodos especificados na interface. A palavra-chave abstract facilita muito o trabalho com interfaces, como explicarei mais tarde.
Esses dois recursos principais – controle de acesso e programação de contrato – permitem que grandes equipes de codificadores trabalhem de maneira mais tranquila com grandes bases de código. Esses recursos também permitem que o IDE forneça um conjunto mais rico de recursos com linguagem inteligente. Este artigo não apenas aborda vários problemas de migração, mas também explica como usar esses novos recursos importantes da linguagem.
Controle de Acesso
Para demonstrar os novos recursos da linguagem, utilizei uma classe chamada Configuração. Esta classe simples contém itens de configuração para o aplicativo Web - por exemplo, o caminho para o diretório de imagens. Idealmente, essas informações residiriam em um arquivo ou banco de dados. A Listagem 1 mostra uma versão simplificada.
Listagem 1. access.php4
<?php
configuração de classe
{
var $_items = array()
função Configuração() {
$this->_items[ 'imgpath' ] = 'imagens';
}
função get($chave) {
retornar $this->_items[ $key ];
}
}
$c = nova configuração();
echo( $c->get( 'imgpath' )."n" );
?>
Esta é uma classe PHP V4 completamente ortodoxa. A variável membro contém a lista de itens de configuração, o construtor carrega os itens e o método de acesso denominado get() retorna o valor do item.
Após executar o script, o seguinte código aparecerá na linha de comando:
%php access.php4
imagens
%
muito bom! Este resultado significa que o código é executado normalmente e o valor do item de configuração imgpath é definido e lido normalmente.
O primeiro passo para converter esta classe para PHP V5 é renomear o construtor. No PHP V5, o método de inicialização de um objeto (construtor) é chamado __construct. Esta pequena mudança é mostrada abaixo.
Listagem 2. access1.php5
<?php
configuração de classe
{
var $_items = array();
função __construct() {
$this->_items[ 'imgpath' ] = 'imagens';
}
função get($chave) {
retornar $this->_items[ $key ];
}
}
$c = nova configuração();
echo( $c->get( 'imgpath' )."n" );
?>
As mudanças desta vez não são grandes. Acabei de mudar para a convenção PHP V5. A próxima etapa é adicionar controle de acesso à classe para garantir que os usuários da classe não possam ler e gravar diretamente a variável de membro $_items. Essa mudança é mostrada abaixo.
Listagem 3. access2.php5
<?php
configuração de classe
{
private $_items = array();
função pública __construct() {
$this->_items[ 'imgpath' ] = 'imagens';
}
função pública get($chave) {
retornar $this->_items[ $key ];
}
}
$c = nova configuração();
echo( $c->get( 'imgpath' )."n" );
?>
Se um usuário deste objeto acessasse diretamente a matriz de itens, o acesso seria negado porque a matriz está marcada como privada. Felizmente, os usuários descobriram que o método get() fornece as tão bem-vindas permissões de leitura.
Para ilustrar como usar permissões protegidas, preciso de outra classe, que deve herdar da classe Configuration. Chamei essa classe de DBConfiguration e presumi que a classe leria os valores de configuração do banco de dados. Esta configuração é mostrada abaixo.
Listagem 4. access3.php
<?php
configuração de classe
{
protegido $_items = array();
função pública __construct() {
$this->carregar();
}
função protegida carregar() { }
função pública get($chave) {
retornar $this->_items[ $key ];
}
}
classe DBConfiguration estende configuração
{
função protegida carregar() {
$this->_items[ 'imgpath' ] = 'imagens';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Esta listagem mostra o uso correto da palavra-chave protegida. A classe base define um método chamado load(). As subclasses desta classe substituirão o método load() para adicionar dados à tabela de itens. O método load() é interno à classe e suas subclasses, portanto, o método não é visível para todos os consumidores externos. Se as palavras-chave forem todas privadas, o método load() não poderá ser substituído.
Eu realmente não gosto desse design, mas o escolhi porque tive que dar à classe DBConfiguration acesso ao array de itens. Eu gostaria de continuar mantendo a matriz de itens inteiramente pela classe Configuration, para que, à medida que outras subclasses sejam adicionadas, essas classes não precisem saber como manter a matriz de itens. Fiz as seguintes alterações.
Listagem 5. access4.php5
<?php
configuração de classe
{
private $_items = array();
função pública __construct() {
$this->carregar();
}
função protegida carregar() { }
função protegida add($chave, $valor) {
$this->_items[ $chave ] = $valor;
}
função pública get($chave) {
retornar $this->_items[ $key ];
}
}
classe DBConfiguration estende configuração
{
função protegida carregar() {
$this->add( 'imgpath', 'imagens' );
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Matrizes de itens agora podem ser privadas porque as subclasses usam o método add() protegido para adicionar itens de configuração à lista. A classe Configuration pode alterar a maneira como armazena e lê itens de configuração independentemente de suas subclasses. Contanto que os métodos load() e add() sejam executados da mesma maneira, a subclasse não deverá apresentar problemas.
Para mim, o controle de acesso adicionado é o principal motivo para considerar a mudança para o PHP V5. É só porque Grady Booch disse que PHP V5 é uma das quatro principais linguagens orientadas a objetos? Não, porque uma vez aceitei uma tarefa para manter o código 100KLOC C++ no qual todos os métodos e membros eram definidos como públicos. Levei três dias para limpar essas definições e, no processo, reduzi significativamente o número de erros e melhorei a capacidade de manutenção. Por que? Porque sem controle de acesso é impossível saber como os objetos utilizam outros objetos, e é impossível fazer qualquer alteração sem saber quais obstáculos superar. Com C++, pelo menos ainda tenho o compilador disponível. O PHP não vem com compilador, então esse tipo de controle de acesso se torna ainda mais importante.
Programação de contrato
O próximo recurso importante a ser aproveitado ao migrar do PHP V4 para o PHP V5 é o suporte para programação de contrato por meio de interfaces, classes abstratas e métodos. A Listagem 6 mostra uma versão da classe Configuration na qual os codificadores do PHP V4 tentaram construir uma interface básica sem usar a palavra-chave interface.
Listagem 6. interface.php4
<?php
classe IConfiguração
{
função get($chave) { }
}
configuração de classe estende IConfiguration
{
var $_items = array()
função Configuração() {
$this->carregar();
}
função carregar() { }
função get($chave) {
retornar $this->_items[ $key ];
}
}
classe DBConfiguration estende configuração
{
função carregar() {
$this->_items[ 'imgpath' ] = 'imagens';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
A listagem começa com uma pequena classe IConfiguration que define todas as interfaces fornecidas pela classe Configuration ou classes derivadas. Esta interface definirá o contrato entre a classe e todos os seus usuários. O contrato estabelece que todas as classes que implementam IConfiguration devem ser equipadas com um método get() e que todos os usuários de IConfiguration devem insistir em usar apenas o método get().
O código abaixo é executado em PHP V5, mas é melhor usar o sistema de interface fornecido conforme mostrado abaixo.
Listagem 7. interface1.php5
<?php
interface ICConfiguração
{
função get($chave);
}
classe Configuração implementa IConfiguration
{
...
}
classe DBConfiguration estende configuração
{
...
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Por um lado, os leitores podem entender o status de execução com mais clareza, por outro lado, uma única classe pode implementar várias interfaces; A Listagem 8 mostra como estender a classe Configuration para implementar a interface Iterator, que é a interface interna do PHP.
Listagem 8. interface2.php5
<?php
interface IConfiguração {
...
}
classe Configuração implementa IConfiguration, Iterator
{
private $_items = array();
função pública __construct() {
$this->carregar();
}
função protegida carregar() { }
função protegida add($chave, $valor) {
$this->_items[ $chave ] = $valor;
}
função pública get($chave) {
retornar $this->_items[ $key ];
}
função pública rebobinar() { reset($this->_items });
função pública atual() { return atual($this->_items });
chave de função pública() { chave de retorno ($this->_items });
função pública next() { return next($this->_items });
função pública válida() { return ( $this->current() !== false });
}
class DBConfiguration estende configuração {
...
}
$c = new DBConfiguration();
foreach($c as $k => $v ) { echo( $k." = ".$v."n" });
?>
A interface Iterator permite que qualquer classe pareça ser uma matriz de seus consumidores. Como você pode ver no final do script, você pode usar o operador foreach para reiterar todos os itens de configuração no objeto Configuration. O PHP V4 não possui essa funcionalidade, mas você pode usar essa funcionalidade de diversas maneiras dentro da sua aplicação.
A vantagem do mecanismo de interface é que os contratos podem ser reunidos rapidamente sem a necessidade de implementar nenhum método. A etapa final é a implementação da interface, onde você deve implementar todos os métodos especificados. Outro novo recurso útil do PHP V5 são as classes abstratas, que facilitam a implementação da parte central de uma interface com uma classe base e, em seguida, o uso dessa interface para criar classes de entidade.
Outro uso de classes abstratas é criar uma classe base para múltiplas classes derivadas nas quais a classe base nunca é instanciada. Por exemplo, quando DBConfiguration e Configuration existem ao mesmo tempo, apenas DBConfiguration pode ser usado. A classe Configuration é apenas uma classe base – uma classe abstrata. Portanto, você pode forçar esse comportamento usando a palavra-chave abstrata conforme mostrado abaixo.
Listagem 9. abstract.php5
<?php
configuração de classe abstrata
{
protegido $_items = array();
função pública __construct() {
$this->carregar();
}
função abstrata protegida load();
função pública get($chave) {
retornar $this->_items[ $key ];
}
}
classe DBConfiguration estende configuração
{
função protegida carregar() {
$this->_items[ 'imgpath' ] = 'imagens';
}
}
$c = new DBConfiguration();
echo( $c->get( 'imgpath' )."n" );
?>
Agora, todas as tentativas de instanciar um objeto do tipo Configuration irão falhar porque o sistema considera a classe abstrata e incompleta.
Métodos e membros estáticos
Outro novo recurso importante no PHP V5 é o suporte para membros e métodos estáticos em classes. Ao usar essa funcionalidade, você pode usar o popular padrão singleton. Este padrão é ideal para a classe Configuration porque a aplicação deve ter apenas um objeto de configuração.
A Listagem 10 mostra a versão PHP V5 da classe Configuration como um singleton.
Listagem 10. static.php5
<?php
configuração de classe
{
privado $_items = array()
;
função pública estática get() {
if (self::$_instance == null)
self::$_instance = new Configuração();
retornar self::$_instance;
}
função privada __construct() {
$this->_items[ 'imgpath' ] = 'imagens';
}
função pública __get($chave) {
retornar $this->_items[ $key ];
}
}
echo(Configuração::get()->{ 'imgpath' }."n" );
?>
A palavra-chave estática tem muitos usos. Considere usar esta palavra-chave quando precisar acessar alguns dados globais para todos os objetos de um único tipo.
Método Mágico
Outra grande novidade no PHP V5 é o suporte para métodos mágicos, que permitem que objetos alterem rapidamente a interface do objeto - por exemplo, adicionando variáveis de membro para cada item de configuração no objeto Configuração. Não há necessidade de utilizar o método get(), basta procurar um determinado item e tratá-lo como um array, conforme mostrado abaixo.
Listagem 11. magic.php5
<?php
configuração de classe
{
private $_items = array();
função __construct() {
$this->_items[ 'imgpath' ] = 'imagens';
}
função __get($chave) {
retornar $this->_items[ $key ];
}
}
$c = nova configuração();
echo($c->{ 'imgpath' }."n" );
?>
Neste exemplo, criei um novo método __get() que é chamado sempre que o usuário procura uma variável membro no objeto. O código dentro do método usará então a matriz de itens para encontrar o valor e retornar esse valor como se houvesse uma variável de membro especificamente para aquela palavra-chave. Supondo que o objeto seja um array, no final do script você pode ver que usar o objeto Configuration é tão simples quanto encontrar o valor de imgpath.
Ao migrar do PHP V4 para o PHP V5, você deve estar atento a esses recursos da linguagem que estão completamente indisponíveis no PHP V4, e deve revalidar as classes para ver como elas podem ser utilizadas.
As exceções
finalmente encerram este artigo introduzindo o novo mecanismo de exceção no PHP V5. As exceções fornecem uma maneira completamente nova de pensar sobre o tratamento de erros. Todos os programas inevitavelmente geram erros - arquivo não encontrado, falta de memória, etc. Se exceções não forem usadas, um código de erro deverá ser retornado. Por favor, veja o código PHP V4 abaixo.
Listagem 12. arquivo.php4
<?php
função parseLine( $l )
{
// ...
return array('erro' => 0,
dados => array() // dados aqui
);
}
função readConfig($caminho)
{
if ($caminho == null) return -1;
$fh = fopen( $caminho, 'r' );
if ($fh == null) return -2
;
$l = fgets( $fh );
$ec = parseLine( $l );
if ( $ec['error'] != 0 ) return $ec['error'];
}
fclose($fh);
retornar 0;
}
$e = readConfig( 'meuconfig.txt' );
se ( $e != 0 )
echo("Ocorreu um erro (".$e.")n" );
?>
Este código de E/S de arquivo padrão lerá um arquivo, recuperará alguns dados e retornará um código de erro se algum erro for encontrado. Tenho duas perguntas sobre esse script. O primeiro é o código de erro. O que esses códigos de erro significam? Para descobrir o que significam esses códigos de erro, você deve criar outro sistema para mapear esses códigos de erro em sequências significativas. O segundo problema é que o resultado retornado de parseLine é muito complicado. Eu só preciso que ele retorne dados, mas na verdade ele precisa retornar um código de erro e dados. A maioria dos engenheiros (inclusive eu) costuma ficar com preguiça e apenas retornar dados e ignorar os erros porque os erros são difíceis de gerenciar.
A Listagem 13 mostra quão claro o código é ao usar exceções.
Listagem 13. arquivo.php5
<?php
função parseLine( $l )
{
// Analisa e lança e exceção quando inválido
retornar matriz(); //dados
}
função readConfig($caminho)
{
if ($ caminho == nulo)
throw new Exception( 'argumento ruim' );
$fh = fopen( $path, 'r' );
se ($fh == nulo)
throw new Exception( 'não foi possível abrir o arquivo' );
while( !feof( $fh ) ) {
$l = fgets( $fh );
$ec = parseLine( $l );
}
fclose($fh);
}
tentar {
readConfig( 'meuconfig.txt');
} catch(Exceção $e) {
eco($e);
}
?>
Não preciso me preocupar com códigos de erro porque a exceção contém um texto descritivo do erro. Também não preciso pensar em como rastrear o código de erro retornado de parseLine porque a função apenas gerará um erro se ocorrer. A pilha se estende até o bloco try/catch mais próximo, que está na parte inferior do script.
As exceções revolucionarão a maneira como você escreve código. Em vez de gerenciar a dor de cabeça dos códigos de erro e mapeamentos, você pode se concentrar nos erros que deseja tratar. Esse código é mais fácil de ler, manter e eu diria que até encorajo você a adicionar tratamento de erros, pois geralmente rende dividendos.
Conclusão
Os novos recursos orientados a objetos e a adição do tratamento de exceções fornecem fortes motivos para a migração do código do PHP V4 para o PHP V5. Como você pode ver, o processo de atualização não é difícil. A sintaxe que se estende ao PHP V5 parece com o PHP. Sim, essas sintaxes vêm de linguagens como Ruby, mas acho que funcionam muito bem juntas. E essas linguagens expandem o escopo do PHP de uma linguagem de script para pequenos sites para uma linguagem que pode ser usada para completar aplicativos de nível empresarial.