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:
[url=http://codigofonte.uol.com.br/artigo/php/php-orientado-a-objetos-para-quem-esta-comecando]PHP Orientado a Objetos Para quem está começando</url>
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.
<?
/* Script tutorial sobre extensão, herança e visibilidade (PHP Orientado a Objetos).
* @author Everton3x (Everton da Rosa)
* @version 1.0
*/
class MensagemSimples {
public $titulo;
public $texto;
function setTitulo($valor){
$this->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.'<br>';
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:\www\xampplite\htdocs\tutoriais\tutorial.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.<br>Testando a visibilidade das propriedades.');
// Recuperando os valores das propriedades.
$msg->printTitulo();
echo '<br>';
$msg->printTexto();
</code>
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.
<b>Primeiros passos na visibilidade de métodos</b>
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:
<code>
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.<br>Testando a visibilidade das propriedades.');
// Recuperando os valores das propriedades.
$msg->printTitulo();
echo '<br>';
$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:\www\xampplite\htdocs\tutoriais\tutorial.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 '<br>';
$msg->printTexto('Mensagem de teste.<br>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 classesEstender 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 = '<h1 style="color: '.$this->cor.'">'.$titulo.'</h1><p>'.$texto.'</p>';
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 = '<h1 style="color: '.$this->cor.'">'.$titulo.'</h1><p>'.$texto.'</p>';
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-paiOu 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 = '<h1 style="color: '.$this->cor.'">'.parent::printTitulo($titulo).'</h1><p>'.parent::printTexto($texto).'</p>';
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:\xampplite\htdocs\tutoriais\tutorial.php-oo.visibilidade-heranca-estensao.php on line 41Isto 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.