A orientação a objetos (OO) é um padrão de programação em que um software não é composto por um grande bloco de códigos específicos, e sim de vários blocos de códigos distantes e independentes, que juntos montam um sistema. O PHP faz parte de um grupo de linguagens que possuem suporte a OO, mas não é preso a ela.

Esse padrão não possui um objetivo único e tem como objetivo:

  • Reutilização de código (tempo economizado);
  • Escalabilidade (código adicionado mais facilmente);
  • Manutenibilidade (manutenção mais fácil);
  • Desenvolvimento mais rápido.

A POO possui alguns conceitos fundamentais para seu desenvolvimento:

  • Abstração: são tipos abstratos de dados;
  • Objetos: um objeto é uma entidade do mundo real, concreta ou abstrata, que seja aplicável ao sistema;
  • Classes: uma classe é a representação de vários objetos que possuem as mesmas características e comportamentos;
  • Atributos / Propriedades: são as características de um determinado objeto.

Classes e Objetos

Algo que confunde bastante novos estudantes de OO é a diferença entre Classes e Objetos.

As classes definem as características e o comportamento dos seus objetos. Cada característica é representada por um atributo e cada comportamento é definido por um método.

Então uma classe não é um objeto e sim uma abstração de sua estrutura, no qual podemos definir quantos objetos desejamos ter.

Para podermos entender melhor o funcionamento, vamos criar a nossa primeira classe e alguns objetos a partir dela, conforme a Listagem 1.


  <?php
    Class Conta {
    }
    $conta  = new Conta();
    $conta2 = new Conta();
    $conta3 = new Conta();
  ?>
Listagem 1. Nossa primeira Classe

Criamos uma classe vazia de uma conta junto com três objetos que são dessa conta criada. Podemos dizer então que os três objetos são do tipo conta. E é dessa maneira que funciona todo objeto, seu tipo será sempre a classe a partir do qual ele é criado.

Atributos e Métodos

Uma classe é composta por atributos e métodos, que juntos dão funcionalidade a um objeto. Podemos ver na Listagem 2 uma classe composta por dois atributos e três métodos.


  <?php
   Class Conta {
     public $saldo = 500;
     public $titular;
   
     function sacar($valor){
   
     }
   
     function depositar($valor){
   
     }
   
     function verSaldo(){
   
     }
   }
                          
    $conta1 = new Conta();
    $conta1 ->depositar(500);
    $conta1->sacar(20);
   
    $conta2 = new Conta();
    $conta2->depositar(250);
    $conta2->verSaldo();
  ?>
Listagem 2. Atributos e Métodos

A classe Conta tem como atributos o saldo da conta e o titular. E como métodos possui depositar(), sacar() e verSaldo().

Para acessarmos o método depositar() do nosso objeto $conta1 precisamos utilizar uma seta(->). Seu nome é Operador de Acesso a Objetos e é através dessa seta que indicamos que estamos acessando um atributo ou método de tal objeto.

Faremos agora uma codificação mais completa em cima de nossa classe Conta, como mostra a Listagem 3.


  <?php
    Class Conta{
      public $saldo = 0;
      public $titular;
 
      function depositar($valor){
        $this->saldo += $valor;
      }
   
      function sacar($valor){
        if(($this->saldo > 0) && ($this->saldo >= $valor))
        {
          $this->saldo -= $valor;
        }else{
          echo "Saldo insuficiente";    
        }
      }
      
      function verSaldo(){
        echo "Saldo Atual:".$this->saldo. "<br>";
      }
    }
   
    $novaConta = new Conta();
    $novaConta->verSaldo();
    $novaConta->depositar(500);
    $novaConta->verSaldo();
    $novaConta->sacar(150);
    $novaConta->verSaldo();
  ?>
Listagem 3. Classe conta completa

Note que para acessarmos nossos atributos dentro dos métodos utilizamos a variável reservada $this. Se fosse preciso chamar algum método da própria classe dentro de um outro método teríamos de usar $this, que é usado para acessar atributos e métodos da própria classe e só existe dentro deste escopo. No PHP ele é fundamental e obrigatório, já em algumas outras linguagens de programação $this é opcional.

Herança

Um dos conceitos fundamentais da OO é a herança de classes, pois permite que uma classe estenda outra e a classe filha vai herdar todos os atributos e métodos da classe pai. Ela pode então tanto possuir novos métodos e atributos, quanto reescrever métodos já existentes, como mostra a Listagem 4.


  <?php
    class Conta{
      public $saldo = 0;
                          
      function depositar($valor) {
                                     
      }
                                     
      function sacar() {
                                     
      }
   }
   
      class ContaCorrente extends Conta {
        function transferir($contaDestino, $valor){
          $this->saldo -= $valor;
        }
      }         
     
   
    $novaConta = new ContaCorrente();
    $novaConta->transferir('xxx-xxx', 500);
    echo "Saldo:".$novaConta->saldo;                
  ?>
