PHP O que é POO

Até aqui, você guardou dados em variáveis e arrays e escreveu o comportamento em funções separadas. A Programação Orientada a Objetos (POO) junta as duas coisas: os dados de um "produto" e o que um produto sabe fazer passam a morar no mesmo lugar, chamado objeto. Esta aula é a porta de entrada do Nível 3: ela mostra a ideia, o vocabulário e por que vale a pena. Os detalhes de cada parte ganham uma aula própria nas próximas páginas.

Você vai aprender
  • A diferença entre classe (o molde) e objeto (a coisa feita com o molde).
  • Como a lojinha da Ana fica escrita com arrays e funções, e como fica com uma classe.
  • Os problemas que a POO resolve (e quando ela não é necessária).
  • O vocabulário: classe, objeto, instância, propriedade, método, $this.
  • A ideia dos quatro pilares: encapsulamento, herança, polimorfismo e abstração.

Pré-requisitos: funções, tipos e retorno e arrays associativos. Todos os exemplos rodam na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/loja, com o PHP 8.5.


A ideia antes do código: o molde e o bolo

Pense numa forma de bolo. A forma não é um bolo: ninguém come a forma. Ela é o molde que diz que todo bolo feito com ela vai ter aquele formato. Com uma forma só, a Ana faz quantos bolos quiser, e cada bolo é independente: um pode ser de chocolate, o outro de cenoura; comer um pedaço de um não diminui o outro.

Na POO é igual:

Na cozinhaNa POONa lojinha da Ana
A forma de boloClasse: o molde, a descriçãoProduto: "todo produto tem nome, preço e estoque, e pode ser vendido"
Cada bolo assadoObjeto (ou instância): uma coisa concreta feita com o moldeA caneca, a camiseta, o adesivo
O sabor, o tamanho de cada boloPropriedades: os dados de cada objetoA caneca custa 25,90; a camiseta custa 49,90
O que se faz com um bolo (cortar, servir)Métodos: as ações que o objeto sabe fazerVender 3 canecas; mostrar a descrição

Outra comparação que ajuda: a ficha de cadastro em branco (com os campos Nome, Preço, Estoque) é a classe. Cada ficha preenchida é um objeto. A ficha em branco existe uma vez; fichas preenchidas, quantas você quiser.


A lojinha da Ana com arrays e funções

A Ana vende canecas e camisetas pela internet. Com o que você já sabe, cada produto é um array associativo e o "vender" é uma função:

<?php
$caneca = ["nome" => "Caneca", "preco" => 25.90, "estoque" => 10];
$camiseta = ["nome" => "Camiseta", "preco" => 49.90, "estoque" => 4];

function vender(array $produto, int $quantidade): array
{
    $produto["estoque"] -= $quantidade;
    return $produto;
}

$caneca = vender($caneca, 3);
echo $caneca["nome"] . ": " . $caneca["estoque"] . " em estoque\n";
$ php arrays.php
Caneca: 7 em estoque

Funciona, e para um programa pequeno está ótimo. Mas repare em três coisas que começam a incomodar quando a loja cresce:

  1. Nada garante o formato. O array $caneca é só um "saco de chaves". Ninguém impede que alguém crie um produto sem "preco", ou com "preço" (com cedilha), ou com o preço "barato".
  2. Os dados e as ações moram longe. A função vender está solta no arquivo. Quem lê $caneca não sabe quais funções servem para ela; quem lê vender não sabe que formato de array ela espera.
  3. Um erro de digitação passa quase despercebido. Olhe o que acontece:
<?php
$caneca = ["nome" => "Caneca", "preco" => 25.90, "estoque" => 10];
$caneca["preço"] = 19.90;
echo "Preço: " . $caneca["preco"] . "\n";
echo "Desconto: " . $caneca["desconto"] . "\n";
$ php erro-array.php
Preço: 25.9                    « a promoção não pegou: ela foi para outra chave
                               « linha em branco que o PHP põe antes do aviso
Warning: Undefined array key "desconto" in /home/ana/loja/erro-array.php on line 5
Desconto: 

