DevTools

Cheatsheet PHP

Linguagem de programação web server-side

Voltar às linguagens
PHP
124 cards encontrados
Categorias:
Versões:

Básico


13 cards
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);  // false

Variá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


12 cards
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");  // 2

trim() 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 → ISO

Para 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


13 cards
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);  // true

array_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


10 cards
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


12 cards
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]);  // 5

callable é 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


17 cards
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;  // true

enum (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;    // 10485760

static 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 chamada

static:: (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;                // __toString

Magic 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


9 cards
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ómico

flock() 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


9 cards
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


9 cards
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);   // true

number_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


8 cards
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ção

set_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 + die

var_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


12 cards
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;  // confuso

Bitwise: & (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.