Listagem 4. Herança

Adicionamos um novo método à classe ContaCorrente, onde nela há uma referência ao atributo saldo da própria classe ContaCorrente que acessamos usando $this. E será que esse atributo existe mesmo? Sim, graças a herança, a classe ContaCorrente herda da classe Conta todos os métodos e atributos ali definidos. Sendo assim, o atributo saldo existe tanto em Conta quanto em ContaCorrente e em outras contas que nós venhamos a estender a partir da nossa classe pai Conta. Para estendermos uma classe basta utilizar o termo extends e seguir a sintaxe apresentada em nosso exemplo.

É importante sabermos que o PHP não possui herança múltipla, ou seja, não é possível que uma classe filha estenda de dois pais diferentes, ela só pode possuir uma única classe pai.

Classes abstratas

Uma classe abstrata é uma classe que não pode ser instanciada como um objeto diretamente. Ela tem que ser estendida por alguma classe concreta, e quando um objeto desta classe for criado, ele herdará métodos e atributos da classe abstrata. Veja na Listagem 5.


  <?php
    abstract class Conta{
      public $saldo =0;
      public function sacar(){
      }
                                     
      public function depositar($valor){
      }
   
      class ContaPoupanca extends Conta{
        public function resgatar($valor) {
        }
      }
    }
   
    $conta1 = new ContaPoupanca();
    $conta1->depositar(500);
    $conta1->resgatar(250); 
  ?>
Listagem 5. Classe Abstrata

Note que a classe estendida faz uso dos métodos declarados na classe abstrata, ou seja, em classes abstratas e concretas o conceito de herança é o mesmo.

Mas, para que serve uma classe abstrata? Vamos pensar no funcionamento de um banco, onde os clientes podem ter uma conta corrente e poupança: o funcionamento de uma conta segue um determinado padrão, o que difere uma da outra são as ações (métodos) que podemos executar.

Métodos abstratos

Podemos ter também métodos abstratos em nossas classes, como mostra a Listagem 6.


  <?php
    abstract class Conta {

      public $saldo = 0;

      abstract function sacar();
      abstract function depositar($valor);
    }
   
    class ContaPoupanca extends Conta {
      
      public function sacar() {
      }

      public function depositar($valor) {
      }

      public function resgatar($valor){
      }
    }
   
    $conta1 = new ContaPoupanca();
    $conta1->depositar(500);
    $conta1->resgatar(250);
  ?>
Listagem 6. Métodos Abstratos

Todo método abstrato precisa, obrigatoriamente, ser implementado na classe filha, ou seja, todas as contas, independentemente do tipo devem possuir as operações básicas de saque, depósito, transferência e consulta. Porém, contas de tipos diferentes podem tratar estas ações de formas diferentes. Por exemplo: um depósito em uma conta poupança gera um certo rendimento ao saldo aplicado - para este caso um método abstrato é uma forma de garantir que este método seja implementado na classe ContaPoupança e em todas as outras classes que estende–lás.

Classes finais

Uma classe final é uma classe que não pode ser estendida por nenhuma outra classe, ou seja, a classe final não tem herdeiros, pois ela é a última de sua hierarquia. Em nosso exemplo temos uma conta do tipo poupança que, pela regra de negócio de um banco, não possui uma derivação, ou seja, não deve ser estendida. Para estes casos definimos a classe como final, ou seja, somente existirão objetos da classe poupança e não filhos da mesma, pois o correto é que todas as contas estendam a nossa classe pai Conta e mais nenhuma outra, como mostra a Listagem 7.


  <?php
    final class ContaPoupanca {
     public function resgatar($valor){
   
     }
      public function verSaldo(){
   
      } 
    }
   
    $poupanca = new ContaPoupanca();
    $poupanca->resgatar(250);
  ?>
Listagem 7. Classes Finais

Métodos Finais

Também podemos ter métodos finais que jamais podem ser reescritos nas classes filhas. Em nosso exemplo de agência bancária, podemos concluir que o método sacar de uma Conta é padrão para todas as Contas, independentemente de seu tipo. Quando temos uma situação como esta podemos definir estes métodos como final, impedindo assim que eles sejam reescritos e saiam do padrão estabelecido na classe pai, como mostra a Listagem 8.


  <?php
    class Conta {
     public function depositar($valor){
   
      }
     final public function sacar($valor){ #método final, não pode ser reescrito
   
      }
  }
   
  class ContaCorrente extends Conta{
    public function depositar(){
   
     }
  } 
  ?>
