Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

PHP Orientado a Objetos para Iniciantes

by

Portuguese (Português) translation by Erick Patrick (you can also view the original English article)

Para muitos programadores PHP, orientação a objetos é um conceito amedrontador, cheio de sintaxes complicadas e pontos de paradas. Com o mesmo detalhamento do meu livro, Pro PHP and jQuery, neste artigo você aprenderá os conceitos por trás da programação orientada a objetos (POO), um estilo de codificação onde ações relacionadas são agrupadas em classes para ajudar na criação de códigos mais compactos e efetivos.

Compreendendo a Programação Orientada a Objetos

Programação orientada a objetos é um estilo de programação que permite os desenvolvedores agruparem tarefas semelhantes em classes. Isso ajuda a mantermo-nos dentro do princípio "don't repeat yourself" (DRY) (em português, não se repita), além de faciltar a manutenção do código.

"Programação orientada a objetos é um estilo de programação que permite os desenvolvedores agruparem tarefas semelhantes em classes."

Um dos maiors benefícios da programação DRY é que, se alguma informação é alterada em seu programa, geralmente, só uma mudança é necessária para atualizar o código. Um dos maiores problemas para os desenvolvedores é ter de manter códigos onde os dados são declarados e redeclarados, acabando num jogo de pique esconde, em busca de funcionalidades e dados duplicados pelo código.

POO é intimidadora para uma grande quantidade de programadores porque ela introduz uma nova sintaxe e, a primeira vista, parece ser bem mais complexa que a simples programação procedural. Entretanto, se analisar bem, POO é bem direta e, fundamentalmente, uma abordagem mais simples para programação.

Compreendendo Objetos e Classes

Antes de lidar diretamente com as partes mais interessante da POO, um entendimento básico das diferenças entre objetos e classes é necessário. Essa seção falará sobre a base das classes, suas diferentes capacidades e alguns de seus usos.

Reconhecendo as Diferenças Entre Objetos e Classes

Fotos por Instant Jefferson e John Wardell

Quando programadores de longa data falam sobre objetos e classes, esses termos parecem permutáveis entre si. Porém, não é bem assim.

Logo de cara, há uma grande confusão na POO: quando programadores de longa data falam sobre objetos e classes, esses termos parecem permutáveis entre si. Porém, não é bem assim, mmesmo que a diferença entre eles seja um pouco complicada de perceber, no início.

Uma classe, por exemplo, é como uma planta baixa para uma casa. Ela define a forma da casa no papel, com as relações entre as diferentes partes da casa, claramente definidas e planejadas, mesmo a casa ainda não existindo.

Um objeto, por outro lado, seria a casa de verdade, construída de acordo com a planta baixa. Os dados guardados no objeto são como a madeira, fios e concreto que compoem a casa: sem a ordem criada pela planta baixa, são só um monte de materiais. Entretanto, quando tudo é colocado seguindo uma ordem, eles viram uma casa organizada de verdade e útil.

As classes servem de estrutura para os dados e ações, e usam essa informação para construir objetos. Mais de um objeto pode ser construido de uma mesma classe, ao mesmo tempo, cada um independente dos outros. Continuando a nossa analogia de classes e objetos em relação a construção, é parecida com a maneira que um condomínio de casas pode ser construído, usando a mesma planta baixa: 150 casas diferentes, todas bastante parecidas umas às outras, porém, contem
famílias e decorações diferentes por dentro.

Estruturando Classes

A sintaxe para criar uma classe é bem direta: declare-a usando a palavra chave class, seguida do nome da classe e um par de chaves ({}):

<?php

class MyClass
{
  // As propriedades e métodos da Classe vem aqui
}

?>

Após criar a classe, ela pode ser instanciada e guardada em alguma variável usando a palavra chave new:

$obj = new MyClass;

Para vermos o conteúdo da classe, usamos var_dump():

var_dump($obj);

Experimente isso, colocando todo o código anterior em um único arquivo php, chamado test.php na sua pasta local de testes:

<?php

