domingo, fevereiro 11, 2007

Constantes e as limitações do PHP

Houston, we have a problem! Seriam limitações? Bugs? Sinceramente, ainda não sei, mas como o intuito é falar sobre PHP de maneira descompromissada, largo mão até da corriqueira mania de “evangelizar” (quem sou eu) a linguagem que trabalho há uns 7 anos e sou professor há quase outros tantos, para meter um pouco de pimenta no acarajé nosso de cada dia.

Lembro vagamente do meu professor de matemática em alguma série ginasial dizendo que uma constante é uma maneira de representar algum dado absoluto e imutável. Na verdade não lembro, odiava matemática (putz, por que eu fui odiar logo matemática), mas certamente ele falou quando fez a gente engolir aquele ¶ obscuro. Quando comecei a estudar PHP, logo li que constantes também só admitem valores escalares. Até aí tudo bem, estava iniciando na linguagem e pude sobreviver sem questionar nada.

O PHP5 trouxe alguns avanços na programação orientada a objetos e blábláblá que você, se está aqui, já deve saber. Entre as dádivas da quinta geração está a instrução const. Com ela é possível declarar constantes de classe, ou seja, uma maneira de representar algum dado absoluto e imutável, como dizia meu professor (ou seria professora, ...), agora inerente a uma classe.

Via de regra, constantes de classe são sempre públicas e estáticas (estão vinculadas à própria classe e não às suas instâncias), tanto é que declarar algo como public const ou static const são erros de sintaxe por se tratar de algo redundante. Sobre elas recai a mesma restrição imposta às suas irmãs globais, oriundas de instruções define, no que diz respeito à impossibilidade de assumirem valores como objetos ou arrays.

Perdoai minha insolência, mas por que esta limitação? O que há de mal nas linhas:

define('MEU_ARRAY', array("a" => 1));
echo MEU_ARRAY["a"];

ou

define('MEU_OBJETO', new Objeto());
echo MEU_OBJETO->getQualquerCoisa();

Quando o assunto é array, concordo que pode não ser interessante ter que garantir a constância de uma estrutura complexa na memória. No entanto, ao atribuir um objeto a uma variável no PHP5, seu valor será apenas uma referência ao objeto. Em outras palavras, nenhuma variável no PHP5 possui um valor complexo, mas sim um dado escalar referente ao endereço de memória onde reside de fato o objeto. Por que então as constantes, que só admitem valores escalares, não podem agora referenciar objetos? Note que o que deve ser imutável é o valor da constante, ou seja, a referência ao objeto. Mas o objeto em si pode sofrer alterações em seus atributos sem maiores preocupações.

Só para ilustrar, suponha uma listagem de dados de clientes (dentre eles os seus endereços). A classe Cliente possui um atributo do tipo Endereço que por sua vez, possui um atributo do tipo Uf a fim de representar qual o Estado do cliente. Ao buscar os dados do banco e construir o objeto cliente, uma instrução como essa:

while ( ... ) {
$cliente = ...;
$cliente->endereco->uf = new Uf($registroDoBanco[‘uf_sigla’]);
$lista[] = $cliente;
}

Criaria um objeto Uf para cada cliente, mesmo que a sigla obtida no banco de dados seja a mesma obtida numa linha anterior.

Para evitar essas instâncias desnecessárias, seria possível carregar um array de Ufs obtidas e só criar uma nova quando esta for baseada numa nova sigla. Veja:

while (...) {

$cliente = ...;

$uf = $registroDoBanco[‘uf_sigla’];

if (!in_array($uf, $ufsJaObtidas)) {

$ufsJaObtidas[$uf] = new Uf($uf);

}

$cliente->endereco->uf = $ufsJaObtidas[$uf];

$lista[] = $cliente;

}

No entanto, gostaria de contar com uma saída mais elegante em que eu não me preocupasse em sobrecarregar meu código sempre que fosse listar algo do tipo. Pensei no padrão de projeto Typesafe Enum.

Joshua Bloch, idealizador deste padrão, em seu livro Effective Java Programming Language Guide, recomenda 57 regras para programadores Java. Dentre elas, quatro são focadas em trazer construções interessantes disponíveis na linguagem C. Uma delas (o item 21) são justamente os tipos enumerados (enum). Estes são comuns em diversas linguagens e bancos de dados. São úteis em momentos em que você já conhece todas as variações possíveis que determinado dado pode assumir.