A linha 3 queria baixar o preço para 19,90, mas escreveu "preço" com cedilha. O PHP não reclamou: simplesmente criou uma chave nova no array. A promoção "sumiu" em silêncio. Só a chave que não existe ("desconto") gerou um aviso (Warning = "aviso"; o programa continuou).


A mesma lojinha com uma classe

Agora o mesmo programa em POO. Primeiro a Ana escreve o molde (a classe Produto), dizendo quais dados todo produto tem e o que todo produto sabe fazer. Depois ela cria os produtos de verdade com new.

<?php
class Produto
{
    public string $nome;
    public float $preco;
    public int $estoque;

    public function vender(int $quantidade): void
    {
        $this->estoque -= $quantidade;
    }

    public function descricao(): string
    {
        return "{$this->nome}: {$this->estoque} em estoque";
    }
}

$caneca = new Produto();
$caneca->nome = "Caneca";
$caneca->preco = 25.90;
$caneca->estoque = 10;

$camiseta = new Produto();
$camiseta->nome = "Camiseta";
$camiseta->preco = 49.90;
$camiseta->estoque = 4;

$caneca->vender(3);

echo $caneca->descricao() . "\n";
echo $camiseta->descricao() . "\n";
$ php produto.php
Caneca: 7 em estoque
Camiseta: 4 em estoque     « vender canecas não mexeu na camiseta

Não se preocupe em entender cada detalhe agora: cada peça tem a sua aula. Por enquanto, leia o programa assim:

TrechoO que fazAula
class Produto { … }Cria o molde chamado Produto. Sozinho, não cria produto nenhum.Classes e Objetos
public string $nome;Uma propriedade: todo produto tem um nome, que é texto (string).Propriedades e Métodos
public function vender(…)Um método: uma função que mora dentro da classe e age sobre um produto.Propriedades e Métodos
$this->estoque"O estoque deste produto": o objeto que chamou o método.Propriedades e Métodos
new Produto()Usa o molde para criar um objeto novo (um "bolo").Classes e Objetos
$caneca->vender(3)A seta -> acessa algo dentro do objeto: "caneca, venda 3".Classes e Objetos

Compare com a versão de arrays: em vez de vender($caneca, 3) (uma função solta que recebe o produto), agora é $caneca->vender(3): o próprio produto sabe se vender. Os dados e as ações andam juntos.

Experimente: rode o programa. Depois venda 2 camisetas ($camiseta->vender(2);) antes dos echo e rode de novo. Repare que cada objeto guarda o seu próprio estoque.

<?php
class Produto
{
    public string $nome;
    public float $preco;
    public int $estoque;

    public function vender(int $quantidade): void
    {
        $this->estoque -= $quantidade;
    }

    public function descricao(): string
    {
        return "{$this->nome}: {$this->estoque} em estoque";
    }
}

$caneca = new Produto();
$caneca->nome = "Caneca";
$caneca->preco = 25.90;
$caneca->estoque = 10;

$camiseta = new Produto();
$camiseta->nome = "Camiseta";
$camiseta->preco = 49.90;
$camiseta->estoque = 4;

$caneca->vender(3);

echo $caneca->descricao() . "\n";
echo $camiseta->descricao() . "\n";

A classe protege os dados

Lembra do preço "barato"? Como a propriedade foi declarada como float (número com casas decimais), o PHP recusa um texto ali:

<?php
class Produto
{
    public float $preco = 25.90;
}
$caneca = new Produto();
$caneca->preco = "barato";
$ php tipo.php

Fatal error: Uncaught TypeError: Cannot assign string to property Produto::$preco of type float in /home/ana/loja/tipo.php:7
Stack trace:
#0 {main}
  thrown in /home/ana/loja/tipo.php on line 7

Traduzindo: "não é possível atribuir uma string à propriedade Produto::$preco, que é do tipo float". O erro aparece na hora, na linha certa, em vez de virar um bug escondido. E o erro de digitação (preço com cedilha)? Num objeto, o PHP 8.2 em diante avisa (Deprecated: Creation of dynamic property Produto::$preço is deprecated), e você vai ver como transformar isso num erro de verdade em Propriedades e Métodos e Visibilidade.


Por que usar POO

