Cheatsheet PHP
Linguagem de programação web server-side
PHP
Básico
Abrir e Fechar PHP
<?php // Código PHP aqui echo "Olá Mundo"; ?> <!-- Tag curta (echo) --> <?= "Saída direta" ?> <!-- Em ficheiros .php puros, omitir o ?> --> <?php echo "Sem fecho necessário";
Use <?php para abrir e ?> para fechar. A tag curta <?= é equivalente a echo. Em ficheiros 100% PHP, omita o ?> final para evitar output acidental.
Concatenação e Interpolação
$nome = "Ana";
$idade = 25;
// Concatenação com ponto
echo "Olá " . $nome . "!";
// Interpolação (aspas duplas)
echo "Olá $nome, tens $idade anos";
echo "Olá {$nome}!"; // com chavetas
// Aspas simples (literal)
echo 'Olá $nome'; // mostra: Olá $nome
// Heredoc (multi-linha)
$texto = <<<TXT
Olá $nome,
Tens $idade anos.
TXT;
// Nowdoc (sem interpolação)
$raw = <<<'TXT'
Sem $interpolação aqui
TXT;O operador . concatena strings. Aspas duplas "$var" interpolam variáveis. {$var} para expressões complexas. Heredoc permite multi-linha com interpolação; Nowdoc sem.
Operador Ternário e Elvis
// Ternário completo
$status = ($idade >= 18) ? "adulto" : "menor";
// Elvis (se truthy, usa o valor)
$nome = $input ?: "Anónimo";
// Equivalente a: $input ? $input : "Anónimo"
// Null coalescing (se não-null)
$nome = $_GET['nome'] ?? "Visitante";
// Encadeamento
$nivel = $score > 90 ? "A"
: ($score > 70 ? "B" : "C");
// Ternário em HTML
<?= $ativo ? "Ativo" : "Inativo" ?>O ternário ? : é um if/else compacto. O operador Elvis ?: retorna o valor se truthy. ?? só verifica null. Use com moderação — ternários longos são difíceis de ler.
Constantes Mágicas
__LINE__; // número da linha atual __FILE__; // caminho completo do ficheiro __DIR__; // pasta do ficheiro __FUNCTION__; // nome da função __CLASS__; // nome da classe __METHOD__; // Classe::metodo __NAMESPACE__; // namespace atual // Uso prático require __DIR__ . '/config.php'; echo "Erro em " . __FILE__ . ":" . __LINE__; // PHP 8.1: enum __TRAIT__; // nome da trait
Constantes mágicas começam e terminam com __. __DIR__ substitui dirname(__FILE__). __LINE__ dá a linha atual. Úteis para debug, includes e logging.
Variáveis
$nome = "Cláudio";
$idade = 30;
$preco = 19.99;
$ativo = true;
$nulo = null;
// Variáveis dinâmicas
$campo = "nome";
echo $$campo; // "Cláudio"
// Constantes
define("MAX", 100);
const PI = 3.14159;
// Verificar existência
isset($nome); // true
isset($inexist); // falseVariáveis começam com $. define() e const criam constantes. isset() verifica se existe e não é null. Tipos: string, int, float, bool, null.
Superglobais
$_GET['id']; // parâmetros URL $_POST['nome']; // dados do form $_REQUEST['x']; // GET + POST + COOKIE $_SESSION['user']; // sessão $_COOKIE['token']; // cookies $_SERVER['HTTP_HOST']; // servidor $_FILES['upload']; // upload $_ENV['APP_KEY']; // ambiente // Exemplo prático $id = $_GET['id'] ?? 0; $nome = $_POST['nome'] ?? '';
Superglobais são arrays automáticos disponíveis em todo o lado. $_GET para URL, $_POST para formulários, $_SESSION para sessão. Sempre sanitize antes de usar!
Incremento e Atribuição
$x = 5; $x++; // 6 (pós-incremento) ++$x; // 7 (pré-incremento) $x--; // 6 (pós-decremento) // Atribuição composta $x += 10; // $x = $x + 10 $x -= 3; // $x = $x - 3 $x *= 2; // $x = $x * 2 $x /= 4; // $x = $x / 4 $x %= 3; // $x = $x % 3 $x .= "!"; // $x = $x . "!" (string) $x ??= "default"; // só se null
++$x incrementa antes de usar; $x++ usa e depois incrementa. .= concatena strings. ??= atribui só se a variável for null. +=, -=, *= são atalhos aritméticos.
Tipos de Dados
// Tipos escalares $str = "texto"; // string $num = 42; // int $float = 3.14; // float $bool = true; // bool // Tipos compostos $arr = [1, 2, 3]; // array $obj = new stdClass; // object // Verificar tipo gettype($num); // "integer" is_string($str); // true is_int($num); // true is_array($arr); // true // Casting $str2 = (string) 42; // "42" $int2 = (int) "42px"; // 42 $bool2 = (bool) ""; // false
Use gettype() para ver o tipo e is_*() para verificar. (int), (string), (bool) fazem casting. Valores falsy: 0, "", null, false, [].
Include e Require
// Inclui ficheiro (warning se não existir) include 'header.php'; // Obrigatório (fatal error se não existir) require 'config.php'; // Inclui apenas 1 vez require_once 'database.php'; include_once 'helpers.php'; // Caminho relativo ao ficheiro atual require __DIR__ . '/partials/nav.php'; // Retornar valores de ficheiros $config = require 'config.php';
require para ficheiros essenciais (para se falhar). include para opcionais. *_once evita inclusão duplicada. __DIR__ dá o caminho absoluto da pasta atual.
Echo, Print e Output
// echo (mais rápido, sem retorno)
echo "Olá", " ", "Mundo"; // múltiplos args
echo "Olá" . " Mundo"; // concatenação
// print (retorna 1)
print "Olá Mundo";
// printf (formatado)
printf("%s tem %d anos", "Ana", 25);
// Output buffering
ob_start();
echo "Conteúdo capturado";
$html = ob_get_clean();
// print_r para debug
print_r($array);echo é mais rápido e aceita múltiplos argumentos. print retorna 1 (usável em expressões). printf() formata com placeholders. ob_start()/ob_get_clean() capturam output para variável.
Operadores
// Aritméticos $a + $b; // soma $a - $b; // subtração $a * $b; // multiplicação $a / $b; // divisão $a % $b; // resto $a ** $b; // potência (2**3 = 8) // Comparação $a == $b; // igual (valor) $a === $b; // idêntico (valor + tipo) $a != $b; // diferente $a <=> $b; // spaceship (-1, 0, 1) // Lógicos $a && $b; // AND $a || $b; // OR !$a; // NOT // Null coalescing $val = $_GET['x'] ?? 'default';
=== compara valor E tipo (prefira sempre). <=> (spaceship) retorna -1, 0 ou 1. ?? retorna o primeiro valor não-null. ** é potência. % é o resto da divisão.
Comentários
// Comentário de uma linha
# Também funciona (estilo shell)
/*
* Comentário
* multi-linha
*/
/**
* DocBlock para funções/classes
* @param string $nome
* @return string
*/
function saudar(string $nome): string {
return "Olá $nome";
}// e # para uma linha. /* */ para multi-linha. /** */ (DocBlock) para documentação — IDEs leem @param, @return, @throws.
Conversão de Tipos (Casting)
// Casting explícito $str = (string) 42; // "42" $int = (int) "42px"; // 42 $float = (float) "3.14"; // 3.14 $bool = (bool) ""; // false $arr = (array) $obj; // objeto → array // settype (muda a variável) $var = "42"; settype($var, "integer"); // $var é int agora // Conversão automática (type juggling) "10" + 5; // 15 (int) "10" . 5; // "105" (string) true + true; // 2 // PHP 8: mais estrito // "abc" + 1; // TypeError!
Use (int), (string), (float), (bool) para casting. settype() muda o tipo da variável. PHP 8 é mais estrito: operações com strings não-numéricas lançam TypeError.
Strings
Funções Básicas de Strings
$str = " Olá Mundo ";
strlen($str); // 13 (comprimento)
trim($str); // "Olá Mundo"
strtoupper($str); // " OLÁ MUNDO "
strtolower($str); // " olá mundo "
ucfirst("ola mundo"); // "Ola mundo"
ucwords("ola mundo"); // "Ola Mundo"
str_repeat("ha", 3); // "hahaha"
str_pad("5", 3, "0", STR_PAD_LEFT); // "005"
// Contar ocorrências
substr_count("banana", "na"); // 2trim() remove espaços nas pontas. strlen() dá o comprimento. strtoupper()/strtolower() mudam capitalização. str_pad() preenche até um tamanho. substr_count() conta repetições.
Formatação de Strings
// sprintf (formatação)
sprintf("Olá %s, tens %d anos", "Ana", 25);
sprintf("Preço: %.2f€", 19.9); // "19.90€"
sprintf("ID: %05d", 42); // "ID: 00042"
sprintf("%x", 255); // "ff" (hex)
// number_format
number_format(1234567.891, 2, ",", ".");
// "1.234.567,89"
// money_format alternativo
"R$ " . number_format(99.9, 2);
// nl2br (quebras de linha → <br>)
echo nl2br("Linha 1\nLinha 2");
// htmlspecialchars (escapar HTML)
htmlspecialchars('<script>alert("x")</script>');sprintf() formata com placeholders: %s (string), %d (int), %f (float). number_format() formata números com separadores. htmlspecialchars() escapa HTML (previne XSS).
Serialização
// serialize / unserialize
$dados = ['nome' => 'Ana', 'itens' => [1,2,3]];
$str = serialize($dados);
// 'a:2:{s:4:"nome";s:3:"Ana";...}'
$restaurado = unserialize($str);
// Cuidado: segurança!
// NUNCA unserialize() dados do utilizador
// Use json_encode/json_decode em vez disso
// __sleep e __wakeup
class User {
public function __sleep(): array {
return ['nome', 'email']; // o que serializar
}
public function __wakeup(): void {
// após unserialize
}
}serialize() converte para string. unserialize() restaura. Perigoso: nunca use unserialize() com dados do utilizador (RCE). Prefira json_encode()/json_decode(). __sleep()/__wakeup() controlam serialização.
Substring e Posição
$str = "Olá Mundo PHP"; // Extrair parte substr($str, 4); // "Mundo PHP" substr($str, 4, 5); // "Mundo" substr($str, -3); // "PHP" // Encontrar posição strpos($str, "Mundo"); // 4 strrpos($str, "P"); // 10 (última) stripos($str, "mundo"); // 4 (case-insensitive) // Verificar se contém str_contains($str, "Mundo"); // true (PHP 8+) str_starts_with($str, "Olá"); // true str_ends_with($str, "PHP"); // true
substr() extrai parte da string. strpos() encontra a posição (ou false). PHP 8+: str_contains(), str_starts_with(), str_ends_with() — mais legíveis que strpos() !== false.
Regex com preg_*
// Testar se corresponde
preg_match('/^\d{9}$/', $telefone);
// Extrair primeiro match
preg_match('/(\d{4})-(\d{3})/', $str, $m);
$m[0]; // match completo
$m[1]; // 1º grupo
// Todos os matches
preg_match_all('/\d+/', "a1 b22 c333", $all);
$all[0]; // ["1", "22", "333"]
// Substituir com regex
preg_replace('/\s+/', ' ', $texto);
preg_replace('/\d+/', 'X', "a1b2"); // "aXbX"
// Split por regex
preg_split('/[\s,]+/', "a, b c");preg_match() testa/encontra o primeiro match. preg_match_all() encontra todos. preg_replace() substitui com padrão. Delimitadores: /, #, ~. Modificadores: i (case-insensitive), s (dotall).
mb_* — Strings Multibyte
$str = "Olá Mundo 🌍";
// strlen conta bytes, mb_strlen conta caracteres
strlen($str); // 16 (bytes)
mb_strlen($str); // 12 (caracteres)
// Substring multibyte
mb_substr($str, 0, 3); // "Olá"
mb_strtoupper($str); // "OLÁ MUNDO 🌍"
mb_strtolower($str); // "olá mundo 🌍"
// Encoding
mb_detect_encoding($str); // "UTF-8"
mb_convert_encoding($str, 'ISO-8859-1');
// Internal encoding
mb_internal_encoding('UTF-8');Funções mb_* lidam com UTF-8 corretamente. strlen() conta bytes, mb_strlen() conta caracteres. Use mb_substr(), mb_strtoupper() para acentos e emojis. Defina mb_internal_encoding('UTF-8') no início.
Substituição
// Substituir texto
str_replace("mundo", "PHP", "Olá mundo");
// "Olá PHP"
// Substituir com limite
str_replace("a", "o", "banana", $count);
// "bonono" ($count = 3)
// Substituir com arrays
$search = ["foo", "bar"];
$replace = ["baz", "qux"];
str_replace($search, $replace, "foo bar");
// Substituir por posição
substr_replace("Olá Mundo", "PHP", 4, 5);
// "Olá PHP"str_replace() substitui todas as ocorrências. O 4º parâmetro (por referência) conta quantas substituições fez. substr_replace() substitui por posição. Para regex, use preg_replace().
Comparação e Encoding
// Comparação
strcmp("abc", "abc"); // 0 (iguais)
strcasecmp("ABC", "abc"); // 0 (case-insensitive)
strncmp("abcdef", "abcxyz", 3); // 0 (3 chars)
// Encoding
mb_strlen("café"); // 4 (UTF-8 correto)
mb_strtoupper("café"); // "CAFÉ"
mb_substr("Olá Mundo", 0, 3); // "Olá"
mb_detect_encoding($str);
// Converter encoding
mb_convert_encoding($str, "UTF-8", "ISO-8859-1");
utf8_encode($str); // ISO → UTF-8
utf8_decode($str); // UTF-8 → ISOPara strings com acentos/emoji, use funções mb_* (multibyte). mb_strlen() conta caracteres corretamente. strcmp() compara strings (retorna 0 se iguais). Sempre use UTF-8 no projeto.
sprintf e printf
// printf: output direto
printf("Nome: %s, Idade: %d", "Ana", 25);
// sprintf: retorna string
$msg = sprintf("Total: %.2f EUR", 19.9);
// "Total: 19.90 EUR"
// Placeholders
sprintf("%05d", 42); // "00042" (padding)
sprintf("%-10s|", "Esq"); // "Esq |" (left)
sprintf("%+d", 42); // "+42" (sinal)
sprintf("%x", 255); // "ff" (hex)
sprintf("%b", 10); // "1010" (binário)
// Argumento posicional
sprintf("%2\$s %1\$s", "Mundo", "Olá");
// "Olá Mundo"sprintf() formata e retorna string. printf() faz output direto. Placeholders: %s string, %d int, %f float, %x hex. Modificadores: %05d padding, %.2f decimais, %-10s alinhamento.
Split e Join
// Dividir string em array
$parts = explode(",", "a,b,c");
// ["a", "b", "c"]
$words = explode(" ", "Olá Mundo PHP", 2);
// ["Olá", "Mundo PHP"] (limite 2)
// Juntar array em string
$str = implode(", ", ["a", "b", "c"]);
// "a, b, c"
$url = implode("/", ["api", "users", "1"]);
// "api/users/1"
// Dividir em caracteres
$chars = str_split("ABC");
// ["A", "B", "C"]explode() divide por delimitador. O 3º parâmetro limita o número de partes. implode() junta array com separador. str_split() divide em caracteres individuais.
str_replace Avançado
// Múltiplas substituições
$busca = ['foo', 'bar', 'baz'];
$troca = ['FOO', 'BAR', 'BAZ'];
str_replace($busca, $troca, 'foo bar baz');
// strtr (mais eficiente para múltiplas)
strtr('foo bar', ['foo' => 'FOO', 'bar' => 'BAR']);
// Substituir só 1ª ocorrência
$pos = strpos($str, 'needle');
if ($pos !== false) {
$str = substr_replace($str, 'novo', $pos, 6);
}
// Remover acentos
function semAcentos(string $s): string {
return strtr($s, 'áàãâéêíóôõúüç',
'aaaaeeiooouuc');
}str_replace() aceita arrays para múltiplas substituições. strtr() é mais eficiente para pares chave-valor. substr_replace() substitui por posição. Para padrões complexos, use preg_replace().
Trim e Padding Avançado
$str = " Olá Mundo ";
trim($str); // "Olá Mundo"
ltrim($str); // "Olá Mundo "
rtrim($str); // " Olá Mundo"
// Trim com caracteres personalizados
trim("---texto---", "-"); // "texto"
trim("00042", "0"); // "42"
trim("\t\n texto \r\n"); // "texto"
// Padding
str_pad("5", 3, "0", STR_PAD_LEFT); // "005"
str_pad("Hi", 10, "-", STR_PAD_BOTH); // "----Hi----"
str_pad("OK", 6, ".", STR_PAD_RIGHT); // "OK...."
// Wordwrap
wordwrap("Texto longo aqui", 10, "\n");trim() remove espaços (ou caracteres indicados) das extremidades. ltrim()/rtrim() só esquerda/direita. str_pad() preenche até ao tamanho desejado. wordwrap() quebra texto em linhas de tamanho fixo.
Arrays
Criar Arrays
// Array indexado
$frutas = ["maçã", "banana", "pera"];
// Array associativo
$user = [
"nome" => "Ana",
"idade" => 30,
"ativo" => true,
];
// Array multidimensional
$matriz = [
[1, 2, 3],
[4, 5, 6],
];
// Criar com range
$numeros = range(1, 10);
$letras = range('a', 'f');
// Array vazio
$vazio = [];Sintaxe curta [] (PHP 5.4+). Arrays podem ser indexados (0, 1, 2...) ou associativos (chave => valor). range() cria sequências. Arrays em PHP são mapas ordenados (aceitam qualquer tipo de chave).
array_reduce e array_keys
$numeros = [1, 2, 3, 4, 5];
// reduce: acumular num valor
$soma = array_reduce($numeros, fn($acc, $n) => $acc + $n, 0);
// 15
$concat = array_reduce(["a","b","c"], fn($acc, $s) => $acc . $s, "");
// "abc"
// keys e values
$arr = ["nome" => "Ana", "idade" => 30];
array_keys($arr); // ["nome", "idade"]
array_values($arr); // ["Ana", 30]
// in_array (verificar existência)
in_array("Ana", $arr); // true
in_array("Ana", $arr, true); // true (strict)
array_key_exists("nome", $arr); // truearray_reduce() acumula todos os elementos num único valor. array_keys()/array_values() extraem chaves/valores. in_array() verifica se um valor existe. array_key_exists() verifica chave.
array_column e array_multisort
$users = [
['nome' => 'Ana', 'idade' => 30],
['nome' => 'Rui', 'idade' => 25],
['nome' => 'Eva', 'idade' => 35],
];
// Extrair coluna
$nomes = array_column($users, 'nome');
// ['Ana', 'Rui', 'Eva']
// Reindexar por coluna
$porNome = array_column($users, null, 'nome');
// ['Ana' => [...], 'Rui' => [...], ...]
// Ordenar por coluna
array_multisort(
array_column($users, 'idade'), SORT_ASC,
$users
);array_column() extrai uma coluna de array multidimensional. Com null como 2º arg, reindexa pela coluna. array_multisort() ordena o array original por uma coluna específica.
array_flip e array_search
// array_flip: troca chaves ↔ valores
$map = ['a' => 1, 'b' => 2, 'c' => 3];
array_flip($map); // [1 => 'a', 2 => 'b', 3 => 'c']
// array_search: encontra chave pelo valor
$frutas = ['maçã', 'banana', 'cereja'];
array_search('banana', $frutas); // 1
array_search('x', $frutas); // false
// array_key_exists vs isset
$arr = ['nome' => null];
array_key_exists('nome', $arr); // true
isset($arr['nome']); // false (é null!)
// array_rand: chave(s) aleatória(s)
array_rand($frutas); // 0, 1 ou 2
array_rand($frutas, 2); // [0, 2] (2 chaves)array_flip() inverte chaves e valores. array_search() retorna a chave do valor encontrado (ou false). array_key_exists() verifica chave mesmo com valor null (diferente de isset()). array_rand() retorna chave(s) aleatória(s).
Aceder e Modificar
$arr = ["a" => 1, "b" => 2, "c" => 3]; // Aceder $arr["a"]; // 1 $arr["b"] ?? 0; // 2 (com default) // Modificar $arr["d"] = 4; // adicionar $arr["a"] = 10; // atualizar unset($arr["b"]); // remover // Adicionar ao fim/início $arr[] = 5; // fim (push) array_push($arr, 6, 7); // fim (múltiplos) array_unshift($arr, 0); // início // Remover do fim/início $ultimo = array_pop($arr); $primeiro = array_shift($arr);
$arr[] adiciona ao fim. unset() remove um elemento. array_push()/array_pop() para o fim. array_unshift()/array_shift() para o início. ?? evita erro se a chave não existir.
Ordenação
$numeros = [3, 1, 4, 1, 5]; sort($numeros); // [1, 1, 3, 4, 5] (perde chaves) rsort($numeros); // [5, 4, 3, 1, 1] (reverso) // Preservando chaves asort($numeros); // ordena por valor arsort($numeros); // reverso por valor ksort($numeros); // ordena por chave krsort($numeros); // reverso por chave // Ordenação personalizada $users = [["nome" => "B"], ["nome" => "A"]]; usort($users, fn($a, $b) => strcmp($a["nome"], $b["nome"])); // Ordenar por coluna array_multisort(array_column($users, "nome"), SORT_ASC, $users);
sort() ordena valores (perde chaves). asort() preserva chaves. usort() aceita callback personalizado. array_multisort() ordena por coluna de array multidimensional. Prefixo r = reverso, k = por chave.
array_intersect e array_diff
$a = [1, 2, 3, 4, 5]; $b = [3, 4, 5, 6, 7]; // Interseção (comuns) array_intersect($a, $b); // [3, 4, 5] // Diferença (em $a mas não em $b) array_diff($a, $b); // [1, 2] // Associativo $x = ['a' => 1, 'b' => 2]; $y = ['b' => 2, 'c' => 3]; array_intersect_assoc($x, $y); // ['b' => 2] array_diff_key($x, $y); // ['a' => 1] // Combinar com spread $comum = [...array_intersect($a, $b)];
array_intersect() retorna valores comuns. array_diff() retorna valores que estão no 1º mas não nos outros. Sufixos: _key compara chaves, _assoc compara ambos. Preservam chaves do 1º array.
Iterar Arrays
$frutas = ["maçã", "banana", "pera"];
// foreach simples
foreach ($frutas as $fruta) {
echo $fruta;
}
// foreach com chave
foreach ($frutas as $i => $fruta) {
echo "$i: $fruta";
}
// for tradicional
for ($i = 0; $i < count($frutas); $i++) {
echo $frutas[$i];
}
// while com each (antigo)
// array_walk (callback)
array_walk($frutas, function($val, $key) {
echo "$key => $val\n";
});foreach é a forma preferida de iterar. $i => $val dá chave e valor. count() retorna o tamanho. array_walk() aplica callback a cada elemento. Evite modificar o array durante iteração.
Merge, Slice e Chunk
$a = [1, 2, 3]; $b = [3, 4, 5]; // Merge $c = array_merge($a, $b); // [1,2,3,3,4,5] $d = [...$a, ...$b]; // spread (PHP 7.4+) // Associativo: última chave ganha $x = ["a" => 1]; $y = ["a" => 2, "b" => 3]; array_merge($x, $y); // ["a" => 2, "b" => 3] // Slice (extrair parte) array_slice([1,2,3,4,5], 1, 3); // [2,3,4] // Chunk (dividir em grupos) array_chunk([1,2,3,4,5], 2); // [[1,2], [3,4], [5]] // Unique array_unique([1, 2, 2, 3, 3]); // [1, 2, 3]
array_merge() combina arrays. Spread [...$a, ...$b] é mais moderno. array_slice() extrai parte. array_chunk() divide em grupos. array_unique() remove duplicados.
array_walk e array_walk_recursive
$frutas = ['maçã', 'banana', 'cereja'];
// array_walk: modifica por referência
array_walk($frutas, function (&$item, $key) {
$item = ucfirst($item);
});
// ['Maçã', 'Banana', 'Cereja']
// Com parâmetro extra
$prefixo = 'Fruta: ';
array_walk($frutas, function (&$item) use ($prefixo) {
$item = $prefixo . $item;
});
// Recursivo (arrays aninhados)
$nested = ['a' => [1, 2], 'b' => [3, 4]];
array_walk_recursive($nested, function (&$val) {
$val *= 2;
});
// ['a' => [2, 4], 'b' => [6, 8]]array_walk() aplica callback a cada elemento (por referência). Diferente de array_map(): modifica o original e passa chave como 2º argumento. array_walk_recursive() entra em arrays aninhados.
array_map e array_filter
$numeros = [1, 2, 3, 4, 5];
// map: transformar cada elemento
$dobro = array_map(fn($n) => $n * 2, $numeros);
// [2, 4, 6, 8, 10]
// filter: manter os que passam
$pares = array_filter($numeros, fn($n) => $n % 2 === 0);
// [2 => 2, 4 => 4]
// filter sem callback (remove falsy)
$limpo = array_filter([0, 1, "", "a", null, 2]);
// [1 => 1, 3 => "a", 5 => 2]
// map com chaves preservadas
$upper = array_map('strtoupper', ["ana", "rui"]);
// ["ANA", "RUI"]array_map() transforma cada elemento e retorna novo array. array_filter() mantém só os que passam no teste. Sem callback, array_filter() remove valores falsy. Ambos preservam as chaves originais.
Destructuring e Spread
// Destructuring (list)
[$a, $b, $c] = [1, 2, 3];
echo $a; // 1
// Com chaves
["nome" => $nome, "idade" => $idade] = [
"nome" => "Ana", "idade" => 30
];
// Ignorar elementos
[, , $terceiro] = [1, 2, 3];
// Spread em chamadas
function soma($a, $b, $c) { return $a+$b+$c; }
$args = [1, 2, 3];
soma(...$args); // 6
// Spread para criar arrays
$base = [1, 2];
$extra = [...$base, 3, 4]; // [1,2,3,4]Destructuring [$a, $b] = [1, 2] extrai valores. Com chaves: ["nome" => $n]. Spread ... expande arrays em argumentos ou noutro array. Vírgula sozinha [, , $x] ignora posições.
array_unique e Contagem
$arr = [1, 2, 2, 3, 3, 3];
// Remover duplicados
array_unique($arr); // [1, 2, 3]
array_values(array_unique($arr)); // reindexar
// Contagem
count($arr); // 6
sizeof($arr); // 6 (alias)
// Contagem recursiva
$nested = ['a' => [1, 2], 'b' => [3]];
count($nested, COUNT_RECURSIVE); // 5
// Contar valores
array_count_values(['a','b','a','c','a']);
// ['a' => 3, 'b' => 1, 'c' => 1]
// Verificar existência
in_array('banana', $frutas); // true/false
in_array('5', [5, 10], true); // false (strict)array_unique() remove duplicados (preserva chaves — use array_values() para reindexar). count() conta elementos. array_count_values() conta ocorrências. in_array() com 3º arg true faz comparação strict.
Controlo de Fluxo
If / Else / ElseIf
$idade = 20;
if ($idade >= 18) {
echo "Adulto";
} elseif ($idade >= 13) {
echo "Adolescente";
} else {
echo "Criança";
}
// Sintaxe alternativa (templates)
<?php if ($ativo): ?>
<p>Ativo</p>
<?php else: ?>
<p>Inativo</p>
<?php endif; ?>
// Null coalescing chain
$val = $a ?? $b ?? $c ?? "default";if/elseif/else para condições. A sintaxe alternativa if: endif; é ideal em templates HTML. ?? encadeado verifica múltiplas variáveis. Use === em comparações para evitar coerção de tipos.
Operadores de Comparação
// Loose vs Strict 0 == "0"; // true (compara valor) 0 === "0"; // false (compara valor + tipo) null == false; // true null === false; // false // Spaceship 1 <=> 2; // -1 2 <=> 2; // 0 3 <=> 2; // 1 // Útil em usort $nums = [3, 1, 2]; usort($nums, fn($a, $b) => $a <=> $b); // Null coalescing vs ternário $val = $x ?? "default"; // só null $val = $x ?: "default"; // qualquer falsy
Sempre prefira === (strict) a == (loose). <=> (spaceship) retorna -1, 0 ou 1 — ideal para usort(). ?? só ativa com null; ?: com qualquer valor falsy.
Loops Aninhados e Labels
// Loop aninhado com break/continue por nível
for ($i = 1; $i <= 3; $i++) {
for ($j = 1; $j <= 3; $j++) {
if ($i === 2 && $j === 2) break 2;
echo "$i,$j ";
}
}
// 1,1 1,2 1,3 2,1
// foreach aninhado
foreach ($categorias as $cat) {
foreach ($cat->produtos as $prod) {
if ($prod->stock === 0) continue 2;
echo $prod->nome;
}
}
// while com múltiplas condições
$x = 0; $y = 10;
while ($x < 5 && $y > 3) {
$x++; $y--;
}break 2 e continue 2 atuam sobre o loop externo. O número indica quantos níveis. Em loops aninhados, use continue 2 para saltar a iteração do loop pai. Combine condições com && ou ||.
Switch e Match
// Switch clássico
$dia = "segunda";
switch ($dia) {
case "segunda":
case "terça":
echo "Início";
break;
case "sexta":
echo "Fim";
break;
default:
echo "Meio";
}
// Match (PHP 8+) — mais seguro
$resultado = match($dia) {
"segunda", "terça" => "Início",
"sexta" => "Fim",
default => "Meio",
};switch compara com == (loose). match (PHP 8+) compara com === (strict) e retorna valor. match não precisa de break e lança erro se nenhum caso corresponder (sem default).
Estruturas Alternativas (Templates)
<?php if ($loggedIn): ?>
<p>Bem-vindo, <?= $nome ?></p>
<?php else: ?>
<a href="/login">Entrar</a>
<?php endif; ?>
<?php foreach ($items as $item): ?>
<li><?= htmlspecialchars($item) ?></li>
<?php endforeach; ?>
<?php while ($row = $stmt->fetch()): ?>
<p><?= $row['nome'] ?></p>
<?php endwhile; ?>Sintaxe alternativa if: endif;, foreach: endforeach;, while: endwhile; é ideal em templates HTML. Mais legível que chavetas quando misturado com HTML. Use <?= para output escapado.
Match Expression (PHP 8) 8.0
// Switch: verbose, loose comparison
switch ($status) {
case 'ativo':
$label = 'Ativo';
break;
case 'inativo':
$label = 'Inativo';
break;
default:
$label = 'Desconhecido';
}
// Match: expression, strict, no break
$label = match($status) {
'ativo' => 'Ativo',
'inativo', 'suspenso' => 'Bloqueado',
default => throw new Exception("Status inválido"),
};match (PHP 8+) é uma expressão que retorna valor. Compara com === (strict). Não precisa de break. Múltiplos casos com vírgula. Sem default, lança UnhandledMatchError se nada corresponder.
For e Foreach
// for tradicional
for ($i = 0; $i < 10; $i++) {
echo $i;
}
// for com múltiplas variáveis
for ($i = 0, $j = 10; $i < $j; $i++, $j--) {
echo "$i $j";
}
// foreach (arrays)
foreach ([1, 2, 3] as $val) {
echo $val;
}
// foreach com referência
$arr = [1, 2, 3];
foreach ($arr as &$val) {
$val *= 2; // modifica o original
}
unset($val); // importante!for quando sabe o número de iterações. foreach para arrays/objetos. &$val itera por referência (modifica o original) — faça unset($val) depois para evitar bugs.
Break, Continue e Goto
// break com nível (loops aninhados)
for ($i = 0; $i < 10; $i++) {
for ($j = 0; $j < 10; $j++) {
if ($i * $j > 20) break 2; // sai dos 2
echo "$i x $j = " . ($i*$j);
}
}
// continue com nível
for ($i = 0; $i < 5; $i++) {
for ($j = 0; $j < 5; $j++) {
if ($j === 2) continue 2; // salta o for externo
}
}
// goto (evitar!)
goto fim;
echo "Isto não executa";
fim:
echo "Saltou para aqui";break 2 sai de 2 loops aninhados. continue 2 salta para o próximo do loop externo. goto existe mas evite — torna o código difícil de seguir. Prefira funções e condições claras.
While e Do-While
// while
$n = 0;
while ($n < 5) {
echo $n++;
}
// do-while (executa pelo menos 1x)
do {
$input = readline("Nome: ");
} while (empty($input));
// Loop infinito com break
$i = 0;
while (true) {
if ($i >= 10) break;
echo $i++;
}
// continue (saltar iteração)
for ($i = 0; $i < 10; $i++) {
if ($i % 2 === 0) continue;
echo $i; // só ímpares
}while repete enquanto a condição for verdadeira. do-while executa pelo menos uma vez. break sai do loop. continue salta para a próxima iteração. Cuidado com loops infinitos!
declare e Ticks
// strict_types: força tipos exatos
declare(strict_types=1);
function soma(int $a, int $b): int {
return $a + $b;
}
soma(1, "2"); // TypeError!
// ticks: executa a cada N statements
declare(ticks=1);
register_tick_function(function () {
echo "Tick executado\n";
});
// encoding (PHP 8.3+)
// declare(encoding='UTF-8');declare(strict_types=1) deve ser a 1ª linha do ficheiro. Força type hints exatos (sem coerção). ticks executa callback a cada N statements (útil para profiling). strict_types é por ficheiro, não global.
Funções
Definir Funções
// Função básica
function saudar(string $nome): string {
return "Olá, $nome!";
}
// Parâmetro com default
function potencia(int $base, int $exp = 2): int {
return $base ** $exp;
}
// Sem retorno (void)
function log(string $msg): void {
file_put_contents('app.log', $msg . PHP_EOL, FILE_APPEND);
}
// Nullable return
function encontrar(int $id): ?string {
return $id === 1 ? "Ana" : null;
}Use type hints nos parâmetros e retorno. ?string permite null. void indica sem retorno. Defaults com = valor. Nomes de funções são case-insensitive.
Recursão e Memoização
// Fatorial recursivo
function fatorial(int $n): int {
return $n <= 1 ? 1 : $n * fatorial($n - 1);
}
// Fibonacci com memoização
function fib(int $n, array &$cache = []): int {
if ($n <= 1) return $n;
return $cache[$n] ??= fib($n-1, $cache) + fib($n-2, $cache);
}
// Recursão com array
function achatar(array $arr): array {
$result = [];
foreach ($arr as $item) {
is_array($item)
? $result = [...$result, ...achatar($item)]
: $result[] = $item;
}
return $result;
}Recursão: função que se chama a si mesma. Sempre tenha um caso base para parar. &$cache por referência para memoização. ??= calcula e guarda só se não existir.
Funções Variáveis e Dinâmicas
// Função variável
$fn = 'strtoupper';
echo $fn('olá'); // "OLÁ"
// Nome dinâmico
$acao = $_GET['acao'] ?? 'listar';
$funcoes = ['listar' => 'listarItens', 'criar' => 'criarItem'];
if (isset($funcoes[$acao])) {
$funcoes[$acao]();
}
// call_user_func
call_user_func('strtoupper', 'olá');
call_user_func_array('array_merge', [[1], [2]]);
// function_exists
if (function_exists('array_is_list')) {
// PHP 8.1+
}Funções variáveis: guarde o nome numa variável e chame com $fn(). call_user_func() chama por nome. call_user_func_array() passa args como array. function_exists() verifica disponibilidade. Útil para padrões de dispatch.
Arrow Functions e Closures
// Arrow function (PHP 7.4+)
$dobro = fn($x) => $x * 2;
$soma = fn($a, $b) => $a + $b;
// Captura automática do escopo
$fator = 3;
$multiplicar = fn($x) => $x * $fator;
// Closure tradicional
$contador = function() {
static $count = 0;
return ++$count;
};
// Closure com use
$prefixo = "Dr.";
$saudar = function($nome) use ($prefixo) {
return "$prefixo $nome";
};fn() => é uma arrow function (captura variáveis automaticamente). function() use ($var) é uma closure que importa variáveis do escopo. static preserva o valor entre chamadas.
Type Hints Avançados
// Union types (PHP 8+)
function formatar(int|float $num): string {
return number_format($num, 2);
}
// Nullable
function buscar(int $id): ?User {
return $id ? new User() : null;
}
// mixed (PHP 8+)
function dump(mixed $val): void {
var_dump($val);
}
// never (PHP 8.1+)
function abort(string $msg): never {
throw new RuntimeException($msg);
}
// strict mode
declare(strict_types=1);
function soma(int $a, int $b): int {
return $a + $b;
}
// soma(1, "2"); // TypeError!declare(strict_types=1) desativa coerção automática. Union types int|float aceitam múltiplos tipos. ?Type permite null. mixed aceita qualquer. never indica que nunca retorna.
Named Arguments (PHP 8) 8.0
// Antes: ordem importa
array_slice($arr, 0, 5, true);
// Agora: por nome
array_slice(
array: $arr,
offset: 0,
length: 5,
preserve_keys: true
);
// Saltar parâmetros com default
htmlspecialchars($input, double_encode: false);
// Combinar positional + named
str_pad('5', length: 3, pad_string: '0');Named arguments (PHP 8+) permitem passar parâmetros por nome em qualquer ordem. Ideal para funções com muitos parâmetros opcionais. Pode saltar defaults. Depois de um named, todos devem ser named.
Parâmetros Variáveis e Spread
// Variadic (...args)
function soma(...$nums): int {
return array_sum($nums);
}
soma(1, 2, 3); // 6
// Spread para chamar
$args = [1, 2, 3];
soma(...$args); // 6
// Named arguments (PHP 8+)
function criarUser(string $nome, int $idade = 0, bool $ativo = true) {}
criarUser(nome: "Ana", ativo: false);
// First-class callable (PHP 8.1+)
$strlen = strlen(...);
echo $strlen("olá"); // 3...$args aceita número variável de argumentos. ... (spread) expande arrays. Named arguments nome: "Ana" (PHP 8+) permitem saltar parâmetros. strlen(...) cria callable de primeira classe.
Funções Anónimas e Closures
// Closure com estado
function contador(): Closure {
$count = 0;
return function() use (&$count) {
return ++$count;
};
}
$next = contador();
$next(); // 1
$next(); // 2
// IIFE (Immediately Invoked)
$result = (function() {
$x = calcular();
$y = processar($x);
return $y;
})();
// Closure::fromCallable (PHP 7.1+)
$upper = Closure::fromCallable('strtoupper');
echo $upper('olá'); // "OLÁ"Closures capturam variáveis com use. &$var captura por referência (modifica o original). IIFE executa imediatamente. Closure::fromCallable() converte funções em closures. Útil para callbacks e factories.
Union Types e Mixed (PHP 8) 8.0
// Union types
function formatar(int|float $num): string {
return number_format($num, 2);
}
// Nullable shorthand
function encontrar(int $id): ?User {
// retorna User ou null
}
// mixed (qualquer tipo)
function debug(mixed $valor): void {
var_dump($valor);
}
// never (nunca retorna)
function abortar(string $msg): never {
throw new RuntimeException($msg);
}Union types int|float aceitam múltiplos tipos. ?Type é atalho para Type|null. mixed aceita qualquer tipo. never (PHP 8.1) indica que a função nunca retorna (lança exceção ou termina).
Callbacks e Higher-Order
// Função como argumento
function aplicar(callable $fn, array $arr): array {
return array_map($fn, $arr);
}
$result = aplicar(fn($x) => $x * 2, [1, 2, 3]);
// Retornar função
function multiplicador(int $fator): Closure {
return fn($x) => $x * $fator;
}
$triplo = multiplicador(3);
echo $triplo(5); // 15
// call_user_func
call_user_func('strtoupper', 'olá'); // "OLÁ"
call_user_func_array('max', [1, 5, 3]); // 5callable é o type hint para funções/callbacks. array_map(), array_filter(), usort() aceitam callbacks. call_user_func() chama dinamicamente. Funções que retornam funções = higher-order.
First-Class Callable Syntax (PHP 8.1)
// Antes: Closure::fromCallable ou string
$fn = Closure::fromCallable('strlen');
$fn2 = 'strtoupper';
// PHP 8.1+: sintaxe (...)
$len = strlen(...);
echo $len('Olá'); // 3
$upper = strtoupper(...);
$nums = array_map(abs(...), [-1, -2, 3]);
// [1, 2, 3]
// Métodos de objetos
$obj = new MyClass();
$method = $obj->doSomething(...);
$method('arg');
// Métodos estáticos
$static = MyClass::create(...);A sintaxe função(...) (PHP 8.1+) cria um Closure a partir de qualquer callable. Substitui Closure::fromCallable(). Funciona com funções, métodos de objeto ($obj->method(...)) e métodos estáticos (Class::method(...)).
Fibers e Generators 8.0
// Generator (lazy evaluation)
function range_infinito(): Generator {
$i = 0;
while (true) {
yield $i++;
}
}
foreach (range_infinito() as $n) {
if ($n > 5) break;
echo $n; // 0, 1, 2, 3, 4, 5
}
// Fiber (PHP 8.1+)
$fiber = new Fiber(function (): void {
$valor = Fiber::suspend('pausa');
echo "Recebi: $valor";
});
$resultado = $fiber->start(); // 'pausa'
$fiber->resume('dados'); // "Recebi: dados"Generator com yield é lazy — só calcula quando pedido. Ideal para sequências infinitas e ficheiros grandes. Fiber (PHP 8.1+) permite pausar/retomar execução. Base de sistemas assíncronos.
OOP
Classes e Objetos
class User {
public string $nome;
private int $idade;
protected float $saldo = 0;
public function __construct(
string $nome,
int $idade
) {
$this->nome = $nome;
$this->idade = $idade;
}
public function saudar(): string {
return "Olá, {$this->nome}";
}
}
$user = new User("Ana", 30);
echo $user->saudar();public acessível de fora. private só dentro da classe. protected classe + filhas. $this referencia a instância. __construct() é o construtor. new cria instâncias.
Constructor Promotion (PHP 8)
// Antes (PHP 7)
class UserOld {
private string $nome;
private int $idade;
public function __construct(string $nome, int $idade) {
$this->nome = $nome;
$this->idade = $idade;
}
}
// Agora (PHP 8+)
class User {
public function __construct(
private string $nome,
private int $idade,
private bool $ativo = true,
) {}
public function getNome(): string {
return $this->nome;
}
}Constructor Promotion (PHP 8+) declara e atribui propriedades direto no construtor. private string $nome no parâmetro cria a propriedade automaticamente. Reduz boilerplate significativamente.
Namespaces e Autoload
// src/Models/User.php
namespace App\Models;
class User {
public function __construct(
private string $nome
) {}
}
// index.php
require 'vendor/autoload.php';
use App\Models\User;
use App\Services\{EmailService, LogService};
$user = new User("Ana");
// Alias
use App\Models\User as AppUser;
use function App\Helpers\formatar;
use const App\Config\MAX_SIZE;namespace organiza classes em pacotes. use importa classes. autoload (Composer) carrega classes automaticamente. use function e use const importam funções/constantes. PSR-4: 1 classe = 1 ficheiro.
Final, Visibility e Constants
class Base {
final public function fixo(): void {}
// Não pode ser overridden
}
final class Imutavel {
// Não pode ser extendida
}
class Config {
// Visibility em constantes (PHP 7.1+)
public const VERSAO = '2.0';
protected const DB_HOST = 'localhost';
private const SECRET = 'abc123';
// Constante com expressão (PHP 8.3+)
// const TOTAL = self::PRECO * 1.23;
}
// Acesso
echo Config::VERSAO; // "2.0"final impede override de método ou extensão de classe. Constantes de classe podem ter visibilidade (public, protected, private) desde PHP 7.1. Aceda com Classe::CONST. PHP 8.3 permite final em constantes.
Intersection Types (PHP 8.1) 8.0
interface HasNome {
public function getNome(): string;
}
interface HasEmail {
public function getEmail(): string;
}
class Contacto implements HasNome, HasEmail {
public function getNome(): string { return 'Ana'; }
public function getEmail(): string { return 'ana@mail.com'; }
}
// Intersection: deve implementar AMBAS
function notificar(HasNome&HasEmail $c): void {
echo "{$c->getNome()}: {$c->getEmail()}";
}
notificar(new Contacto()); // OK
// DNF types (PHP 8.2): combinação de union + intersection
function processar((HasNome&HasEmail)|null $c): void {}Intersection types A&B exigem que o valor implemente TODAS as interfaces. Diferente de union A|B (uma ou outra). PHP 8.2 introduz DNF types: (A&B)|C combina intersection dentro de union com parênteses.
Herança e Polimorfismo
class Animal {
public function __construct(
protected string $nome
) {}
public function som(): string {
return "...";
}
}
class Cao extends Animal {
public function som(): string {
return "{$this->nome}: Au au!";
}
}
class Gato extends Animal {
public function som(): string {
return "{$this->nome}: Miau!";
}
}
// Polimorfismo
$animais = [new Cao("Rex"), new Gato("Felix")];
foreach ($animais as $a) {
echo $a->som(); // chama o método correto
}extends cria herança. A classe filha herda métodos e pode sobrescrever (override). parent::metodo() chama o método da classe pai. Polimorfismo: mesma interface, comportamentos diferentes.
Enums (PHP 8.1+)
enum Status: string {
case Ativo = 'ativo';
case Inativo = 'inativo';
case Pendente = 'pendente';
public function label(): string {
return match($this) {
self::Ativo => '✅ Ativo',
self::Inativo => '❌ Inativo',
self::Pendente => '⏳ Pendente',
};
}
}
$status = Status::Ativo;
echo $status->value; // "ativo"
echo $status->label(); // "✅ Ativo"
// Comparação
$status === Status::Ativo; // trueenum (PHP 8.1+) define conjuntos fixos de valores. : string ou : int para backed enums. ->value dá o valor. Podem ter métodos. Ideais para estados, tipos e categorias fixas.
Dependency Injection
// Sem DI (acoplado)
class OrderService {
private $db = new Database(); // ruim!
}
// Com DI (desacoplado)
class OrderService {
public function __construct(
private DatabaseInterface $db,
private LoggerInterface $logger,
) {}
public function criar(Order $order): void {
$this->db->save($order);
$this->logger->info("Order criada");
}
}
// Injeção manual
$service = new OrderService(
new MySQLDatabase(),
new FileLogger()
);Dependency Injection: receba dependências no construtor em vez de criar dentro. Programe para interfaces, não implementações. Facilita testes (mocks) e troca de implementações. Base de SOLID.
Readonly Properties (PHP 8.1+) 8.0
class Point {
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
}
$p = new Point(1.5, 2.5);
echo $p->x; // 1.5
$p->x = 3.0; // Error! Cannot modify readonly
// Readonly class (PHP 8.2+)
readonly class Config {
public function __construct(
public string $host,
public int $port,
) {}
}readonly (PHP 8.1+) permite escrever só uma vez. Após atribuição, não pode ser modificado. Ideal para Value Objects e DTOs. readonly class (PHP 8.2+) aplica a todas as propriedades.
Interfaces e Abstract
interface Pagavel {
public function pagar(float $valor): bool;
public function getMetodo(): string;
}
abstract class PagamentoBase implements Pagavel {
abstract protected function validar(): bool;
public function pagar(float $valor): bool {
if (!$this->validar()) return false;
// processar...
return true;
}
}
class CartaoCredito extends PagamentoBase {
protected function validar(): bool {
return true;
}
public function getMetodo(): string {
return "Cartão";
}
}interface define contrato (só assinaturas). implements cumpre o contrato. abstract class pode ter métodos com corpo. abstract method obriga implementação na filha. Uma classe pode implementar múltiplas interfaces.
Static e Constants
class Config {
const MAX_UPLOAD = 10_485_760; // 10MB
private static array $cache = [];
public static function get(string $key): mixed {
return self::$cache[$key] ?? null;
}
public static function set(string $key, mixed $val): void {
self::$cache[$key] = $val;
}
}
// Uso sem instância
Config::set('debug', true);
Config::get('debug'); // true
Config::MAX_UPLOAD; // 10485760static pertence à classe (não à instância). Acede com Classe::metodo(). self:: referencia a própria classe. const define constantes de classe. Útil para singletons, factories e utilitários.
Late Static Binding
class Animal {
public static function criar(): static {
return new static(); // classe chamada
}
public function tipo(): string {
return static::class; // classe real
}
}
class Cao extends Animal {}
$cao = Cao::criar();
echo get_class($cao); // "Cao" (não "Animal")
echo $cao->tipo(); // "Cao"
// self vs static
// self:: → classe onde está definido
// static:: → classe que foi chamadastatic:: (Late Static Binding) refere-se à classe chamada, não à definida. self:: refere-se sempre à classe onde o método está. new static() cria instância da classe filha. Essencial para factories e herança.
Attributes (PHP 8) 8.0
// Definir attribute
#[Attribute(Attribute::TARGET_CLASS)]
class Route {
public function __construct(
public string $path,
public string $method = 'GET',
) {}
}
// Usar
#[Route('/users', method: 'POST')]
class UserController {
public function store() {}
}
// Ler via Reflection
$ref = new ReflectionClass(UserController::class);
$attrs = $ref->getAttributes(Route::class);
$route = $attrs[0]->newInstance();
echo $route->path; // "/users"#[Attribute] (PHP 8+) substitui annotations em comentários. Metadados nativos legíveis via Reflection. Usados em frameworks (Symfony, Laravel) para rotas, validação, injeção de dependências.
Traits
trait Loggable {
public function log(string $msg): void {
echo "[" . static::class . "] $msg\n";
}
}
trait Timestampable {
public ?DateTime $criadoEm = null;
public function marcarCriado(): void {
$this->criadoEm = new DateTime();
}
}
class Post {
use Loggable, Timestampable;
public function __construct(
public string $titulo
) {
$this->marcarCriado();
$this->log("Post criado");
}
}trait partilha código entre classes sem herança. use TraitName dentro da classe. Múltiplas traits com use A, B. Resolve "herança múltipla" do PHP. static::class dá o nome da classe atual.
Magic Methods
class Product {
private array $data = [];
public function __get(string $key): mixed {
return $this->data[$key] ?? null;
}
public function __set(string $key, mixed $val): void {
$this->data[$key] = $val;
}
public function __isset(string $key): bool {
return isset($this->data[$key]);
}
public function __toString(): string {
return json_encode($this->data);
}
public function __invoke(): void {
echo "Chamado como função";
}
}
$p = new Product();
$p->nome = "Teclado"; // __set
echo $p->nome; // __get
echo $p; // __toStringMagic methods começam com __. __get()/__set() para propriedades dinâmicas. __toString() quando usado como string. __invoke() quando chamado como função. __construct(), __destruct() para ciclo de vida.
Object Cloning e __clone
class Carrinho {
public array $itens = [];
public function __construct(
public string $cliente
) {}
public function __clone(): void {
// Deep copy dos itens
$this->itens = array_map(
fn($i) => clone $i,
$this->itens
);
}
}
$original = new Carrinho('Ana');
$original->itens = [new Item('Livro')];
$copia = clone $original;
$copia->cliente = 'Bruno';
// $original->cliente continua "Ana"clone cria cópia rasa do objeto. __clone() é chamado após a cópia — use para deep copy de objetos internos. Sem clone, a atribuição $b = $a copia a referência, não o objeto.
Enums Avançados (PHP 8.1) 8.0
// Backed enum (com valores)
enum Status: string {
case Ativo = 'ativo';
case Inativo = 'inativo';
case Suspenso = 'suspenso';
public function label(): string {
return match($this) {
self::Ativo => 'Ativo ✅',
self::Inativo => 'Inativo ❌',
self::Suspenso => 'Suspenso ⏸',
};
}
public function podeEditar(): bool {
return $this === self::Ativo;
}
}
$status = Status::from('ativo');
$status->value; // 'ativo'
$status->name; // 'Ativo'
$status->label(); // 'Ativo ✅'
Status::tryFrom('x'); // null (sem erro)Enums com : string ou : int são backed (têm valor). from() converte de valor (lança erro se inválido). tryFrom() retorna null. Podem ter métodos e usar match. Substituem constantes mágicas.
Ficheiros e JSON
Ler e Escrever Ficheiros
// Ler tudo de uma vez
$conteudo = file_get_contents('dados.txt');
// Escrever (sobrescreve)
file_put_contents('output.txt', 'Olá!');
// Append (adicionar ao fim)
file_put_contents('log.txt', "linha\n", FILE_APPEND);
// Ler como array de linhas
$linhas = file('dados.txt', FILE_IGNORE_NEW_LINES);
// Verificar existência
if (file_exists('config.ini')) {
$ini = parse_ini_file('config.ini');
}file_get_contents() lê tudo. file_put_contents() escreve. FILE_APPEND adiciona sem sobrescrever. file() retorna array de linhas. file_exists() verifica antes de ler.
SplFileObject e Streams
// Ler linha a linha (eficiente)
$file = new SplFileObject('grande.csv');
$file->setFlags(SplFileObject::READ_CSV);
foreach ($file as $linha) {
[$nome, $email] = $linha;
echo "$nome: $email\n";
}
// fopen/fread/fwrite
$fp = fopen('dados.txt', 'r');
while (!feof($fp)) {
$linha = fgets($fp);
}
fclose($fp);
// php://input (raw body)
$body = file_get_contents('php://input');SplFileObject é orientado a objetos e eficiente para ficheiros grandes. fopen()/fgets() para leitura manual. php://input lê o body raw de requests. Sempre feche com fclose().
File Locking e Atomicidade
// Lock exclusivo (escrita)
$fp = fopen('contador.txt', 'c+');
if (flock($fp, LOCK_EX)) {
$val = (int) stream_get_contents($fp);
ftruncate($fp, 0);
rewind($fp);
fwrite($fp, (string)($val + 1));
fflush($fp);
flock($fp, LOCK_UN);
}
fclose($fp);
// Lock partilhado (leitura)
$fp = fopen('dados.txt', 'r');
flock($fp, LOCK_SH);
$conteudo = stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);
// Escrita atómica com rename
file_put_contents('tmp_' . $f, $dados);
rename('tmp_' . $f, $f); // atómicoflock() evita condições de corrida. LOCK_EX = exclusivo (escrita), LOCK_SH = partilhado (leitura). Sempre faça fflush() antes de LOCK_UN. rename() é atómico no mesmo filesystem — ideal para escrita segura.
JSON
// Array → JSON
$dados = ['nome' => 'Ana', 'idade' => 30];
$json = json_encode($dados, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// JSON → Array
$arr = json_decode($json, true); // true = array
$obj = json_decode($json); // stdClass
// Tratar erros
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception(json_last_error_msg());
}
// PHP 8+: named + flags
json_encode($dados, flags: JSON_THROW_ON_ERROR);json_encode() converte para JSON. json_decode($json, true) para array associativo. JSON_PRETTY_PRINT formata. JSON_UNESCAPED_UNICODE preserva acentos. JSON_THROW_ON_ERROR lança exceção em erro.
CSV e Ficheiros Grandes
// Escrever CSV
$fp = fopen('dados.csv', 'w');
fputcsv($fp, ['Nome', 'Email']);
fputcsv($fp, ['Ana', 'ana@mail.com']);
fclose($fp);
// Ler CSV
$fp = fopen('dados.csv', 'r');
$headers = fgetcsv($fp);
while (($row = fgetcsv($fp)) !== false) {
$dados[] = array_combine($headers, $row);
}
fclose($fp);
// Ficheiro grande (linha a linha)
$handle = fopen('gigante.log', 'r');
while (($linha = fgets($handle)) !== false) {
processar($linha); // memória constante
}
fclose($handle);fputcsv() escreve CSV. fgetcsv() lê linha a linha. array_combine() mapeia headers com valores. Para ficheiros grandes, use fgets() em loop — memória constante independente do tamanho.
Manipulação de Ficheiros
// Criar pasta
mkdir('uploads/2024', 0755, true); // recursivo
// Copiar, mover, apagar
copy('a.txt', 'b.txt');
rename('b.txt', 'c.txt'); // move/renomeia
unlink('c.txt'); // apaga ficheiro
rmdir('pasta-vazia'); // apaga pasta
// Informações
filesize('dados.txt'); // bytes
filemtime('dados.txt'); // última modificação
pathinfo('foto.jpg', PATHINFO_EXTENSION); // "jpg"
// Listar pasta
$ficheiros = glob('uploads/*.jpg');
$todos = scandir('.');mkdir() com true cria recursivamente. rename() move/renomeia. unlink() apaga ficheiros. glob() encontra por padrão. pathinfo() extrai extensão, nome, pasta.
Pathinfo e Manipulação de Paths
$path = '/var/www/uploads/foto.jpg';
pathinfo($path, PATHINFO_DIRNAME); // /var/www/uploads
pathinfo($path, PATHINFO_BASENAME); // foto.jpg
pathinfo($path, PATHINFO_FILENAME); // foto
pathinfo($path, PATHINFO_EXTENSION); // jpg
// Construir paths
$destino = __DIR__ . '/uploads/' . $filename;
// Normalizar
realpath('./../ficheiro.txt');
basename('/caminho/para/file.php'); // file.php
dirname('/caminho/para/file.php'); // /caminho/para
// Temp
$tmp = tempnam(sys_get_temp_dir(), 'prefix_');pathinfo() extrai partes do caminho. realpath() resolve . e ... basename() dá o nome do ficheiro. dirname() dá a pasta. tempnam() cria ficheiro temporário único.
Upload de Ficheiros
// HTML: <form enctype="multipart/form-data">
// <input type="file" name="foto">
if (isset($_FILES['foto'])) {
$f = $_FILES['foto'];
if ($f['error'] !== UPLOAD_ERR_OK) {
die("Erro: " . $f['error']);
}
$ext = pathinfo($f['name'], PATHINFO_EXTENSION);
$permitidas = ['jpg', 'png', 'webp'];
if (!in_array(strtolower($ext), $permitidas)) {
die("Tipo não permitido");
}
$destino = 'uploads/' . uniqid() . '.' . $ext;
move_uploaded_file($f['tmp_name'], $destino);
}$_FILES contém dados do upload. move_uploaded_file() move para o destino final. Sempre valide: error, extensão, tamanho. Use uniqid() para nomes únicos. Nunca confie no nome original.
DirectoryIterator e Glob
// glob: encontrar ficheiros por padrão
$fotos = glob('uploads/*.{jpg,png,webp}', GLOB_BRACE);
$phpFiles = glob(__DIR__ . '/*.php');
// DirectoryIterator (OOP)
$dir = new DirectoryIterator('/var/www');
foreach ($dir as $item) {
if ($item->isDot()) continue;
echo $item->getFilename();
echo $item->getSize();
echo $item->isDir() ? ' [DIR]' : ' [FILE]';
}
// RecursiveDirectoryIterator
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/src')
);
foreach ($it as $file) {
if ($file->getExtension() === 'php') {
echo $file->getPathname();
}
}glob() encontra ficheiros por padrão (suporta *, ?, {a,b}). DirectoryIterator é a forma OOP de listar diretórios. RecursiveDirectoryIterator percorre subdiretórios recursivamente.
Base de Dados e Segurança
PDO — Conexão
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=app;charset=utf8mb4',
'root',
'senha',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
} catch (PDOException $e) {
die("Erro na conexão: " . $e->getMessage());
}PDO suporta MySQL, PostgreSQL, SQLite, etc. ERRMODE_EXCEPTION lança exceções em erros. FETCH_ASSOC retorna arrays associativos. charset=utf8mb4 para Unicode completo.
Sessões e Cookies
// Iniciar sessão
session_start();
// Guardar/ler dados
$_SESSION['user_id'] = 42;
$_SESSION['nome'] = 'Ana';
$userId = $_SESSION['user_id'] ?? null;
// Destruir sessão
session_destroy();
$_SESSION = [];
// Cookies
setcookie('token', $valor, [
'expires' => time() + 86400,
'path' => '/',
'httponly' => true,
'secure' => true,
'samesite' => 'Strict',
]);
$token = $_COOKIE['token'] ?? null;session_start() inicia a sessão (antes de qualquer output). $_SESSION guarda dados no servidor. setcookie() cria cookies no cliente. httponly impede acesso via JS. secure só em HTTPS.
SQLite e PDO Avançado
// SQLite (sem servidor)
$pdo = new PDO('sqlite:dados.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Último ID inserido
$pdo->prepare("INSERT INTO users (nome) VALUES (?)")
->execute(['Ana']);
$id = $pdo->lastInsertId();
// Múltiplas linhas
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch modes
$stmt->fetch(PDO::FETCH_OBJ); // stdClass
$stmt->fetch(PDO::FETCH_COLUMN); // 1ª coluna
$stmt->fetchAll(PDO::FETCH_KEY_PAIR); // id => nome
// quote (evitar — use prepared)
$pdo->quote("O'Brien"); // "O''Brien"sqlite: cria BD em ficheiro (sem servidor). lastInsertId() retorna o ID gerado. fetchAll() retorna todas as linhas. FETCH_KEY_PAIR cria mapa chave→valor. quote() escapa mas prefira sempre prepared statements.
Prepared Statements
// INSERT seguro
$stmt = $pdo->prepare(
"INSERT INTO users (nome, email) VALUES (:nome, :email)"
);
$stmt->execute([
':nome' => $nome,
':email' => $email,
]);
$id = $pdo->lastInsertId();
// SELECT com parâmetros
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();
// Múltiplos resultados
$stmt = $pdo->query("SELECT * FROM users");
$users = $stmt->fetchAll();prepare() + execute() previne SQL Injection. Use placeholders :nome ou ?. fetch() retorna 1 linha. fetchAll() retorna todas. NUNCA concatene variáveis na query!
PDO — UPDATE e DELETE
// UPDATE
$stmt = $pdo->prepare(
"UPDATE users SET nome = :nome, email = :email WHERE id = :id"
);
$stmt->execute([
':nome' => $novoNome,
':email' => $novoEmail,
':id' => $userId,
]);
echo $stmt->rowCount(); // linhas afetadas
// DELETE
$stmt = $pdo->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$userId]);
// Transação
$pdo->beginTransaction();
try {
$pdo->exec("UPDATE contas SET saldo = saldo - 100 WHERE id = 1");
$pdo->exec("UPDATE contas SET saldo = saldo + 100 WHERE id = 2");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
}rowCount() retorna linhas afetadas. beginTransaction()/commit()/rollBack() para transações atómicas. Se algo falhar, rollBack() desfaz tudo. Essencial para operações que dependem umas das outras.
Segurança — Hash e Escape
// Hash de passwords
$hash = password_hash($senha, PASSWORD_DEFAULT);
// Verificar password
if (password_verify($input, $hash)) {
echo "Senha correta";
}
// Rehash se necessário
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$novoHash = password_hash($senha, PASSWORD_DEFAULT);
}
// Escapar output (XSS)
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
// CSRF token
$token = bin2hex(random_bytes(32));
$_SESSION['csrf'] = $token;password_hash() cria hash seguro (bcrypt). password_verify() compara. htmlspecialchars() previne XSS. random_bytes() gera tokens seguros. Nunca use md5() ou sha1() para passwords!
Headers de Segurança
// Headers HTTP de segurança
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block');
header('Content-Security-Policy: default-src \'self\'');
header('Strict-Transport-Security: max-age=31536000');
header('Referrer-Policy: strict-origin-when-cross-origin');
// Cache
header('Cache-Control: no-store, no-cache');
header('Pragma: no-cache');
// Content type
header('Content-Type: application/json; charset=utf-8');
echo json_encode($dados);Headers de segurança: X-Frame-Options impede clickjacking. CSP controla recursos permitidos. HSTS força HTTPS. X-Content-Type-Options impede MIME sniffing. Sempre defina Content-Type correto.
Sanitização e Validação
// filter_var
$email = filter_var($input, FILTER_SANITIZE_EMAIL);
$url = filter_var($input, FILTER_SANITIZE_URL);
$int = filter_var($input, FILTER_VALIDATE_INT);
$emailOk = filter_var($input, FILTER_VALIDATE_EMAIL);
// filter_input (direto do request)
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
// Validação manual
$nome = trim($_POST['nome'] ?? '');
if (mb_strlen($nome) < 2 || mb_strlen($nome) > 100) {
$erros[] = "Nome inválido";
}
// htmlspecialchars no output
echo htmlspecialchars($nome, ENT_QUOTES, 'UTF-8');filter_var() sanitiza/valida dados. FILTER_VALIDATE_* retorna false se inválido. FILTER_SANITIZE_* limpa caracteres. Sempre valide no servidor — nunca confie no cliente.
Transações PDO
try {
$pdo->beginTransaction();
$pdo->prepare("UPDATE contas SET saldo = saldo - ? WHERE id = ?")
->execute([100, $deOrigem]);
$pdo->prepare("UPDATE contas SET saldo = saldo + ? WHERE id = ?")
->execute([100, $paraDestino]);
$pdo->prepare("INSERT INTO transferencias (de, para, valor) VALUES (?, ?, ?)")
->execute([$deOrigem, $paraDestino, 100]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}beginTransaction() inicia transação. commit() confirma tudo. rollBack() desfaz tudo se algo falhar. Essencial para operações que dependem umas das outras (transferências, encomendas). Sempre use try/catch com rollBack().
Números e Datas
Funções Matemáticas
abs(-5); // 5 max(1, 5, 3); // 5 min(1, 5, 3); // 1 round(3.7); // 4 floor(3.7); // 3 ceil(3.2); // 4 round(3.14159, 2); // 3.14 rand(1, 100); // aleatório 1-100 random_int(1, 100); // criptograficamente seguro pow(2, 10); // 1024 sqrt(144); // 12 fmod(10, 3); // 1 (resto float) intdiv(10, 3); // 3 (divisão inteira)
round() arredonda. floor() para baixo, ceil() para cima. random_int() é criptograficamente seguro (prefira a rand()). intdiv() faz divisão inteira. fmod() é o resto para floats.
Funções date() e time()
// date() — formatação rápida
echo date('d/m/Y'); // 25/12/2024
echo date('H:i:s'); // 14:30:00
echo date('Y-m-d H:i:s'); // 2024-12-25 14:30:00
echo date('l'); // Wednesday
echo date('F'); // December
// time() — timestamp atual
$ts = time();
echo date('d/m/Y', $ts + 86400); // amanhã
// strtotime() — string → timestamp
$ts = strtotime('+1 week');
$ts = strtotime('next monday');
$ts = strtotime('2024-12-25');
// Fuso horário
date_default_timezone_set('Europe/Lisbon');date() formata timestamps. time() dá o timestamp atual. strtotime() converte strings em timestamps. date_default_timezone_set() define o fuso. Formatos: d (dia), m (mês), Y (ano), H (hora).
Conversão de Bases e Hex
// Decimal → outras bases
decbin(42); // "101010"
decoct(42); // "52"
dechex(255); // "ff"
// Outras bases → decimal
bindec('101010'); // 42
octdec('52'); // 42
hexdec('ff'); // 255
// base_convert (qualquer base 2-36)
base_convert('ff', 16, 2); // "11111111"
base_convert('255', 10, 16); // "ff"
// Literais em código
$bin = 0b1010; // 10
$oct = 0755; // 493
$hex = 0xFF; // 255
// Cores
$cor = dechex(16711680); // "ff0000"decbin(), decoct(), dechex() convertem decimal para binário/octal/hex. bindec(), octdec(), hexdec() fazem o inverso. base_convert() converte entre bases 2-36. Literais: 0b binário, 0x hex, 0 octal.
Formatação de Números
// number_format
number_format(1234567.891, 2, ',', '.');
// "1.234.567,89"
// sprintf
sprintf("%.2f", 19.9); // "19.90"
sprintf("%05d", 42); // "00042"
sprintf("%x", 255); // "ff" (hex)
sprintf("%b", 10); // "1010" (binário)
// Conversões
intval("42px"); // 42
floatval("3.14"); // 3.14
(int) "42"; // 42
(float) "3.14"; // 3.14
// Verificar
is_numeric("42"); // true
is_nan(acos(2)); // true
is_infinite(INF); // truenumber_format() formata com separadores. sprintf() para formatação avançada. intval()/floatval() convertem. is_numeric() verifica se é número ou string numérica.
Carbon-like sem Dependências
// Helpers de data úteis
function hoje(): string {
return date('Y-m-d');
}
function amanha(): string {
return date('Y-m-d', strtotime('+1 day'));
}
function diasEntre(string $de, string $ate): int {
$d1 = new DateTime($de);
$d2 = new DateTime($ate);
return (int) $d1->diff($d2)->format('%r%a');
}
function formatarPt(DateTime $dt): string {
$meses = ['', 'janeiro', 'fevereiro', 'março',
'abril', 'maio', 'junho', 'julho',
'agosto', 'setembro', 'outubro',
'novembro', 'dezembro'];
return $dt->format('d') . ' de ' .
$meses[(int)$dt->format('n')] .
' de ' . $dt->format('Y');
}Funções utilitárias de data sem dependências externas. strtotime() para cálculos rápidos. DateTime::diff() para diferenças precisas. %r%a dá dias com sinal. Para projetos reais, considere a biblioteca Carbon.
DateTime
// Data atual
$agora = new DateTime();
echo $agora->format('d/m/Y H:i:s');
// Data específica
$natal = new DateTime('2024-12-25');
$custom = DateTime::createFromFormat('d/m/Y', '25/12/2024');
// Modificar
$agora->modify('+1 week');
$agora->add(new DateInterval('P1M')); // +1 mês
$agora->sub(new DateInterval('P2D')); // -2 dias
// Comparar
if ($natal > $agora) {
echo "Natal ainda não chegou";
}
// Timestamp
$ts = $agora->getTimestamp();
$dt = (new DateTime())->setTimestamp($ts);DateTime é a forma moderna de trabalhar com datas. format() formata a saída. modify() aceita strings como +1 week. DateInterval para intervalos precisos. P1M = 1 mês, P2D = 2 dias.
Números Aleatórios e UUID
// Aleatórios seguros
random_int(1, 100); // int seguro
bin2hex(random_bytes(16)); // hex 32 chars
base64_encode(random_bytes(32));
// UUID v4 (sem dependências)
function uuid4(): string {
$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s',
str_split(bin2hex($data), 4));
}
echo uuid4();
// "550e8400-e29b-41d4-a716-446655440000"random_int() e random_bytes() são criptograficamente seguros. rand() e mt_rand() NÃO são seguros para tokens. UUID v4 pode ser gerado com random_bytes() e manipulação de bits.
Diferença entre Datas
$inicio = new DateTime('2024-01-01');
$fim = new DateTime('2024-12-25');
$diff = $inicio->diff($fim);
echo $diff->days; // 359 (total dias)
echo $diff->m; // 11 (meses)
echo $diff->format('%m meses e %d dias');
// Idade
$nascimento = new DateTime('1994-05-15');
$idade = $nascimento->diff(new DateTime())->y;
// Carbon-like (sem dependências)
function diasAte(DateTime $alvo): int {
return (int) (new DateTime())->diff($alvo)->format('%r%a');
}diff() retorna um DateInterval. ->days dá o total de dias. ->y, ->m, ->d dão anos, meses, dias. %r no format mostra sinal negativo se a data já passou.
BCMath — Precisão Arbitrária
// Float tem problemas de precisão
0.1 + 0.2 === 0.3; // false!
// BCMath: strings com precisão exata
bcadd('0.1', '0.2', 10); // '0.3'
bcsub('10', '3.5', 2); // '6.50'
bcmul('1.5', '2.3', 4); // '3.4500'
bcdiv('10', '3', 20); // '3.33333333333333333333'
bcmod('10', '3'); // '1'
bccomp('1.001', '1.0', 3); // 1 (maior)
bcpow('2', '10'); // '1024'
bcsqrt('144', 2); // '12.00'
// Escala global
bcscale(4);
echo bcdiv('1', '3'); // '0.3333'BCMath faz aritmética com precisão arbitrária via strings. Essencial para dinheiro e cálculos científicos. bcscale() define decimais globais. Nunca use float para valores monetários — use bcadd(), bcmul(), etc.
Erros e Debug
Try / Catch / Finally
try {
$resultado = 10 / 0;
} catch (DivisionByZeroError $e) {
echo "Erro: " . $e->getMessage();
} catch (Exception $e) {
echo "Genérico: " . $e->getMessage();
} finally {
echo "Sempre executa";
}
// Múltiplos tipos
try {
// ...
} catch (InvalidArgumentException | RuntimeException $e) {
echo $e->getMessage();
}
// PHP 8: non-capturing catch
try {
// ...
} catch (Exception) {
echo "Algo falhou";
}try executa código arriscado. catch captura exceções específicas. finally executa sempre. PHP 8: catch (Exception) sem variável. Ordene do mais específico ao mais genérico.
Logging e Error Reporting
// error_log para ficheiro
error_log("Erro: " . $e->getMessage(), 3, '/var/log/app.log');
// error_log com contexto
error_log(json_encode([
'level' => 'error',
'message' => $msg,
'context' => ['user_id' => $userId],
'time' => date('c'),
]));
// Configuração por ambiente
// Desenvolvimento:
ini_set('display_errors', '1');
error_reporting(E_ALL);
// Produção:
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php-errors.log');error_log() escreve no log do sistema ou ficheiro. Em desenvolvimento: display_errors = 1. Em produção: display_errors = 0 + log_errors = 1. Nunca mostre erros ao utilizador em produção.
Lançar Exceções
// Lançar exceção
function dividir(int $a, int $b): float {
if ($b === 0) {
throw new InvalidArgumentException(
"Divisor não pode ser zero"
);
}
return $a / $b;
}
// Exceção personalizada
class SaldoInsuficienteException extends Exception {
public function __construct(
private float $saldo,
private float $valor
) {
parent::__construct(
"Saldo {$this->saldo} < {$this->valor}"
);
}
}throw new Exception() lança exceções. Crie classes com extends Exception para erros específicos. Inclua mensagens claras. A exceção propaga até encontrar um catch.
Exceções com Contexto
// Encadear exceções (exception chaining)
try {
$dados = $api->fetch($url);
} catch (HttpException $e) {
throw new ServiceException(
"Falha ao obter dados da API",
previous: $e // PHP 8: named arg
);
}
// Exceção com dados extra
class ValidationException extends Exception {
public function __construct(
private array $errors,
string $message = "Validação falhou"
) {
parent::__construct($message);
}
public function getErrors(): array {
return $this->errors;
}
}Exception chaining: passe a exceção original como previous. getPrevious() acede à causa. Crie exceções com dados extra (ex: $errors). Facilita debugging e logging estruturado.
Error Handling
// Handler global
set_exception_handler(function (Throwable $e) {
error_log($e->getMessage());
echo "Erro interno. Tente novamente.";
});
// Error handler personalizado
set_error_handler(function ($severity, $message, $file, $line) {
throw new ErrorException($message, 0, $severity, $file, $line);
});
// Supressão (evitar!)
$result = @file_get_contents('inexistente.txt');
// error_reporting
error_reporting(E_ALL);
ini_set('display_errors', '0'); // produçãoset_exception_handler() captura exceções não tratadas. set_error_handler() converte erros em exceções. @ suprime erros (evite!). error_reporting(E_ALL) mostra todos. Em produção: log, não mostrar.
Custom Exception Classes
class AppException extends Exception {
public function __construct(
string $message,
private int $httpCode = 500,
?Throwable $previous = null
) {
parent::__construct($message, 0, $previous);
}
public function getHttpCode(): int {
return $this->httpCode;
}
}
class NotFoundException extends AppException {
public function __construct(string $resource, int $id) {
parent::__construct(
"$resource #$id não encontrado",
httpCode: 404
);
}
}
throw new NotFoundException('User', 42);Crie hierarquias de exceções com extends Exception. Adicione contexto (HTTP code, resource, etc.). Facilita catch seletivo e respostas HTTP adequadas. Padrão: uma exceção base por módulo + específicas por caso.
Debug e Var Dump
// var_dump (tipo + valor)
var_dump([1, "a", null, true]);
// print_r (legível)
print_r($array);
// var_export (código PHP válido)
echo var_export($config, true);
// debug_backtrace
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
echo $trace[0]['file'] . ':' . $trace[0]['line'];
// error_log (escreve no log)
error_log("Debug: " . print_r($dados, true));
// dd() (Laravel/Symfony)
// dd($variavel); // dump + dievar_dump() mostra tipo e valor. print_r() é mais legível para arrays. var_export() gera código PHP válido. debug_backtrace() mostra a call stack. error_log() escreve no log sem output.
set_error_handler e Shutdown
// Converter warnings em exceções
set_error_handler(function (
int $severity,
string $message,
string $file,
int $line
): bool {
throw new ErrorException(
$message, 0, $severity, $file, $line
);
});
// Shutdown: captura erros fatais
register_shutdown_function(function () {
$error = error_get_last();
if ($error && in_array($error['type'],
[E_ERROR, E_PARSE, E_CORE_ERROR]
)) {
error_log(json_encode($error));
http_response_code(500);
}
});
// Restaurar handler
restore_error_handler();set_error_handler() converte warnings/notices em exceções. register_shutdown_function() executa no fim — captura erros fatais via error_get_last(). restore_error_handler() restaura o handler anterior.
Truques e Dicas
Operador Null Coalescing (??)
// Básico $nome = $_GET['nome'] ?? 'Visitante'; // Encadeamento $val = $a ?? $b ?? $c ?? 'default'; // Null coalescing assignment (PHP 7.4+) $config['debug'] ??= false; // Só atribui se não existir ou for null // Em arrays aninhados $cidade = $user['endereco']['cidade'] ?? 'N/D'; // Sem erro mesmo se $user['endereco'] não existir! // Combinar com isset $items = $_SESSION['cart'] ?? [];
?? retorna o primeiro valor não-null. Não lança erro em chaves inexistentes. ??= atribui só se null. Substitui padrões como isset($x) ? $x : default. Encadeie para múltiplos fallbacks.
Operadores Bitwise e Ternários
// Bitwise
$a & $b; // AND
$a | $b; // OR
$a ^ $b; // XOR
~$a; // NOT
$a << 2; // shift esquerda (×4)
$a >> 1; // shift direita (÷2)
// Flags com bitwise
define('READ', 1); // 001
define('WRITE', 2); // 010
define('EXEC', 4); // 100
$perms = READ | WRITE; // 3 (011)
$perms & READ; // 1 (tem READ)
$perms & EXEC; // 0 (não tem EXEC)
// Ternário aninhado (evitar!)
$x = $a ?: $b ?? $c; // confusoBitwise: & (AND), | (OR), ^ (XOR), ~ (NOT). << multiplica por 2, >> divide. Útil para flags/permissões. Combine com |, teste com &.
Null-Safe Operator (?->)
// Sem null-safe: verificações em cadeia
$city = null;
if ($user !== null) {
$addr = $user->getAddress();
if ($addr !== null) {
$city = $addr->getCity();
}
}
// Com null-safe (PHP 8+)
$city = $user?->getAddress()?->getCity();
// Se qualquer elo for null, retorna null
$nome = $user?->perfil?->nome ?? 'Anónimo';
// Funciona com métodos e propriedades
$len = $user?->getName()?->length();O operador ?-> (PHP 8+) retorna null se o objeto for null, sem erro. Substitui cadeias de if !== null. Combine com ?? para default. Funciona com propriedades e métodos encadeados.
Spread e Unpacking
// Merge arrays
$a = [1, 2];
$b = [3, 4];
$c = [...$a, ...$b]; // [1, 2, 3, 4]
// Associativo (PHP 8.1+)
$defaults = ['cor' => 'azul', 'size' => 'M'];
$custom = [...$defaults, 'cor' => 'vermelho'];
// ['cor' => 'vermelho', 'size' => 'M']
// Em chamadas de função
function soma($a, $b, $c) { return $a + $b + $c; }
$args = [1, 2, 3];
soma(...$args); // 6
// array_push com spread
$arr = [1, 2];
array_push($arr, ...[3, 4, 5]);Spread ... expande arrays. Em arrays associativos (PHP 8.1+), a última chave ganha. Substitui array_merge() com sintaxe mais limpa. Funciona em chamadas de função e array_push().
list() e Destructuring Avançado
// list() clássico
list($a, $b) = [1, 2];
// Short syntax
[$x, $y] = [10, 20];
// Em foreach
$users = [['Ana', 30], ['Rui', 25]];
foreach ($users as [$nome, $idade]) {
echo "$nome tem $idade";
}
// Ignorar elementos
[, , $terceiro] = [1, 2, 3, 4];
// Associativo (PHP 7.1+)
['nome' => $nome, 'idade' => $idade] = $user;
// Swap
[$a, $b] = [$b, $a];list() ou [] para destructuring. Funciona em foreach para arrays multidimensionais. Vírgula sozinha ignora posições. Destructuring associativo com ['chave' => $var]. Swap elegante: [$a, $b] = [$b, $a].
Short Ternary e Elvis Avançado
// Elvis operator (?:)
$nome = $_POST['nome'] ?: 'Anónimo';
// Equivale a: $_POST['nome'] ? $_POST['nome'] : 'Anónimo'
// vs Null Coalescing (??)
$nome = $_POST['nome'] ?? 'Anónimo';
// ?? só verifica null; ?: verifica falsy
// Diferença prática
$valor = 0;
$valor ?: 10; // 10 (0 é falsy)
$valor ?? 10; // 0 (0 não é null)
// Ternário em expressões
$classe = $ativo ? 'ativo' : 'inativo';
$msg = $erros ? implode(', ', $erros) : 'OK';
// Ternário aninhado (evitar!)
// $x = $a ? 'a' : ($b ? 'b' : 'c');?: (Elvis) retorna o valor se for truthy, senão o default. ?? só verifica null. Diferença: 0 ?: 10 dá 10, mas 0 ?? 10 dá 0. Use ?? para valores opcionais, ?: para truthy/falsy.
Truques com Arrays
// Último elemento $ultimo = end($arr); $ultimo = $arr[array_key_last($arr)]; // Remover duplicados $unico = array_unique([1, 2, 2, 3, 3]); // Achatar array $flat = array_merge(...[[1,2], [3,4]]); // ou $flat = [...[1,2], ...[3,4]]; // array_combine (chaves + valores) $keys = ['a', 'b', 'c']; $vals = [1, 2, 3]; $arr = array_combine($keys, $vals); // array_flip (troca chave ↔ valor) $arr = ['a' => 1, 'b' => 2]; $flip = array_flip($arr); // [1 => 'a', 2 => 'b']
array_key_last() dá a última chave. array_unique() remove duplicados. array_combine() cria array de 2 arrays (chaves + valores). array_flip() troca chaves com valores. Spread ... achata arrays.
Operador @ e Supressão
// Supressão de erros (evitar!)
$valor = @$arr['chave_inexistente'];
$fp = @fopen('inexistente.txt', 'r');
// Alternativas melhores:
$valor = $arr['chave'] ?? null;
if (file_exists('ficheiro.txt')) {
$fp = fopen('ficheiro.txt', 'r');
}
// error_reporting(0) temporário
$old = error_reporting(0);
// ... código que pode dar warning ...
error_reporting($old);O operador @ suprime erros — evite! Torna debugging difícil e esconde bugs. Prefira ?? para chaves inexistentes, file_exists() antes de abrir, ou try/catch. PHP 8: @ não suprime fatal errors.
Novas Funções PHP 8.x 8.0
// str_contains, starts_with, ends_with
str_contains('Olá Mundo', 'Mundo');
str_starts_with('Olá', 'Ol');
str_ends_with('teste.php', '.php');
// array_is_list (PHP 8.1)
array_is_list([1, 2, 3]); // true
array_is_list(['a' => 1]); // false
// enum_exists (PHP 8.1)
enum_exists(Status::class);
// fdiv (divisão sem warning)
fdiv(1, 0); // INF (sem erro!)
// get_debug_type (PHP 8)
get_debug_type(null); // "null"
get_debug_type([]); // "array"
get_debug_type(1.5); // "float"PHP 8+: str_contains(), str_starts_with(), str_ends_with(). PHP 8.1: array_is_list(), enum_exists(), fdiv(). PHP 8: get_debug_type() mostra o tipo de forma legível.
Strings — Truques
// str_contains, starts_with, ends_with (PHP 8+)
str_contains('Olá Mundo', 'Mundo'); // true
str_starts_with('Olá', 'Ol'); // true
str_ends_with('ficheiro.php', '.php'); // true
// str_pad
str_pad('42', 5, '0', STR_PAD_LEFT); // "00042"
// strtr (múltiplas substituições)
strtr('Olá Mundo', ['Olá' => 'Hi', 'Mundo' => 'World']);
// sscanf (extrair com formato)
sscanf('2024-12-25', '%d-%d-%d', $y, $m, $d);
// wordwrap
echo wordwrap($texto, 80, "\n");PHP 8+: str_contains(), str_starts_with(), str_ends_with() — mais legíveis que strpos(). str_pad() preenche. strtr() faz múltiplas substituições de uma vez. sscanf() extrai com formato.
Compact e Extract
// compact: variáveis → array
$nome = 'Ana';
$idade = 30;
$email = 'ana@mail.com';
$user = compact('nome', 'idade', 'email');
// ['nome' => 'Ana', 'idade' => 30, ...]
// extract: array → variáveis (cuidado!)
$dados = ['titulo' => 'Post', 'autor' => 'Ana'];
extract($dados);
echo $titulo; // 'Post'
echo $autor; // 'Ana'
// extract com prefixo (mais seguro)
extract($dados, EXTR_PREFIX_ALL, 'd');
echo $d_titulo; // 'Post'compact() cria array a partir de variáveis. extract() faz o inverso — cuidado: sobrescreve variáveis existentes! Use EXTR_PREFIX_ALL para segurança. Prefira destructuring com [...] a extract().
Novidades PHP 8.2 e 8.3 8.0
// PHP 8.2: readonly classes
readonly class Point {
public function __construct(
public float $x,
public float $y,
) {}
}
// PHP 8.2: constantes em traits
trait HasId {
public const TABLE = 'items';
}
// PHP 8.3: typed class constants
class Config {
final const int MAX = 100;
final const string APP = 'MyApp';
}
// PHP 8.3: json_validate()
json_validate('{"ok":true}'); // true
json_validate('{invalid}'); // false
// PHP 8.3: Randomizer
$rng = new Random\Randomizer();
$rng->getInt(1, 100);
$rng->shuffleArray([1, 2, 3]);PHP 8.2: readonly class, constantes em traits, DNF types. PHP 8.3: final e tipos em constantes, json_validate() sem decode, Random\Randomizer para aleatoriedade OOP, #[\Override] attribute.