class MyClass
{
  // As propriedades e métodos da Classe vem aqui
}

$obj = new MyClass;

var_dump($obj);

?>

Carregue a página no seu navegador, usando o endereço http://localhost/test.php (ou o endereço indicado pelo seu servidor local) e o resultado a seguir deve aparecer:

object(MyClass)#1 (0) { }

De uma forma bem simplória, você acabou de criar seu primeiro código em POO.

Definindo as Propriedades da Classe

Para adicionar dados à classe, usamos as propriedades, que são variáveis específicas à classe. Elas funcionam de forma parecida às variáveis normais, exceto que elas estão ligadas ao objeto e só podem ser acessadas usando o objeto.

Para adicionar uma propriedade a MyClass, adicione o seguinte trecho de código ao seu script:

<?php

class MyClass
{
  public $prop1 = "Sou um propriedade de classe!";
}

$obj = new MyClass;

var_dump($obj);

?>

A palavra chave public determina a visibilidade da propriedade, a qual você aprenderá mais sobre, nas próximas seções. Depois disso, a propriedade é nomeada usando os padrões básicos de nomeação de variáves, e, então, um valor é atribuído (embora propriedades de classe não necessitem de valores iniciais).

Para ver o valor da propriedade e mostrá-la no navegador, referencie o objeto o qual será feita a leitura, bem como a propriedade a ser lida, dessa forma:

echo $obj->prop1;

Já que múltiplas instâncias de uma mesma classe podem existir, se um objeto em específico não for referenciado, o código não será capaz de determinar de qual objeto ler a propriedade. O uso da flecha (->) é um construto da POO no PHP que permite acessar as propriedades e métodos de um dado objeto.

Modifique o código do arquivo test.php para ler a propriedade ao invés de mostrar todo o conteúdo da classe, dessa forma:

<?php

class MyClass
{
  public $prop1 = "Sou um propriedade de classe!";
}

$obj = new MyClass;

echo $obj->prop1; // Mostra a saída/conteúdo da propriedade

?>

Atualizando a página no seu navegador, você obtem isso, agora:

Sou uma propriedade de classe!

Definindo Métodos de Classe

Métodos são funções específicas das classes. Ações particulares que os objetos serão capazes de executar são definidas dentro das classes na forma de métodos.

Por exemplo, para criar métodos que atribuam e retornem o valor de uma propriedade de classe chamada $prop1, adicione o código a seguir:

<?php