VantagemO que significa na prática
OrganizaçãoTudo o que é de "produto" está na classe Produto. Para mudar como um produto é vendido, você sabe onde procurar.
Formato garantidoTodo objeto Produto tem as mesmas propriedades, com os tipos certos. Nada de "saco de chaves".
ProteçãoVocê pode proibir que o estoque seja mexido de fora da classe, a não ser pelo método vender (que confere se há estoque).
ReaproveitamentoUm ProdutoDigital pode "herdar" tudo de Produto e mudar só o que é diferente.
É assim que o PHP moderno é escritoFrameworks (Laravel, Symfony), bibliotecas do Composer, o próprio PHP (PDO, DateTime, exceções): tudo é objeto. Você já usou objetos em PDO e exceções, talvez sem perceber.
POO não é obrigatória

Um script de 20 linhas que lê um CSV e mostra um total não precisa de classes. O PHP deixa você misturar: funções soltas onde elas bastam, classes onde o programa tem "coisas" com dados e comportamento (produtos, pedidos, clientes, contas). Use POO quando o programa começar a ter vários arrays com o mesmo formato e várias funções que só servem para eles.


O vocabulário da POO

Estes nomes vão aparecer em todas as aulas do Nível 3 e em qualquer documentação de PHP. Muitos têm sinônimos:

TermoSinônimosO que éExemplo
Classetipo, moldeA descrição de um tipo de coisaclass Produto
ObjetoinstânciaUma coisa concreta criada a partir da classenew Produto()
Instanciarcriar um objetoUsar o new$caneca = new Produto();
Propriedadeatributo, campoUma variável que pertence ao objeto$caneca->preco
Métodofunção da classeUma função que pertence à classe e age sobre o objeto$caneca->vender(3)
$this"este", "eu mesmo"Dentro de um método, o objeto que foi chamado$this->estoque
Membros-Propriedades e métodos, juntos-

Na documentação oficial, você também vai ver a notação Produto::$preco (a propriedade $preco da classe Produto) e Produto::vender() (o método vender da classe Produto). É só uma forma de escrever "o que pertence a quem", como nas mensagens de erro que você já viu.


Os quatro pilares (só a ideia)

Livros e entrevistas de emprego falam nos "quatro pilares da orientação a objetos". São quatro ideias que você vai praticar, uma de cada vez, nas próximas aulas. Por enquanto, basta a ideia:

PilarA ideia em uma fraseNo dia a diaAula
EncapsulamentoEsconder os detalhes internos e só deixar mexer pelos métodos certos.No caixa eletrônico você não mexe no cofre: usa os botões "sacar" e "depositar", que conferem o saldo.Visibilidade
HerançaUma classe nova aproveita tudo de outra e muda ou acrescenta só o que é diferente.Um ProdutoDigital é um Produto, mas não tem frete.Herança
PolimorfismoObjetos diferentes respondem à mesma "ordem", cada um do seu jeito."Pague" funciona com Pix, boleto ou cartão; cada um paga de um jeito.Interfaces
AbstraçãoMostrar só o que importa e esconder a complexidade; descrever "o que" sem dizer "como".Você dirige sem saber como o motor funciona: volante, pedais e câmbio bastam.Classes Abstratas

Além dos pilares, o PHP tem ferramentas próprias para organizar classes: membros static, traits, enums, readonly e property hooks e métodos mágicos. Todas estão neste mesmo grupo do menu.


Quando dá errado

Um nome de propriedade digitado errado
<?php
class Produto
{
    public string $nome = "Caneca";
    public float $preco = 25.90;
}

$caneca = new Produto();
$caneca->preço = 19.90;
echo $caneca->preco . "\n";
echo $caneca->desconto . "\n";
$ php erro-obj.php

Deprecated: Creation of dynamic property Produto::$preço is deprecated in /home/ana/loja/erro-obj.php on line 9
25.9

Warning: Undefined property: Produto::$desconto in /home/ana/loja/erro-obj.php on line 11

O que significa: na linha 9, a Ana escreveu preço (com cedilha), que não existe na classe. O PHP criou a propriedade na hora (uma "propriedade dinâmica") e avisou: Deprecated quer dizer "isso ainda funciona, mas está condenado e vai deixar de funcionar". Na linha 11, ler uma propriedade que não existe dá Warning: Undefined property ("propriedade indefinida").

