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.
- 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 cozinha | Na POO | Na lojinha da Ana |
|---|---|---|
| A forma de bolo | Classe: o molde, a descrição | Produto: "todo produto tem nome, preço e estoque, e pode ser vendido" |
| Cada bolo assado | Objeto (ou instância): uma coisa concreta feita com o molde | A caneca, a camiseta, o adesivo |
| O sabor, o tamanho de cada bolo | Propriedades: os dados de cada objeto | A 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 fazer | Vender 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:
- 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". - Os dados e as ações moram longe. A função
venderestá solta no arquivo. Quem lê$canecanão sabe quais funções servem para ela; quem lêvendernão sabe que formato de array ela espera. - 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:
| Trecho | O que faz | Aula |
|---|---|---|
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
| Vantagem | O que significa na prática |
|---|---|
| Organização | Tudo o que é de "produto" está na classe Produto. Para mudar como um produto é vendido, você sabe onde procurar. |
| Formato garantido | Todo objeto Produto tem as mesmas propriedades, com os tipos certos. Nada de "saco de chaves". |
| Proteção | Você pode proibir que o estoque seja mexido de fora da classe, a não ser pelo método vender (que confere se há estoque). |
| Reaproveitamento | Um ProdutoDigital pode "herdar" tudo de Produto e mudar só o que é diferente. |
| É assim que o PHP moderno é escrito | Frameworks (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. |
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:
| Termo | Sinônimos | O que é | Exemplo |
|---|---|---|---|
| Classe | tipo, molde | A descrição de um tipo de coisa | class Produto |
| Objeto | instância | Uma coisa concreta criada a partir da classe | new Produto() |
| Instanciar | criar um objeto | Usar o new | $caneca = new Produto(); |
| Propriedade | atributo, campo | Uma variável que pertence ao objeto | $caneca->preco |
| Método | função da classe | Uma 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:
| Pilar | A ideia em uma frase | No dia a dia | Aula |
|---|---|---|---|
| Encapsulamento | Esconder 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ça | Uma 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 |
| Polimorfismo | Objetos 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ção | Mostrar 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
<?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.
<?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
| Escrita | Significado |
|---|---|
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->prop | Lê ou altera uma propriedade do objeto |
$obj->metodo() | Chama um método do objeto |
$this | Dentro 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:
2. Criar o objeto
Complete para criar um objeto da classe Cliente:
3. A seta
Complete para chamar o método vender do objeto $caneca com a quantidade 2:
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.