Listagem 8. Métodos Finais

Traits

Traits, a partir do PHP 5.4, nos proporcionam uma maneira simples e objetiva de reaproveitamento de código, pois são como classes onde usamos a palavra reservada trait, então escrevemos os métodos que queremos. E para usarmos um trait em uma classe usamos a palavra USE, como mostra a Listagem 9.


<?php
  class Conta {
    public $saldo = 0;
    public function getSaldo() {
      echo "Saldo Atual: {$this->saldo}"; 
    }
  }
  trait Acoes {
    public function getSaldo(){
      echo "Saldo Disponivel: {$this->saldo}";
   
    }
    public function depositar($valor){
      $this->saldo += $valor;
    }         
    public function sacar($valor){
      if($this->saldo >= $valor){
        $this->saldo -= $valor;           
      }
    } 
  }
  class ContaCorrente extends Conta{
    use Acoes;
  } 
   
  $o = new ContaCorrente();
  $o->depositar(500);
  $o->sacar(200);
  $o->getSaldo();
              
  // Saldo Disponivel: 300
?>
Listagem 9. Uso de Traits

Note que o método getSaldo() foi reescrito dentro do Trait, ou seja, sobrescreverá os métodos da classe base (pai).

Podemos ainda usar múltiplos traits em nossas classes, como na Listagem 10.


<?php
  class Conta {
    public $saldo = 0;
    public function getSaldo(){
      echo "Saldo Atual: {$this->saldo}";
    }
  } 
   
  trait Acoes {
    public function depositar($valor){
      $this->saldo += $valor;
    }
    public function sacar($valor){
      if($this->saldo >= $valor){
        $this->saldo -= $valor;
      }
    }
  }
   
  trait consultaExtrato{
    public function getSaldo(){
      echo "Saldo Disponivel para saque:{$this->saldo}<br>";
    }
    public function gerarExtrato($periodo){
      echo "Gerando extrato período $periodo aguarde...";
    }
  } 
   
  class ContaCorrente extends Conta{
    use Acoes, consultaExtrato;
  } 
   
  $o = new ContaCorrente();
  $o->depositar(500);
  $o->sacar(200);
  $o->getSaldo();
  $o->gerarExtrato('20/01/2013');
  ?>
Listagem 10. Uso de Múltiplos Traits

Desta vez temos dois traits com nomes diferentes, e note que sobrescrevemos o método getSaldo() novamente no trait consultaExtrato.

PHP Data Objects (PDO)

O PDO veio para solucionar a migração de um banco de dados para outro, do MySQL para o PostgreSQL, por exemplo. O PDO é uma camada de abstração de acesso a dados, onde os métodos de manipulação de dados são independentes do SGDB que você está utilizando, ou seja, podemos usar as mesmas funções para executar queries e consultar dados.

O PDO veio no PHP 5.1 e dá suporte a vários sistemas gerenciadores de banco de dados, como MySQL, PostgreSQL, SQlite, Informix, Oracle, SQL Server, IBM e etc.

A conexão com um banco de dados através do PDO se dá durante a criação de um objeto da classe PDO, passando informações de conexão com o banco na forma de um DSN (Data Source Name), além das credencias de acesso, como mostra a Listagem 11.


  <?php
    // MySQL
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", "senha");
                          
    // PostgreSQL
                          
    $db = new PDO("pgsql:host=localhost;dbname=banco", "root", "senha");
                          
    // SQLite
   
    $db = new PDO("sqlite:banco.sqlite");
  ?>
Listagem 11. Conexão com PDO

Veja que criamos a $db que guarda um objeto da classe PDO e entre parênteses passamos o host, nome do banco, usuário e senha. Uma vez que o objeto da classe PDO tenha sido instanciado, conectamos em nosso banco. Para desconectar, basta "matarmos" o objeto ou aguardar que ele seja morto automaticamente ao final de nosso script, como mostra a Listagem 12.


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", "");
    unset($db);
  ?>
Listagem 12. Encerrando conexão

Veja que utilizamos o unset para encerrar a conexão.

Executando comando

Depois de conectados temos a nossa disposição uma série de métodos para lidar com o banco. Utilizando a linguagem SQL, vamos fazer o uso do método exec, de acordo com o código da Listagem 13.


  <?php  
  $db = new PDO("mysql:host=localhost;dbname=banco", "root", "");
   
  $db->exec("CREATE TABLE clientes(id INT AUTO_INCREMENT, 
     nome VARCHAR(255), email VARCHAR(255)) ");
  ?>
Listagem 13. Exec SQL

Veja que acessamos o método exec através de nossa conexão com o “->” e criamos uma tabela chamada clientes com os campos id, nome e email.

Fazendo consultas