Devido à popularidade deste padrão de projeto, o JCP (Java Community Process – entidade que dita os caminhos dessa tecnologia) resolveu, sob a JSR 161, introduzir a declaração de tipos Enum no Java 5 (tiger), o que já era comum em ambientes .NET. Dizem as más línguas, esse fato foi um dos principais catalisadores desta JSR.

Pois bem, no PHP não temos o tipo Enum, no entanto o padrão Typesafe Enum poderia resolver nosso problema, veja como seria declarada a classe Uf:

class Uf {

const AC = new Uf("AC"); const AL = new Uf("AL");

const AM = new Uf("AM"); const AP = new Uf("AP");

const BA = new Uf("BA"); const CE = new Uf("CE");

const DF = new Uf("DF"); const ES = new Uf("ES");

const GO = new Uf("GO"); const MA = new Uf("MA");

const MG = new Uf("MG"); const MS = new Uf("MS");

const MT = new Uf("MT"); const PA = new Uf("PA");

const PB = new Uf("PB"); const PE = new Uf("PE");

const PI = new Uf("PI"); const PR = new Uf("PR");

const RJ = new Uf("RJ"); const RN = new Uf("RN");

const RO = new Uf("RO"); const RS = new Uf("RS");

const SC = new Uf("SC"); const SE = new Uf("SE");

const SP = new Uf("SP"); const TO = new Uf("TO");

private $sigla;

private $nome;

private function __construct($sigla = "") {

$this->setSigla($sigla);

}

public function getSigla() {

return $this->sigla;

}

private function setSigla($sigla) {

$this->sigla = $sigla;

switch($this->sigla) {

case "AC":

$this->setNome("Acre");

break;

case "AL":

$this->setNome("Alagoas");

break;

// ... E assim por diante

}

}

public function getNome() {

return $this->nome;

}

public function setNome($nome) {

$this->nome = $nome;

}

}

?>

Veja que a classe Uf já declarou uma constante para cada Estado do Brasil e atribuiu a ela uma instância dela mesma, ou seja, cada constante da classe Uf é um objeto Uf, cuja sigla e nome serão configurados de acordo com a string passada como parâmetro ao seu construtor. Assim como no Singleton, o construtor é privado para que ninguém tente construir um objeto Uf, todos eles já serão criados na primeira referência à classe Uf de todo o script.

Nosso script de acesso a dados ficaria assim:

while ( ... ) {

$cliente = ...;

$cliente->endereco->uf = Uf::$registroDoBanco[‘uf_sigla’];

$lista[] = $cliente;

}

No caso da sigla obtida no banco de dados ser PE, seria o mesmo que termos:

$cliente->endereco->uf = Uf::PE;

Simples e eficaz, no entanto é preciso ter cuidado com o uso dos tipos enumerados. Recomendo apenas em casos de objetos simples e relativamente poucos. Não considero 26 objetos compostos por dois atributos de pequenas strings como algo degradante em termos de desempenho, porém não o recomendo em casos onde os objetos sejam muitos ou possuam gráficos extensos, visto que haverá muita alocação desnecessária de memória no momento da primeira referência à classe enum.

Nossa classe Uf não pôde implementar o Typesafe Enum justamente pela limitação de valores escalares para constantes. Tive uma idéia. Por que não declarar as constantes simplesmente como variáveis de classe (static). Ora, eu vou perder a garantia do valor constante (perdão professor (a)), mas posso obter o mesmo resultado:

class Uf {

public static $AC = new Uf("AC"); public static $AL = new Uf("AL");

}

O código de acesso ficaria:

$uf = ::$registroDoBanco[‘uf_sigla’];

$cliente->endereco->uf = Uf::$$uf;

Entrei no ônibus, esbarrei na manivela. O PHP também não suporta chamada a qualquer procedimento no momento da declaração de atributos. O curioso é que se nossas variáveis de classe $AC e companhia fossem arrays, não haveria problemas:

class Uf {

public static $AC = array ("AC");

}

This is Houston. Say again please! Bom, minha conclusão antes que a véia que for passando me leve pro delegado é que constantes não podem referenciar objetos por um triste legado do PHP4 onde estes eram, como li em algum lugar, arrays que comeram espinafre. Procurei algo a respeito no site de bugs do PHP, mas como não encontrei nada, achei que o problema era comigo mesmo. Fessora, suas aulas de matemática eram ótimas, eu juro.

Marcadores: , ,

0 Comentários:

Postar um comentário

Assinar Postar comentários [Atom]

<< Página inicial