Como consertar: use o nome exato declarado na classe (preco). Diferente do array, o objeto avisa o erro de digitação na escrita. Leve esses avisos a sério: eles apontam a linha exata do problema.

Usar a classe como se fosse o objeto
<?php
class Produto { public string $nome; }
Produto->nome = "Caneca";
$ php sem-new.php

Fatal error: Cannot use temporary expression in write context in /home/ana/loja/sem-new.php on line 3
Stack trace:
#0 {main}

O que significa: a Ana esqueceu o new e tentou usar o molde direto. Sem o $, o PHP não enxerga ali uma variável onde guardar o nome, e recusa o programa inteiro antes de rodar ("não dá para usar uma expressão temporária num lugar de escrita"). A mensagem é estranha, mas a linha indicada aponta o problema.

Como consertar: a classe é só a forma. Para ter um produto, crie um objeto e guarde numa variável: $caneca = new Produto(); e depois $caneca->nome = "Caneca";. Veja os detalhes em Classes e Objetos.


Referência rápida

EscritaSignificado
class Nome { … }Declara uma classe (o molde)
public tipo $prop;Declara uma propriedade
public function nome(): tipo { … }Declara um método
$obj = new Nome();Cria um objeto (instancia a classe)
$obj->propLê ou altera uma propriedade do objeto
$obj->metodo()Chama um método do objeto
$thisDentro de um método: o objeto atual

Missão

Missão: a Ana começou a vender adesivos. Crie um terceiro objeto $adesivo com new Produto(), nome Adesivo, preço 3.50 e estoque 50. Venda 5 adesivos e mostre a descrição dele. A saída deve ter Adesivo: 45 em estoque.

<?php
class Produto
{
    public string $nome;
    public float $preco;
    public int $estoque;

    public function vender(int $quantidade): void
    {
        $this->estoque -= $quantidade;
    }

    public function descricao(): string
    {
        return "{$this->nome}: {$this->estoque} em estoque";
    }
}

$caneca = new Produto();
$caneca->nome = "Caneca";
$caneca->preco = 25.90;
$caneca->estoque = 10;
$caneca->vender(3);
echo $caneca->descricao() . "\n";

// crie o $adesivo aqui
<?php
class Produto
{
    public string $nome;
    public float $preco;
    public int $estoque;

    public function vender(int $quantidade): void
    {
        $this->estoque -= $quantidade;
    }

    public function descricao(): string
    {
        return "{$this->nome}: {$this->estoque} em estoque";
    }
}

$caneca = new Produto();
$caneca->nome = "Caneca";
$caneca->preco = 25.90;
$caneca->estoque = 10;
$caneca->vender(3);
echo $caneca->descricao() . "\n";

$adesivo = new Produto();
$adesivo->nome = "Adesivo";
$adesivo->preco = 3.50;
$adesivo->estoque = 50;
$adesivo->vender(5);
echo $adesivo->descricao() . "\n";

Exercícios

1. A palavra do molde

Complete com a palavra que declara o molde Cliente:

Cliente { }

2. Criar o objeto

Complete para criar um objeto da classe Cliente:

$ana = ;

3. A seta

Complete para chamar o método vender do objeto $caneca com a quantidade 2:

$caneca;

4. Na comparação da cozinha, o que é a classe?

5. Dentro do método vender, o que $this representa?

6. Qual pilar descreve "um ProdutoDigital aproveita tudo de Produto e muda só o frete"?

7. A Ana criou $caneca e $camiseta com new Produto() e chamou $caneca->vender(3). O que acontece com o estoque da camiseta?


Resumo

  • A classe é o molde (a forma de bolo); o objeto é a coisa feita com ele (o bolo). Um molde, muitos objetos, cada um independente.
  • Propriedades são os dados do objeto; métodos são as ações. Os dois moram juntos na classe.
  • new Produto() cria um objeto; -> acessa o que está dentro dele; $this é "este objeto" dentro de um método.
  • Comparada a arrays soltos, a classe garante o formato e os tipos, e avisa erros de digitação.
  • Os quatro pilares: encapsulamento, herança, polimorfismo e abstração. Cada um tem a sua aula.
  • POO não é obrigatória: use quando o programa tem "coisas" com dados e comportamento.