class MyClass
{
  public $prop1 = "Sou um propriedade de classe!";

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

$obj = new MyClass;

echo $obj->prop1;

?>

Nota — POO permite que os objetos referenciem-se usando $this. Quando estiver dentro de um método, use $this da mesma forma que você usaria o nome do objeto fora da classe.

Para usar os métodos, execute-os da mesma forma que faria com funções normais, mas, antes, referencie o objeto ao qual eles pertencem. Para ler o valor da propriedade $prop1 da classe MyClass, depois altera-lo e, por fim, le-lo novamente, faça as seguintes alterações no código:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

$obj = new MyClass;

echo $obj->getProperty(); // Lê o valor da propriedade

$obj->setProperty("Sou um novo valor da propriedade!"); // Atribui um novo valor

echo $obj->getProperty(); // Lê o valor novamente para mostrar a mudança

?>

Atualize a página no seu navegador e você verá o resultado a seguir:

Sou uma propriedade de classe!
Sou um novo valor da propriedade!

"O poder da POO mostra-se ao usar múltiplas instâncias da
mesma classe."

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

// Create two objects
$obj = new MyClass;
$obj2 = new MyClass;

// Mostra o valor de $prop1 de ambos os objetos
echo $obj->getProperty();
echo $obj2->getProperty();

// Atribui novos valores para ambos os objetos
$obj->setProperty("Sou um novo valor de propriedade!");
$obj2->setProperty("Pertenço à segunda instância!");

// Mostra o valor de $prop1 de ambos os objetos
echo $obj->getProperty();
echo $obj2->getProperty();

?>

Quando atualizar a página do seu navegador, verá o resultado a seguir:

Sou uma propriedade de classe!
Sou uma propriedade de classe!
Sou um novo valor de propriedade!
Pertenço à segunda instância!

Como pode ver, POO mantém os objetos como entidades diferentes, o que torna fácil a separação de diferentes partes de código em pedaços pequenos e relacionados.

Métodos Mágicos em POO

Para facilitar o uso dos objetos, o PHP provê uma série de métodos mágicos, métodos especiais chamados quando certas ações comuns ocorrem com objetos. Isso permite executar várias tarefas úteis com certa facilidade.

Usando Construtores e Destruidores

Quando um ojeto é instanciado, é desejável que algumas coisas ocorram de cara. Para lidar com isso, o PHP provê o método __construct(), que é chamado automaticamente quando um novo objeto é
criado.

Para ilustrar os conceitos dos construtores, adicione um construtor à classe MyClass que mostre uma mensagem todoa vez que uma nova instância for criada:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

// Cria um novo objeto
$obj = new MyClass;

// Mostra o valor de $prop1
echo $obj->getProperty();

// Mostra uma mensagem ao final do arquivo
echo "Fim do arquivo.<br />";

?>

Nota__CLASS__ retorna o nome da classe na qual foi usado; isso é o que chamaos de constante mágica. Há inúmeras constantes mágicas disponíveis e você pode ler mais sobre elas no manual do PHP.

Atualize a página no seu navegador e terá os resultados a seguir:

A classe "MyClass" foi instanciada!
Sou uma propriedade de classe!
Fim do arquivo.

Para chamar uma função quando um objeto for destruído, o método mágico __destruct() está disponível. Ele é útil para finalizar as tarefas da classe (encerrar uma conexão com a base de dados, por exemplo).

Mostre uma mensagem quando um objeto for destruído usando o método mágico
__destruct() na class MyClass:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

// Cria um novo objeto
$obj = new MyClass;

// Mostra o valor de $prop1
echo $obj->getProperty();

// Mostra uma mensagem ao final do arquivo
echo "Fim do arquivo.<br />";

?>

Com um método destruidor definido, atualize a página no seu navegador e verá o resultado a seguir:

A classe "MyClass" foi instanciada!
Sou uma propriedade de classe!
Fim do arquivo.
A classe "MyClass" foi destruída.

"Quando o fim do arquivo é alcançado, o PHP libera, automaticamente, todos os recursos."

Para executar, explicitamente, o método destruidor, você pode destruir o objeto usando a função
unset():

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

// Cria um novo objeto
$obj = new MyClass;

// Mostra o valor de $prop1
echo $obj->getProperty();

// Destrói o objeto
unset($obj);

// Mostra uma mensagem ao final do arquivo
echo "Fim do arquivo.<br />";

?>

Os resultados das mudanças aparecerão assim que você atualizar a página no seu navegador:

A classe "MyClass" foi instanciada!
Sou uma propriedade de classe!
A classe "MyClass" foi destruída.
Fim do arquivo.

Convertendo Em Uma String

Para evitar que aconteça um erro quando – e se – algum script tentar mostrar a classe MyClass como uma string, existe outro método mágico, chamado __toString().

Sem o método __toString(), tentar mostrar um objeto como uma string resulta em um erro fatal. Tente mostrar um objeto, usando echo, sem o método mágico:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

// Cria um novo objeto
$obj = new MyClass;

// Mostra o objeto como uma string
echo $obj;

// Destrói o objeto
unset($obj);

// Mostra uma mensagem ao final do arquivo
echo "Fim do arquivo.<br />";

?>

O resultado é esse:

A classe "MyClass" foi instanciada!

Catchable fatal error: Object of class MyClass could not be converted to string in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 40

Para evitar esse erro, adicione o método __toString() method:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

// Cria um novo objeto
$obj = new MyClass;

// Mostra o objeto como uma string
echo $obj;

// Destrói o objeto
unset($obj);

// Mostra uma mensagem ao final do arquivo
echo "Fim do arquivo.<br />";

?>

Nesse caso, tentar converter o objeto em string resulta numa chamada ao método getProperty(). Execute o código, recarregando a página no seu navegador, e veja o resultado:

A classe "MyClass" foi instanciada!
Usando o método toString: Sou uma propriedade de classe!
A classe "MyClass" foi destruída.
Fim do arquivo.

Dica — Além dos métodos mágicos discutidos nessa seção, inúmeros outros estão disponíveis. Para uma lista completa, com todos os métodos mágicos, veja a respectiva página do manual do PHP.

Usando Herança de Classe

Classes podem herdar métodos e propriedades de outra classe usando a palavra chave extends. Por exemplo, para criar uma segunda classe que estenda MyClass e adicione um outro método, você faria dessa forma:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }
}

