PHP5 Orientado a Objetos: Visibilidade, herança e extensões de classes

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.

<?
/* 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.'';
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 = '<h1 style="color: '.$this->cor.';">'.$titulo.'</h1>'.$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 = '<h1 style="color: '.$this->cor.';">'.$titulo.'</h1>'.$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 = '<h1 style="color: '.$this->cor.';">'.parent::printTitulo($titulo).'</h1>'.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.

Sobre Everton da Rosa

Contador e Servidor Público Municipal que tem como hobby brincar com PHP.

Nós queremos saber sua opinião aqui