1. Introdução
A implementação de tipos de objetos obrigatórios em PHP às vezes pode ser muito importante. Se estiver faltando, seja por falta de conhecimento - com base em suposições de programação incorretas ou simplesmente por preguiça - você verá resultados inesperados em seu aplicativo da Web específico. Principalmente ao programar em PHP 4, é muito fácil utilizar a função "is_a()" (embora existam outros métodos) para verificar o tipo do objeto com o qual você está trabalhando. É claro que forçar tipos de objetos também pode ser usado para filtrar objetos de entrada que precisam ser passados como parâmetros para outras classes PHP na mesma aplicação.
No entanto, o PHP 4 não expõe alguns pontos fracos em seu modelo de objetos - ocasionalmente pode exigir a escrita de código adicional para implementar certos recursos encontrados em linguagens maduras orientadas a objetos. Este fato é conhecido pela comunidade PHP há muito tempo. Entretanto, com o lançamento do PHP 5, muitos desses recursos extremamente valiosos foram adicionados como parte do modelo de objeto aprimorado. Eles ajudarão a implementar mais detalhadamente o desenvolvimento de código baseado em objetos - permitindo que você use características específicas do objeto.
No caso acima, cuidado especial deve ser tomado quando se trata de coerção de tipo de objeto. Na verdade, o PHP 5 fornece aos desenvolvedores pelo menos duas maneiras de verificar os tipos de objetos durante a execução de uma aplicação web - elas são o operador "instanceof" e o recurso "type hint". Agora, voltando ao tópico principal deste artigo, apresentarei o uso do operador "instanceof" no PHP 5. Você logo descobrirá que ele pode ser muito conveniente para determinar se o objeto com o qual você está trabalhando pertence a um tipo específico;
Este artigo irá ajudá-lo a entender como implementar tipos de objetos obrigatórios no PHP 5 através de alguns exemplos orientados a objetos.
2. O que você não deve fazer
Para mostrar como implementar a coerção de tipo de objeto no PHP 5, usarei a classe widget (X)HTML e uma classe construtora de páginas simples, com modificações simples para se adequar ao ambiente de desenvolvimento do PHP 5.
Meu primeiro exemplo lista algumas classes de widget (X)HTML que derivam de uma classe base abstrata "HTMLElement", que ignora a verificação do tipo de seus objetos de entrada. Por favor, observe primeiro a seguinte classe:
//Defina a classe abstrata 'HTMLElement'
classe abstrata HTMLElement{
atributos $ protegidos;
função protegida __construct($atributos){
if(!is_array($atributos)){
throw new Exception('Tipo de atributo inválido');
}
$this->atributos=$atributos;
}
// Método abstrato 'getHTML()' função abstrata protegida getHTML();
}
//Define a classe específica 'Div'-extends HTMLElement
classe Div estende HTMLElement{
private $saída='<div ';
$dados privados;
função pública __construct($atributos=array(),$dados){
pai::__construct($atributos);
$this->dados=$dados;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
$this->saída.=$this->dados.'</div>';
retorne $isto->saída;
}
}
//Define a classe concreta 'Header1' - estende HTMLElement
classe Header1 estende HTMLElement{
private $saída='<h1 ';
$dados privados;
função pública __construct($atributos=array(),$dados){
pai::__construct($atributos);
$this->dados=$dados;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
$this->saída.=$this->dados.'</h1>';
retorne $isto->saída;
}
}
//Define a classe concreta 'Parágrafo' - estende HTMLElement
classe Parágrafo estende HTMLElement{
private $saída='<p ';
$dados privados;
função pública __construct($atributos=array(),$dados){
pai::__construct($atributos);
$this->dados=$dados;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
$this->saída.=$this->dados.'</p>';
retorne $isto->saída;
}
}
//Define a classe concreta 'UnorderedList' - estende HTMLElement
classe UnorderedList estende HTMLElement{
private $saída='<ul ';
private $itens=array();
função pública __construct($atributos=array(), $items=array()){
pai::__construct($atributos);
if(!is_array($itens)){
throw new Exception('Parâmetro inválido para itens da lista');
}
$this->items=$items;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
foreach($this->itens as $item){
$this->output.='<li>'.$item.'</li>';
}
$this->output.='</ul>';
retorne $isto->saída;
}
}
Como você pode ver, as classes de widget (X)HTML acima são muito úteis para gerar elementos específicos em uma web, mas eu escrevi intencionalmente o código para cada classe para que eles não pudessem validar a eficácia dos parâmetros de entrada. Como você deve ter imaginado, os parâmetros de entrada são passados diretamente para o construtor da classe e atribuídos como propriedades. Surge a pergunta: há algo de errado em fazer isso? Sim, existe. Agora, vou definir minha classe de construtor de páginas mais simples e alimentá-la com widgets como este para que você possa ver como a entrada dessa classe é misturada com objetos incorretos. Aqui está a assinatura da classe geradora de páginas:
class PageGenerator{
privado $saída='';
privado $título;
função pública __construct($title='Página Padrão'){
$this->título=$título;
}
função pública doHeader(){
$this->output='<html><head><title>'.$this-
>título.'</título></cabeça><corpo>';
}
função pública addHTMLElement($htmlElement){
$this->output.=$htmlElement->getHTML();
}
função pública doFooter(){
$this->output.='</body></html>';
}
função pública buscarHTML(){
retorne $isto->saída;
}
}
Agora, começamos a instanciar alguns objetos widget (X)HTML e passá-los para as classes geradoras correspondentes, conforme mostrado no exemplo a seguir:
try{
//Gerar alguns elementos HTML $h1=new Header1(array('name'=>'header1', 'class'=>'headerclass'), 'Content for H1
elemento vai aqui');
$div=new Div(array('name'=>'div1','class'=>'divclass'),'Conteúdo para elemento Div
vai aqui');
$par=new Parágrafo(array('name'=>'par1','class'=>'parclass'),'Conteúdo para Parágrafo
elemento vai aqui');
$ul=new UnorderedList(array ('nome'=>'lista1', 'classe'=>'listaclasse'), array
('item1'=>'valor1', 'item2'=>'valor2', 'item3'=>'valor3'));
//Instancia a classe geradora de páginas $pageGen=new Page Generator();
$pageGen->doHeader();
//Adicionar objeto 'HTMLElement' $pageGen->addHTMLElement($h1);
$pageGen->addHTMLElement($div);
$pageGen->addHTMLElement($par);
$pageGen->addHTMLElement($ul);
$pageGen->doFooter();
//Exibir página da web echo $pageGen->fetchHTML();
}
catch(Exceção $e){
echo $e->getMessage();
saída();
}
Depois de executar o código PHP acima, o resultado obtido é uma página web simples - ela contém alguns objetos (X)HTML criados anteriormente. Neste caso, é fácil entender o que acontecerá se por algum motivo a classe construtora de páginas receber um objeto incorreto e chamar seu método "addHTML()". Aqui, reformulei a condição de conflito - usando um objeto widget (X)HTML inexistente. Por favor, observe o seguinte código novamente:
try{
//Gerar alguns elementos HTML $h1=new Header1(array('name'=>'header1', 'class'=>'headerclass'), 'Content for H1
elemento vai aqui');
$div=new Div(array('name'=>'div1','class'=>'divclass'),'Conteúdo para elemento Div
vai aqui');
$par=new Parágrafo(array('name'=>'par1','class'=>'parclass'),'Conteúdo para Parágrafo
elemento vai aqui');
$ul=new UnorderedList(array ('nome'=>'lista1', 'classe'=>'listaclasse'), array
('item1'=>'valor1', 'item2'=>'valor2', 'item3'=>'valor3'));
//Instancia a classe geradora de páginas $pageGen=new Page Generator();
$pageGen->doHeader();
//Adiciona objeto 'HTMLElement' $pageGen->addHTMLElement($fakeobj) //Passa objeto inexistente para este método $pageGen->addHTMLElement($div);
$pageGen->addHTMLElement($par);
$pageGen->addHTMLElement($ul);
$pageGen->doFooter();
// Exibe a página echo $pageGen->fetchHTML();
}
catch(Exceção $e){
echo $e->getMessage();
saída();
}
Neste caso, conforme mostrado na linha a seguir:
$pageGen->addHTMLElement($fakeobj)//Passa objeto inexistente para este método
Um objeto widget (X)HTML inexistente é passado para a classe geradora de página, este resultará em um erro fatal:
Erro fatal: Chamada para uma função membro em um não-objeto em
E quanto ao
caminho/para/arquivo
?Esta é uma penalidade direta por não verificar o tipo do objeto passado para a classe geradora! Portanto, lembre-se disso ao escrever seus scripts. Felizmente, existe uma solução simples para esses problemas, e é aí que entra o poder do operador “instanceof”. Se você quiser ver como esse operador é usado, continue lendo.
3. Use o operador "instanceof"
Como você pode ver, o uso do operador "instanceof" é muito simples. Ele usa dois parâmetros para completar sua função. O primeiro parâmetro é o objeto que você deseja verificar e o segundo parâmetro é o nome da classe (na verdade, um nome de interface) usado para determinar se o objeto é uma instância da classe correspondente. Claro, usei a terminologia acima intencionalmente para que você possa ver como esse operador é intuitivo de usar. Sua sintaxe básica é a seguinte:
if (object instanceof class name){
//Faça algo útil
}
Agora que você entende como esse operador é usado no PHP 5, vamos definir a classe correspondente do construtor de páginas web para verificar o tipo de objeto passado para seu método "addHTMLElement()". Aqui está a nova assinatura desta classe, que mencionei anteriormente usa o operador "instanceof":
class PageGenerator{
privado $saída='';
privado $título;
função pública __construct($title='Página padrão'){
$this->título=$título;
}
função pública doHeader(){
$this->output='<html><head><title>'.$this->title.'</title></head><body>';
}
função pública addHTMLElement($htmlElement){
if(!$htmlElement instância de HTMLElement){
throw new Exception('Elemento (X)HTML inválido');
}
$this->output.=$htmlElement->getHTML();
}
função pública doFooter(){
$this->output.='</body></html>';
}
função pública buscarHTML(){
retorne $isto->saída;
}
}
Observe, na classe acima, como o operador "instanceof" é incluído no método "addHTMLElement()" para garantir que todos os objetos passados sejam instâncias da classe "HTMLElement" definida anteriormente. Agora, é possível reconstruir a página da web que você viu anteriormente; nesse caso, certifique-se de que todos os objetos de entrada passados para a classe do construtor de páginas da web sejam objetos de widget (X)HTML reais. Aqui está o exemplo correspondente:
try{
//Gerar alguns elementos HTML $h1=new Header1(array('name'=>'header1', 'class'=>'headerclass'), 'Content for H1 element go here');
$div=new Div(array('name'=>'div1', 'class'=>'divclass'), 'O conteúdo do elemento Div vai aqui');
$par=new Paragraph(array('name'=>'par1','class'=>'parclass'),'O conteúdo do elemento Parágrafo vai aqui');
$teststr='Este não é um elemento HTML';
//Instancia a classe geradora de páginas $pageGen=new Page Generator();
$pageGen->doHeader();
//Adiciona o objeto 'HTMLElement' $pageGen->addHTMLElement($teststr) //Passa uma string simples para este método $pageGen->addHTMLElement($h1);
$pageGen->addHTMLElement($div);
$pageGen->addHTMLElement($par);
$pageGen->doFooter();
//Exibir página da web echo $pageGen->fetchHTML();
}
catch(Exceção $e){
echo $e->getMessage();
saída();
}
Como você viu no exemplo acima, estou passando uma string de teste simples (não um objeto "HTMLElement") na classe do construtor de páginas, que lançará uma exceção - capturada por um bloco "catch" específico, conforme mostrado abaixo:
Elemento HTML (X) inválido
Neste momento, para determinar a validade do objeto de entrada, usei o operador "instanceof", para que a página da web acima possa ser. Espero que você possa realmente apreciar a importância de filtrar a entrada para os métodos de sua classe para evitar erros estranhos usando este operador Entrada de dados
Depois de mostrar a implementação correta do operador "instanceof" dentro da classe geradora de páginas da web, há mais coisas a fazer, semelhante ao que escrevi para PHP 4 no artigo anterior. (X). Para classes de widget HTML, eu gostaria de incluir este operador como parte de seu método "getHTML()", permitindo assim a criação de páginas web que geram elementos (X)HTML aninhados.
4. Estenda o uso do operador "instanceof": widgets (X)HTML aninhados
são bons. Você viu que o operador "instanceof" tem um bom desempenho na verificação de tipo em objetos de entrada que são injetados diretamente na funcionalidade do construtor de páginas. Agora, darei um passo adiante e adicionarei uma rotina de verificação ao construtor e ao método "getHTML()" da classe do widget (X)HTML para que eles possam aceitar outros widgets como parâmetros de entrada.
classe Div estende HTMLElement{
private $saída='<div ';
$dados privados;
função pública __construct($atributos=array(),$dados){
if(!$data instanceof HTMLElement&&!is_string($data)){
throw new Exception('Tipo de parâmetro inválido');
}
pai::__construct($atributos);
$this->dados=$dados;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=($this->data instanceof HTMLElement)?
$this->dados->getHTML():$this->dados;
$this->output.='</div>';
retorne $isto->saída;
}
}
classe Header1 estende HTMLElement{
private $saída='<h1 ';
$dados privados;
função pública __construct($atributos=array(),$dados){
if(!$data instanceof HTMLElement&&!is_string($data)){
throw new Exception('Tipo de parâmetro inválido');
}
pai::__construct($atributos);
$this->dados=$dados;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=($this->data instanceof HTMLElement)?
$this->dados->getHTML():$this->dados;
$this->output.='</h1>';
retorne $isto->saída;
}
}
classe Parágrafo estende HTMLElement{
private $saída='<p ';
$dados privados;
função pública __construct($atributos=array(),$dados){
if(!$data instanceof HTMLElement&&!is_string($data)){
throw new Exception('Tipo de parâmetro inválido');
}
pai::__construct($atributos);
$this->dados=$dados;
}
//A implementação específica da função pública do método 'getHTML()' getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
$this->output.=($this->data instanceof HTMLElement)?
$this->dados->getHTML():$this->dados;
$this->output.='</p>';
retorne $isto->saída;
}
}
classe UnorderedList estende HTMLElement{
private $saída='<ul ';
private $itens=array();
função pública __construct($atributos=array(), $items=array()){
pai::__construct($atributos);
if(!is_array($itens)){
throw new Exception('Parâmetro inválido para itens da lista');
}
$this->items=$items;
}
//A implementação específica do método 'getHTML()'
função pública getHTML(){
foreach($this->atributos como $attribute=>$value){
$this->output.=$atributo.'="'.$valor.'" ';
}
$this->output=substr_replace($this->output,'>',-1);
foreach($this->itens as $item){
$this->output.=($item instanceof
HTMLElement)?'<li>'.$item->getHTML().'</li>':'<li>'.$item.'</li>';
}
$this->output.='</ul>';
retorne $isto->saída;
}
}
Conforme mostrado nas classes acima, para permitir que elementos (X)HTML aninhados sejam implementados ao gerar as páginas web correspondentes, refatorei seus construtores e métodos "getHTML()" respectivamente. Observe que incluí o seguinte bloco condicional no construtor de cada classe:
if(!$data instanceof HTMLElement&&!is_string($data)){
throw new Exception('Tipo de parâmetro inválido');
}
Neste ponto, o que eu realmente faço é garantir que apenas dados de string e objetos do tipo "HTMLElement" sejam permitidos como parâmetros de entrada para cada classe. Caso contrário, uma exceção será lançada pelo respectivo método e poderá fazer com que a aplicação interrompa a execução. Então, este é o processo de verificação dos dados de entrada. Agora, vamos dar uma olhada na nova assinatura do método "getHTML()", que também usa o operador "instanceof":
$this->output.=($this->data instanceof HTMLElement)?$this->data-
>getHTML():$this->data;
Como você pode ver, neste caso, o operador this é muito útil para aproveitar as vantagens dos recursos polimórficos da classe de widget (X)HTML. Se o atributo $data também for um widget, então seu método "getHTML()" será chamado corretamente, o que fará com que o elemento web aninhado seja exibido. Por outro lado, se for apenas uma string, ela será adicionada diretamente a todas as saídas da classe atual.
Neste ponto, você deve ter entendido o uso do operador “instanceof” no PHP 5 para garantir que certos objetos pertençam a um tipo específico. Como você pode ver neste artigo, coagir tipos de objetos no PHP 5 é na verdade uma tarefa bastante simples. Por enquanto, é melhor você desenvolver um exemplo de uso desse método para filtrar objetos em seu aplicativo PHP para aprofundar seu entendimento.
5. Resumo
Neste artigo, você aprendeu como usar o operador "instanceof" no PHP 5 para verificar o tipo do seu objeto de entrada, porém o método que mostrei não é o único; Em um artigo posterior, explicarei como implementar o recurso de "dica de tipo" no PHP 5, que é outra maneira de impor a digitação de objetos.
Autor: Compilador Zhu Xianzhong Fonte: Tianji Development