// Cria um novo objeto
$newobj = new MyOtherClass;

// Usa o método da nova classe
echo $newobj->newMethod();

// Usa um método da classe pai
echo $newobj->getProperty();

?>

Após atualizar a página no seu navegador, você terá o seguinte resultado:

A classe "MyClass" foi instanciada!
De um novo método na classe MyOtherClass.
Sou uma propriedade de classe!
A classe "MyClass" foi destruída.

Sobrescrevendo Métodos e Propriedades Herdadas

Para alterar uma propriedade ou o comportamento de um método existente na nova classe, você pode, simplesmente, sobrescreve-los, bastando redeclará-los na nova classe:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function __construct()
  {
      echo "Um novo construtor em " . __CLASS__ . ".<br />";
  }

  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }
}

// Cria um novo objeto
$newobj = new MyOtherClass;

// Usa o método da nova classe
echo $newobj->newMethod();

// Usa um método da classe pai
echo $newobj->getProperty();

?>

As mudanças no resultado serão:

Um novo construtor em MyOtherClass.
De um novo método na classe MyOtherClass.
Sou uma propriedade de classe!
A classe "MyClass" foi destruída.

Preservando Funcionalidades Originais de um Método Enquanto Sobrescreve o Mesmo

Para adicionar uma nova funcionalidade a um método e, ao mesmo tempo, manter a funcionalidade do método original intacta, use a palavra chave parent juntamente ao operador de resolução de escopo (::):

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  public function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function __construct()
  {
      parent::__construct(); // Invoca o construtor da classe pai
      echo "Um novo construtor em " . __CLASS__ . ".<br />";
  }

  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }
}

// Cria um novo objeto
$newobj = new MyOtherClass;

// Usa o método da nova classe
echo $newobj->newMethod();

// Usa um método da classe pai
echo $newobj->getProperty();

?>

Dessa forma, será retornado tanto o que foi codificado no construtor pai quando o do construtor da nova classe:

A classe "MyClass" foi instanciada!
Um novo construtor em MyOtherClass.
De um novo método na classe MyOtherClass.
Sou uma propriedade de classe!
A classe "MyClass" foi destruída.

Atribuindo Visbilidade a Propriedades e Métodos

Para controle adicional sobre objetos, métodos e propriedades, atribuímos visibilidades a eles. Essa visibilidade controla como e de onde as propriedades podem ser acessadas. Há três palavras chaves para visibilidade: public, protected, e private. Em adição à sua visibilidade, um método ou propriedade pode ser declarado como static, o que permite que sejam acessados sem uma instanciação da classe.

"Para controle adicional sobre objetos, métodos e propriedades, atribuímos visibilidades a eles."

Nota — Visibilidade é um novo recurso presente a partir do PHP 5. Para maiores informações sobre a compatibilidade da POO com o PHP 4, veja a respectiva página do manual do PHP.

Métodos e Propriedades Públicas

Todos os métodos e propriedades que usamos, até agora, eram públicos. Isso significa que eles podem ser acessados de qualquer lugar, tanto dentro quanto fora da classe.

Métodos e Propriedades Protegidas

Quando uma propriedades ou método é declarada com protected, ela só pode ser acessada dentro dela própria ou por uma classe descendente (classes que estendem a classe que contem o método protegido).