Para fazer consultas usamos o método query, que executa um comando SQL e traz para nós linhas de um banco de dados. Veja um exemplo na Listagem 14.


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", "");
   
    $dados = $db->query("SELECT * FROM clientes");
  ?>
Listagem 14. Utilizando query

Veja que dentro da $dados é executada uma query que traz todos os dados da tabela clientes.

Podemos ainda acessar nossos dados através dos métodos fetch e fetchAll.

O método fetch retorna apenas um resultado para nós, enquanto o fetchAll retornará todos os resultados. Estes métodos retornam tanto um array quando um objeto, dependendo dos parâmetros especificados, como na Listagem 15.


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root","");
    $dados = $db->query("SELECT * FROM clientes");
    $todos = $dados->fetchAll();
    $um = $dados->fetch();
   
    print_r($todos);
    print_r($um);
  ?>
Listagem 15. Retornando dados com fetch e fetchAll

Note que, por padrão, os métodos retornam índices associativos e numéricos. Podemos fazer com que somente índices associativos sejam mostrados ou apenas numéricos, como mostra o código da Listagem 16.


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root","");
    $dados = $db->query("SELECT * FROM clientes");
    $assoc = $dados->fetchAll(PDO::FETCH_ASSOC);
    $num = $dados->fetchAll(PDO::FETCH_NUM);
      print_r($assoc);
      print_r($num);
  ?>
Listagem 16. Retornando apenas um dos índices

Veja que na $assoc queremos que seja retornado resultados em índices associativos, enquanto na $num retorna apenas índices numéricos.

Transações

Transações são uma sequência de operações feitas em um sistema gerenciador de banco de dados. As transações têm o objetivo de proporcionar uma maneira de recuperar informações a partir de um desastre. Uma transação de banco de dados deve possuir atomicidade, consistência, isolamento e durabilidade (ACID).

Imagine um sistema onde temos que inserir dados em tabelas de estoque, pedido, cliente e logística. O cliente pagou e tudo foi inserido aparentemente bem no sistema, mas não foi inserido o pedido de entrega na tabela de logística. Temos um problema agora, certo?!

As transações evitam esse tipo de problema através do método beginTransaction do PDO. Após chamarmos o método, todos os comandos feitos não serão automaticamente executados. O PDO esperará pelo método commit para efetivar os comandos no banco de dados, ou pelo comando rollback para anular os comandos e desfazer tudo que foi feito. Veja um exemplo na Listagem 17.


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", "");
    $db->beginTransaction();
   
    $db->exec("UPDATE pedidos SET compra = 5641");
    $db->exec("UPDATE clientes SET compra = 5641 ");
    $db->exec("INSERT INTO logística(compra) VALUES (5641)");
    // Caso tudo tenha dado certo
    $db->commit();
   
    // Caso não deu certo
    $db->rollback();
Listagem 17. Exemplo de transação

Prepared Statements

Statements são comandos SQL pré-construídos e garantem que nenhuma query que foi preparada sofra um ataque SQL injection, ou seja, um ataque baseado na manipulação do código SQL pela inserção de informações em uma query.

Imagine a inserção de vários dados em uma tabela. As únicas coisas que mudam são as informações de cada registro, a tabela e as colunas não mudam. Um statement então cria um INSERT onde vai inserir em tabela e colunas pré-determinadas, já que apenas as informações mudarão.

O PDO possui um método chamado prepare que, ao utilizá-lo, o comando não será executado, apenas preparado para ser executado depois. O método prepare retorna um objeto da classe PDOStatement para que, quando utilizamos esse método, precisamos criar placeholders que serão substituídos pelos valores que queremos que estejam naquela query.

Veja na Listagem 18 um exemplo


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", "");
   
    $statement = $db->prepare("INSERT INTO posts (titulo, conteudo) VALUES
     (?, ?)");
Listagem 18. Prepared Statements

Note que as interrogações são nossos placeholders, que serão substituídos por variáveis. Para executarmos as statements usamos o método execute, passando como parâmetro um array de variáveis que substituirão os placeholders, como mostra a Listagem 19.


  <?php
    $db = new PDO("mysql:host=localhost;dbname=banco", "root", "");
              
    $statement = $db->prepare("INSERT INTO posts (titulo, conteudo) VALUES
    (?, ?)");
              
    $statement->execute(array("Arroz", "Meu primeiro item!"));
    $statement->execute(array("Feijão", "Meu segundo item!"));
    $statement->execute(array("Tomate", "Meu terceito item!"));
Listagem 19. Executando os placeholders

Note que temos apenas uma query, mas iremos executar três vezes com três valores diferentes. Estamos passando um array de informações para o método execute, que pegará essas informações e colocará no lugar das interrogações, ou seja, os placeholders.