Introdução
Com o PHP5 o programador passou a contar com Orientação a Objeto (OO) real, pois no PHP4 e inferiores a OO era apenas “simulada”.
Uma das funcionalidades oferecidas pela OO do PHP5 (PHP5-OO) é a extensão de classes, muito útil para acrescentar e modificar funcionalidades de classes sem mexer diretamente no código delas.
Diretamente relacionada com a extensão de classes são os conceitos de visibilidade e de herança, funcionalidades também essenciais para quem quer trabalhar com OO no PHP5.
Este artigo aborda os temas de extensão de classes, herança e visibilidade, procurando expor os seus conceitos e aplicações numa linguagem simples e fácil de entender, voltada principalmente para aqueles usuários que estão ingressando neste campo.
A metodologia utilizada será a de apresentar conceitos e a teoria básica sobre cada assunto, seguida de exemplos de utilização com comentários sobre cada etapa.
Inicialmente estudaremos a questão da visibilidade de propriedades e de métodos, utilizando também exemplos com comentários.
Depois abordaremos a extensão de classes com a adição de métodos e propriedades, explicando o que é e para que serve, seguida de exemplo de utilização.
Como sugestão, aconselha-se a leitura do artigo introdutório (e seus comentários) sobre PHP5-OO, publicado aqui no Código Fonte no link:
PHP Orientado a Objetos Para quem está começando
Uma importante ressalva:
Este artigo é voltado para iniciantes e visa apresentar conceitos e aplicações básicas, não sendo escopo principal a aplicação de qualquer metodologia ou prática de programação específica.
Bom estudo a todos.
Começando a entender visibilidade
A primeira parte de nosso estudo envolverá o conceito de visibilidade de métodos e propriedades.
Mas para fazer isso primeiro precisamos de uma classe. É o que faremos primeiro: uma classe de exemplo com a qual trabalharemos a questão de visibilidade.
titulo = $valor; } function setTexto($valor){ $this->texto = $valor; } function printTitulo(){ echo $this->titulo; } function printTexto(){ echo $this->texto; } } ?>
Explicando a classe:
A classe MensagemSimples apenas gera uma mensagem composta por título e texto.
As propriedades $titulo e $texto armazenam o título e o texto da mensagem respectivamente.
Os métodos setTitulo() e setTexto() atribuem valores às propriedades $titulo e $texto.
Os métodos printTitulo() e printTexto() imprimem o título e o texto na tela do navegador.
É uma classe simples, praticamente sem função real, exceto a função didática necessária ao aprendizado.
Vamos agora a uma pequena explanação teórica.
Visibilidade é o nível de acesso que se dá a propriedades e métodos de uma classe. Ela tem três níveis que são determinados pela prefixação das palavras reservadas public, private ou protected antes da definição da propriedade ou do método.
Assim, temos:
- public define que o acesso à propriedade/método é acessível de qualquer lugar do script;
- protected define que o acesso somente pode ser feito pela classe que define a propriedade/método e por classes que herdam esses métodos/propriedades (é o que veremos ao estudar extensão de classes);
- private define o nível mais alto de controle de acesso, onde a propriedade/método somente pode ser acessado pela classe que o define.
No contexto deste artigo, acesso à propriedade ou método significa que ele pode ser utilizado ou ter seu valor modificado ou consultado.
Também, neste artigo, considera-se classe que define o método/propriedade como aquela que possui o código do método/propriedade.
Finda esta explanação teórica, vamos começar a brincar com nossa classe MensagemSimples para aprender sobre a visibilidade public e protected. A visibilidade private será abordada quando estudarmos a extensão e herança.
Notemos que antes das propriedades $titulo e $texto temos a palavra public. Isto significa que são propriedades públicas e podem ser acessadas de qualquer lugar do script.
Vamos a um exemplo:
$msg = new MensagemSimples; // Criamos o objeto instanciando a classe. // Atribuindo valores às propriedades $msg->titulo = 'Título da mensagem'; $msg->texto = 'Mensagem de teste. Testando a visibilidade das propriedades.'; // Recuperando os valores das propriedades. echo $msg->titulo.''; echo $msg->texto;
Colocando o código acima no final do nosso script e executando ele no navegador, teremos o seguinte resultado:
Título da mensagem
Mensagem de teste.
Testando a visibilidade das propriedades.
Agora veremos o que acontece se trocarmos a palavra public por private na propriedade $texto.
O resultado exibido pelo navegador será o seguinte:
“Fatal error: Cannot access protected property MensagemSimples::$texto in E:wwwxampplitehtdocstutoriaistutorial.php-oo.visibilidade-heranca-estensao.php on line 33”
O que isso significa?
Significa que não é possível utilizar mais a propriedade $texto fora da classe MensagemSimples.
É justamente por isso que criamos os métodos setTitulo(), setTexto(), printTitulo() e printTexto().
Para utilizarmos nossa classe agora, teremos que substituir o código que não funcionou por este (troque antes o public da propriedade $titulo por protected):
// Atribuindo valores às propriedades $msg->setTitulo('Título da mensagem'); $msg->setTexto('Mensagem de teste. Testando a visibilidade das propriedades.'); // Recuperando os valores das propriedades. $msg->printTitulo(); echo ''; $msg->printTexto();
O resultado no navegador será o seguinte:
Título da mensagem
Mensagem de teste.
Testando a visibilidade das propriedades.
Viu como é fácil! Agora para utilizarmos as propriedades devemos fazer isso somente através da própria classe.
Se para as propriedades é possível selecionar níveis de visibilidade, para os métodos isso é possível?
Sim! É o que veremos agora.
Primeiros passos na visibilidade de métodos
Passaremos agora a estudar a visibilidade dos métodos, porém apenas os níveis public e protected. O nível private ficará para estudarmos junto com o tema extensão de classes e herança.
Tomemos novamente o código da nossa classe:
class MensagemSimples { protected $titulo; protected $texto; function setTitulo($valor){ $this->titulo = $valor; } function setTexto($valor){ $this->texto = $valor; } function printTitulo(){ echo $this->titulo; } function printTexto(){ echo $this->texto; } } $msg = new MensagemSimples; // Criamos o objeto instanciando a classe. // Atribuindo valores às propriedades $msg->setTitulo('Título da mensagem'); $msg->setTexto('Mensagem de teste. Testando a visibilidade das propriedades.'); // Recuperando os valores das propriedades. $msg->printTitulo(); echo ''; $msg->printTexto();
Vemos que as propriedades estão definidas como protected, porém os métodos não tem definição nenhuma. Isso significa que eles são public, pois quando não se determina nenhum nível de visibilidade, o nível atribuído é o public.
Vamos supor que queiramos que os métodos setTexto() e setTitulo() sejam protected, faríamos a seguinte alteração:
protected function setTitulo($valor){ $this->titulo = $valor; } protected function setTexto($valor){ $this->texto = $valor; }
Se tentarmos executar agora o nosso script, receberíamos a seguinte mensagem:
“Fatal error: Call to protected method MensagemSimples::setTitulo() from context ” in E:wwwxampplitehtdocstutoriaistutorial.php-oo.visibilidade-heranca-estensao.php on line 32″
Isto significa que não mais podemos acessar os métodos setTitulo() e setTexto() a partir de fora da classe.
Mas como vamos acessá-los então?
Para isso devemos fazer uma pequena modificação na nossa classe:
protected function setTitulo($valor){ $this->titulo = $valor; } protected function setTexto($valor){ $this->texto = $valor; } function printTitulo($valor){ $this->setTitulo($valor); echo $this->titulo; } function printTexto($valor){ $this->setTexto($valor); echo $this->texto; }
Note que adicionamos o parâmetro $valor aos métodos printTitulo() e printTexto() e que dentro dos métodos printTitulo() e printTexto() chamamos os métodos setTitulo() e setValor().
O teste do nosso script também deve mudar:
$msg = new MensagemSimples; // Criamos o objeto instanciando a classe. // Recuperando os valores das propriedades. $msg->printTitulo('Título da mensagem'); echo ''; $msg->printTexto('Mensagem de teste. Testando a visibilidade das propriedades.');
Isso nos exibirá no navegador, novamente:
Título da mensagem
Mensagem de teste.
Testando a visibilidade das propriedades.
O que fizemos é basicamente chamar os métodos print, passando-lhes os valores desejados e estes métodos chamam os métodos set passando-lhes os valores recebidos e depois imprimem os valores atribuídos pelos métodos set.
Alerto que isto é somente um exemplo didático e que na prática não faríamos desta forma. Isso é apenas para aprendermos a utilizar a visibilidade.
Estendendo classes
Estender uma classe significa criar uma classe (classe-filha) que irá modificar e (ou) ampliar as funcionalidades de outra classes (classe-pai).
Vamos ver isso na prática?
Primeiro vamos repetir o código da classe MensagemSimples (somente para lembrarmos dele), que será a nossa classe-pai.
class MensagemSimples { protected $titulo; protected $texto; protected function setTitulo($valor){ $this->titulo = $valor; } protected function setTexto($valor){ $this->texto = $valor; } function printTitulo($valor){ $this->setTitulo($valor); echo $this->titulo; } function printTexto($valor){ $this->setTexto($valor); echo $this->texto; } }
Agora, vamos criar a classe-filha:
class MensagemBonita extends MensagemSimples { }
Vejam que eu inicio com class MensagemBonita, que é o nome da classe-filha, porém não paro aí: eu acrescento a palavra reservada extends seguida do nome da classe-pai, que é MensagemSimples.
Isso significa que a classe MensagemBonita ESTENDE a classe MensagemSimples.
Agora vamos colocar algum código na classe-filha:
class MensagemBonita extends MensagemSimples { protected $cor; public function setCor($cor){ $this->cor = $cor; } public function imprime($titulo,$texto){ $mensagem = ''.$titulo.'
'.$texto.''; echo $mensagem; } }
No código da classe MensagemBonita temos a propriedade $cor, que armazena a cor da fonte do título da mensagem, a qual será atribuída através do método setCor().
O método imprime() recebe o valor a ser utilizado como título e o valor de texto e imprime na tela a mensagem estilizada com a cor.
Vamos testar isso?
$msg = new MensagemBonita; $msg->setCor('Red'); $msg->imprime('Título da mensagem','Texto da mensagem.');
No navegador deverá aparecer:
Título da mensagem (em vermelho e maior).
Texto da mensagem.
Vemos que a classe inicializada foi a nossa classe-filha, o resto do código você já conhece.
Caso você não tenha percebido, nós estendemos a classe MensagemSimples, porém não fizemos nada com ela ainda. Isso foi proposital, pois na parte seguinte deste artigo, vamos começar a brincar com herança e visibilidade, inclusive com private.
Conhecendo mais a extensão de classes
Agora que sabemos como estender classes, vamos brincar com essa funcionalidade para aprendermos mais sobre extensão, herança e visibilidade.
Sabemos que na nossa classe-pai (MensagemSimples), temos o método pública printTitulo(). O que aconteceria se definíssemos um método com o mesmo nome na classe-filha (MensagemBonita)?
Vejamos:
public function printTitulo($titulo,$texto){ $mensagem = ''.$titulo.'
'.$texto.''; echo $mensagem; }
Trocando o nome do método imprime() por printTitulo(), deveríamos executá-lo assim:
$msg->printTitulo('Título da mensagem','Texto da mensagem.');
Teríamos o resultado:
Título da mensagem (em vermelho e maior).
Texto da mensagem.
Ou seja, se declararmos um método na classe-filha com o mesmo nome de outro método da classe-pai, o método da classe-filha será sempre chamada em vez do método da classe-pai.
Para utilizarmos o método da classe-pai, devemos utilizar “parent::”.
Por exemplo, alterando o método imprime():
public function imprime($titulo,$texto){ parent::printTitulo('Título da classe-pai'); }
Teríamos o seguinte resultado impresso no navegador:
Título da classe-pai
Ou seja, o método imprime() chamou o método printTitulo() da classe-pai.
O mesmo raciocínio é válido para propriedades, ou seja, declarando na classe-filha uma propriedade com mesmo nome da classe-pai, ocorrerá a substituição da propriedade pai pela propriedade filha.
Agora vamos aprender um pouco sobre a visibilidade private.
Para isso vamos alterar o método imprime():
public function imprime($titulo,$texto){ $mensagem = ''.parent::printTitulo($titulo).'
'.parent::printTexto($texto).''; echo $mensagem; }
Também precisamos trocar os echo por return dos métodos printTitulo() e printTexto() da classe MensagemSimples.
Executando o nosso código, teremos no navegador:
Título da mensagem (em vermelho e maior).
Texto da mensagem.
O que aconteceu aqui foi que o método imprime() chamou (call) os métodos printTitulo() e printTexto() e apresentou o resultado na tela.
Mas o que aconteceria se colocássemos os métodos printTitulo() e printTexto como protected e private (lembre-se que eles são públicos)?
Primeiro como protected:
protected function printTitulo($valor){ $this->setTitulo($valor); return $this->titulo; } protected function printTexto($valor){ $this->setTexto($valor); return $this->texto; }
O resultado será:
Título da mensagem (em vermelho e maior).
Texto da mensagem.
Ou seja, tudo ocorreu como o previsto pois a visibilidade dos métodos é protected, ou seja, somente podem ser executados a partir da classe que define o método (MensagemSimples) ou das classes que herdam o método (MensagemBonita).
Mas ao alterarmos de protected para private:
private function printTitulo($valor){ $this->setTitulo($valor); return $this->titulo; } private function printTexto($valor){ $this->setTexto($valor); return $this->texto; }
Receberemos no navegador:
Fatal error: Call to private method MensagemSimples::printtitulo() from context ‘MensagemBonita’ in F:xampplitehtdocstutoriaistutorial.php-oo.visibilidade-heranca-estensao.php on line 41
Isto significa que os métodos private somente podem ser executados através da classe que os define (MensagemSimples), ou seja, não são herdados. O mesmo vale para as propriedades.
Resumindo:
Ao estendermos uma classe, a classe que a estende (classe-filha) herda as propriedades e métodos da classe que estende (classe-pai), ou seja, os métodos e propriedades da classe-pai passam a fazer parte da classe-filha, como se nela fossem definidos.
Exceção para as propriedades e métodos private, que não são herdados, podendo somente serem chamados (call) pela classe que os define, a classe-pai.
Resumo e conclusão
Resumindo o nosso longo artigo:
Os métodos e propriedades podem ter seu acesso restrito através das declarações public, protected e private. Isso chama-se visibilidade.
Quando a visibilidade é public, significa que o método/propriedade pode ser acessado de qualquer parte do script (através da classe que define o método/propriedade, através de classes que herdam eles ou de fora de qualquer classe).
Quando a visibilidade é protected, o método/propriedade somente pode ser acessado pela classe que os define ou pelas que os herdam.
Já, quando a visibilidade é private, o método/propriedade somente pode ser acessado pela classe que os define.
Quanto a extensão, ela é feita com a palavra reservada extends.
Além disso, convém lembrar que ao definirmos na classe filha método ou propriedade com nome igual a método ou propriedade definidos na classe pai, os da classe filha sobrescrevem os da classe pai. Para chamar os métodos/propriedades da classe pai, deve-se usar parent::nomeDoMedodoOu Propriedade.
Espero que este artigo tenha cumprido com seu objetivo que foi o de apresentar os principais conceitos e tópicos sobre visibilidade, herança e extensão de classes, de forma simples, didática e de fácil entendimento, voltado para iniciantes.