Declare o método getProperty() como protegido, na classe MyClass, e tente acessá-lo diretamente fora da classe:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  protected function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function __construct()
  {
      parent::__construct();
      echo "Um novo construtor em " . __CLASS__ . ".<br />";
  }

  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }
}

// Cria um novo objeto
$newobj = new MyOtherClass;

// Tentativa de invocar um método protegido fora da classe
echo $newobj->getProperty();

?>

Quando tentar executar esse código, o erro a seguir aparece:

A classe "MyClass" foi instanciada!
Um novo construtor em MyOtherClass.

Fatal error: Call to protected method MyClass::getProperty() from context '' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55

Agora, crie um método em MyOtherClass que invoque o método getProperty():

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  protected function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function __construct()
  {
      parent::__construct();
      echo "Um novo construtor em " . __CLASS__ . ".<br />";
  }

  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }

  public function callProtected()
  {
      return $this->getProperty();
  }
}

// Cria um novo objeto
$newobj = new MyOtherClass;

// Invoca o método protegido a partir de um método público
echo $newobj->callProtected();

?>

E, assim, temos o resultado desejado:

A classe "MyClass" foi instanciada!
Um novo construtor em MyOtherClass.
Sou uma propriedade de classe!
A classe "MyClass" foi destruída.

Métodos e Propriedades Privadas

Uma propriedade ou método declarado com private é acessível somente dentro da classe que os define. Isso significa que mesmo se uma classe estender a classe que define uma propriedade privada, aquela propriedade (ou método) não será acessível na classe filha.

Para demonstrar isso, declare getProperty() como privada dentro da classe MyClass, e tente invocar callProtected() da classe
MyOtherClass:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  private function getProperty()
  {
      return $this->prop1 . "<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function __construct()
  {
      parent::__construct();
      echo "Um novo construtor em " . __CLASS__ . ".<br />";
  }

  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }

  public function callProtected()
  {
      return $this->getProperty();
  }
}

// Cria um novo objeto
$newobj = new MyOtherClass;

// Usa um método da classe pai
echo $newobj->callProtected();

?>

Atualize a página no seu navegador e verá o erro a seguir aparecer:

A classe "MyClass" foi instanciada!
Um novo construtor em MyOtherClass.

Fatal error: Call to private method MyClass::getProperty() from context 'MyOtherClass' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49

Métodos e Propriedades Estáticas

Um método ou propriedade declarada com static podem ser acessados sem, primeiro, instanciar a classe; você pode, simplesmente, usar o nome da classe, o operador de resolução de escopo e o nome da propriedade ou do método.

"Um dos maiores benefícios de se usar propriedades estáticas é que elas são capazes de manter os valores guardados dentro dela enquanto o arquivo for executado."

Para demonstrar isso, adicione uma propriedade estática chamada $count e um método estático chamado plusOne() à classe MyClass. Depois, prepare um laço de repetição do...whilepara mostrar o valor incrementado da proprieade $count enquanto o valor dela for menor que 10:

<?php

class MyClass
{
  public $prop1 = "Sou uma propriedade de classe!";

  public static $count = 0;

  public function __construct()
  {
      echo 'A classe "', __CLASS__, '" foi instanciada!<br />';
  }

  public function __destruct()
  {
      echo 'A classe "', __CLASS__, '" foi destruída.<br />';
  }

  public function __toString()
  {
      echo "Usando o método toString: ";
      return $this->getProperty();
  }

  public function setProperty($newval)
  {
      $this->prop1 = $newval;
  }

  private function getProperty()
  {
      return $this->prop1 . "<br />";
  }

  public static function plusOne()
  {
      return "O valor é " . ++self::$count . ".<br />";
  }
}

class MyOtherClass extends MyClass
{
  public function __construct()
  {
      parent::__construct();
      echo "Um novo construtor em " . __CLASS__ . ".<br />";
  }

  public function newMethod()
  {
      echo "De um novo método na classe " . __CLASS__ . ".<br />";
  }

  public function callProtected()
  {
      return $this->getProperty();
  }
}

do
{
  // Invoca o método plusOne sem instanciar a classe MyClass
  echo MyClass::plusOne();
} while ( MyClass::$count < 10 );

?>

Nota — Quando acessar propriedades estáticas, o símbolo de dólar
($) vem depois do operador de resolução de escopo.

Quando atualizar a página do seu navegador, o resultado será:

O valor é 1.
O valor é 2.
O valor é 3.
O valor é 4.
O valor é 5.
O valor é 6.
O valor é 7.
O valor é 8.
O valor é 9.
O valor é 10.

Comentários com DocBlocks

"O estilo de comentários DocBlock é um
meio bem aceito para documentar classes."

Embora não seja uma parte oficial da POO, os comentários DocBlock são um meio bem aceito para documentar classes. Além de prover um padrão para os
desenvolvedores ao escreverem seus códigos, ele também foi adotado pela maioria dos kits de desenvolvimento de software (do inglês, software development kits - SDKs) populares, como o Eclipse e NetBeans, e serão usados para gerar dicas/indicações de códigos.

Um DocBlock é definido como um bloco de comentário que começa com um asterisco adicional:

/**
 * Esse é um DocBlock bem básico
 */

O verdadeiro poder do DocBlocks vem com a possibilidade de usarmos as tags, que começam com o símbolo de localização (@) e são, imediatamente, seguidos dos nome e valor da tag. As tags do DocBlock permitem aos desenvolvedores definirem os autores de um arquivo, a licença que a classe usa, informações das propriedades e métodos e outras informações úteis.

As tags mais usadas são:

  • @author: Os autores do elemento atual (o qual pode ser uma classe, arquivo, método ou qualquer trecho de código) são listados usando essa tag. Múltiplas tags para autores podem ser usadas no mesmo DocBlock se precisar creditar mais de um autor. O formato para o nome do autor é Erick Patrick <erick.patrick@email.com>.
  • @copyright: Isso indica o ano e o nome de quem tem os direitos sobre o elemento atual. O formato é 2010 Nome da pessoa.
  • @license: Indicamos a licença usada no elemento atual. O formato para a informação da licença é
    http://www.example.com/path/to/license.txt Nome da licença.
  • @var: Isso indica o tipo e a descrição de uma variável ou de uma propriedade de classse. O formato é tipo do elemento descrição.
  • @param: Essa tag mostra o tipo e descrição de um parâmetro de uma função ou método. O formato é tipo $nome_do_elemento descrição do elemento.
  • @return: O tipo e descrição do valor retornado por uma função ou método. O formato é tipo descrição do valor retornado.

Uma classe exemplo, comentada com DocBlocks, parece-se com isso:

<?php

/**
 * A classe simples
 *
 * Essa é uma descrição longa para essa classe,
 * que pode tomar quantas linhas forem precisas. Não
 * é necessária, porém a versão curta é necessária.
 *
 * Essa descrição também pode tomar vários parágrafos, se a
 * descrição precisar ser tão verbosa.
 *
 * @author Erick Patrick <tutsplus@erickpatrick.net>
 * @copyright 2014 Erick Patrick
 * @license http://www.php.net/license/3_01.txt PHP License 3.01
 */
class SimpleClass
{
  /**
   * Uma variável pública
   *
   * @var string guarda dados para a classe
   */
  public $foo;

  /**
   * Atribui um novo valor a $foo durante a instanciação da classe
   *
   * @param string $val é o valor requerido pela classe
   * @return void
   */
  public function __construct($val)
  {
      $this->foo = $val;
  }

  /**
   * Multiplica dois inteiros
   *
   * Aceita um par de números inteiros e retorna
   * o produto dos dois.
   *
   * @param int $bat um número a ser multiplicado
   * @param int $baz um número a ser multiplicado
   * @return int o produto dos dois parâmetros
   */
  public function bar($bat, $baz)
  {
      return $bat * $baz;
  }
}

?>

Uma vez que você dá uma rápida olha na classe acima, os benefícios do DocBlock tornam-se visíveis: tudo está claramente definido, de forma que o próximo desenvolvedor possa pegar esse código e nunca precisar imagina o que uma certa parte de código faz ou deveria receber.

Comparando Código Orientado a Objetos e Código Procedural

Não há maneira certo ou errada de programar. Isso dito, essa seção mostrar bons argumentos para adotar a abordagem orientada a objetos no desenvolvimento de software, especialmente em aplicações de grande porte.

Razão 1: Facilidade de Implementação

"Embora, inicialmente, pareça complicada, POO provê uma abordagem mais fácil para lidar com dados."

Embora, inicialmente, pareça complicada, POO provê uma abordagem mais fácil para lidar com dados. Uma vez que objetos podem guardar dados internamente, variáveis não precisam ser passadas para funções para que elas funcionem corretamente.

E como várias instâncias de uma mesma classe podem existir simultanemanete, lidar com conjuntos de dados grandes se torna infinitamente mais fácil. Por exemplo, imagine que há informações de duas pessoas sendo processadas ao mesmo tempo em um arquivo. Elas precisam de nomes, ocupações e idades.

A Abordagem Procedural

Eis a abordagem procedural para nosso exemplo:

<?php

function changeJob($person, $newjob)
{
  $person['job'] = $newjob; // Muda o emprego da pessoa
  return $person;
}

function happyBirthday($person)
{
  ++$person['age']; // Adiciona 1 à idade da pessoa
  return $person;
}

$person1 = array(
  'name' => 'Tom',
  'job' => 'Button-Pusher',
  'age' => 34
);

$person2 = array(
  'name' => 'John',
  'job' => 'Lever-Puller',
  'age' => 41
);

// Mostra os valores iniciais das pessoas
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";

// Tom foi promovido e fez aniversário
$person1 = changeJob($person1, 'Box-Mover');
$person1 = happyBirthday($person1);

// John fez aniversário também
$person2 = happyBirthday($person2);

// Mostra os novos valores das pessoas
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";

?>

Quando executado, o código mostrará o seguinte:

Person 1: Array
(
  [name] => Tom
  [job] => Button-Pusher
  [age] => 34
)
Person 2: Array
(
  [name] => John
  [job] => Lever-Puller
  [age] => 41
)
Person 1: Array
(
  [name] => Tom
  [job] => Box-Mover
  [age] => 35
)
Person 2: Array
(
  [name] => John
  [job] => Lever-Puller
  [age] => 42
)

Apesar disso não estar tão ruim, tem muita coisa a ser lembrada de uma só vez. O conjunto de atributos da pessoa que foi afetado tem de ser passado e retornado de cada invocação de função, e isso possibilita o aparecimento de erros.

Para melhorar esse exemplo, seria desejável deixar o mínimo para o programador quanto possível. Só a informação extremamente essencial para a operação atual é necessária ser passada para as funções.

É aqui que a POO entra e ajuda você a ajustar as coisas.

A Abordagem POO

Eis a abordagem POO para nosso exemplo:

<?php

class Person
{
  private $_name;
  private $_job;
  private $_age;

  public function __construct($name, $job, $age)
  {
      $this->_name = $name;
      $this->_job = $job;
      $this->_age = $age;
  }

  public function changeJob($newjob)
  {
      $this->_job = $newjob;
  }

  public function happyBirthday()
  {
      ++$this->_age;
  }
}

// Cria duas novas pessoas
$person1 = new Person("Tom", "Button-Pusher", 34);
$person2 = new Person("John", "Lever Puller", 41);

// Mostra seus valores iniciais
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";

// Tom foi promovido e fez aniversário
$person1->changeJob("Box-Mover");
$person1->happyBirthday();

// John também fez aniversário
$person2->happyBirthday();

// Mostra os valores finais
echo "<pre>Person 1: ", print_r($person1, TRUE), "</pre>";
echo "<pre>Person 2: ", print_r($person2, TRUE), "</pre>";

?>

O seguinte seria mostrado no navegador:

Person 1: Person Object
(
  [_name:private] => Tom
  [_job:private] => Button-Pusher
  [_age:private] => 34
)

Person 2: Person Object
(
  [_name:private] => John
  [_job:private] => Lever Puller
  [_age:private] => 41
)

Person 1: Person Object
(
  [_name:private] => Tom
  [_job:private] => Box-Mover
  [_age:private] => 35
)

Person 2: Person Object
(
  [_name:private] => John
  [_job:private] => Lever Puller
  [_age:private] => 42
)

É necessário um pouco mais de preparação na abordagem orientada a objetos, mas, uma vez definida a classe, criar e modificar pessoas é fácil; a informação de uma pessoa não precisa ser passada ou retornada para os métodos e só a informação essencial é passada para cada método.

"POO reduzirá significantemente a carga de trabalho se implementada corretamente."

A primeira vista, a diferença pode não parecer tanta, mas, de acordo com que sua aplicação cresce, POO reduzirá significanetemente a carga de trabalho se implementada corretamente.

DicaNem tudo precisa ser implementado em orientação a objetos. Uma função rápida, que lida com algo pequeno em um único lugar dentro da aplicação, não precisa, necessariamente, estar envolta em uma classe. Use seu melhor julgamento quando precisar decidir entre as abordagens orientada a objetos ou procedural.

Razão 2: Melhor Organização

Outro benefício da POO é a facilidade de empacotamento e catalogação. Cada classe, geralmente, é mantida sozinha em seu próprio arquivo e, se uma convenção de nomenclatura for usada, acessar as classes será extremamente fácil.

Assuma que você tem uma aplicação com 150 classes que são invocadas dinamicamente através de um arquivo controlador na pasta raiz da sua aplicação. Todas as 150 classes foram nomeadas como class._nome_da_classe_.inc.php e ficar dentro de um diretório chamado inc da sua aplicação.

O controlador implementa a função __autoload() do PHP para carregar dinamicamente as classes que ele precisa executar, ao invés de incluir todas as 150 classes dentro do controlador ou, até mesmo, usar uma maneira "mais esperta" de incluir esses arquivos no seu código:

<?php
  function __autoload($class_name)
  {
      include_once 'inc/class.' . $class_name . '.inc.php';
  }
?>

Ao termos cada classe separada em seu próprio arquivo, tornamo-as mais portáteis e fáceis de reusar em novas aplicações, sem precisar copiar e colar para todos os lados.

Razão 3: Manutenção Mais Fácil

Devido a natureza compacta do PHP quando programado corretamente, mudanças no código, geralmente, são mais fáceis de perceber e alterar que em uma implementação procedural gigantesca e spaghetti.

Se um conjunto particular de informação ganha um novo atributo, códigos procedurais podem requerer (no pior dos casos) que o novo atributo seja adicionado a cada funcção que usa tal conjunto.

Uma aplicação POO poderia, potencialmente, ser atualizada, bastando adicionar uma nova propriedade e os métodos relacionados que lidam com tal propriedade a uma classe.

Vários benefícios cobertos nessa seção são produto da combinação de POO com práticas de programação DRY. Claro, é possível criar código procedural que seja fácil de manter e que não cause dores-de-cabeça, porém, é igualmente fácil criar código horroroso usando orientação a objetos. [Pro PHP and jQuery] tentará mostrar a combinação de boas práticas de programação com POO para gerar códigos claros e fáceis de ler e manter.

Resumo

A essa altura, espero que você se sinta confortável com o estilo de programação orientado a objetos. Aprender POO é uma ótima maneira de elevar o seu nível de programação. Quando implementada corretamente, POO gera código fácil de ler, manter e portátil, que salvará você (e os desenvolvedores que trabalham com você) horas de trabalho extra. Você está encucado com algo que não foi apresentado nesse artigo? Você já usa POO e tem algumas dicas para os iniciantes? Compartilhe nos comentários!

Nota do Autor — Esse tutorial é um extrato do livro Pro PHP and jQuery (Apress, 2010).

Advertisement