Dia 8 — Funções avançadas e organização: PHP e MySQL

Informatica · Conteudo · publicado em 02/10/2026
Dia 8 de 17

Funções avançadas e organização

Aula 1

Closures e arrow function

Closures e arrow function

Closure é uma função sem nome, guardada em variável. Como toda função, ela tem escopo próprio e não enxerga as variáveis de fora — a não ser que peça explicitamente com use. A arrow function (fn) é a mesma coisa com a sintaxe de uma linha, que captura as variáveis de fora sozinha.

As duas formas

$dobro = function (int $n): int { return $n * 2; };

$fator = 10;
$comFator = fn (int $n): int => $n * $fator;   // $fator capturado automaticamente
FormaDeclaraCaptura de foraVários comandos
function anônimafunction (int $n): int { return $n * 2; }só com usesim
Arrow functionfn (int $n): int => $n * 2automática, por valornão, é uma expressão só

A arrow function tem o corpo em uma expressão, terminada em =>. Ela não aceita {}, então não cabe if, foreach nem várias linhas — quando a lógica passa disso, é closure.

O use e o escopo léxico

$taxa = 0.15;
$comTaxa = function (float $v) use ($taxa): float { return $v * (1 + $taxa); };
  • use ($taxa) captura por valor: a closure enxerga o valor que a variável tinha no momento da criação. Mudar $taxa depois não afeta a closure.
  • use (&$taxa) captura por referência: a closure enxerga sempre o valor atual, e escrever nela altera a variável de fora. É assim que se faz contador dentro de closure.
  • Quem escreve use é quem decide a visibilidade. O que não entrar no use simplesmente não existe na função.
$contador = 0;
$incrementar = function () use (&$contador): void { $contador++; };
$incrementar(); $incrementar(); $incrementar();   // $contador vale 3

Onde cada uma aparece

SituaçãoUse
Callback de uma linhafn ($v) => ...
Callback com mais de um passofunction ($v) { ... }
Precisa enxergar variável de foraarrow function, ou use
Precisa modificar variável de forafunction () use (&$x) — arrow function não tem &
Precisa devolver referênciafunction &() { } — arrow function não devolve por referência

Arrow function sempre captura por valor, e o fn não aceita use. Para a linha média dos dois, escreva a arrow function com o nome dentro do corpo: fn (int $n): int => $lista[$n] * $fator, ou passe a referência como parâmetro de outra função.

Exemplo

<?php
// ============================================================
// AULA 1 - Closures e arrow function
// ============================================================

declare(strict_types=1);

// --- Closure: funcao sem nome, guardada em variavel ---
$dobro = function (int $n): int {
    return $n * 2;
};

echo "Closure: " . $dobro(21) . "\n\n";

// --- Arrow function: mesma coisa, corpo em uma expressao ---
$triplo = fn (int $n): int => $n * 3;
echo "Arrow function: " . $triplo(14) . "\n\n";

// --- Escopo lexico: a funcao nao enxerga nada de fora sem o `use` ---
$taxa = 0.15;

// Arrow function captura por valor sozinha, sem `use`.
$comTaxaArrow = fn (float $valor): float => $valor * (1 + $taxa);

// Closure precisa declarar o que quer enxergar.
$comTaxaClosure = function (float $valor) use ($taxa): float {
    return $valor * (1 + $taxa);
};

echo "Arrow com \$taxa capturada:  " . $comTaxaArrow(100.00) . "\n";
echo "Closure com use(\$taxa):     " . $comTaxaClosure(100.00) . "\n\n";

// Captura por VALOR: mudar a variavel depois nao afeta a closure.
$taxa = 0.50;
echo "Depois de mudar \$taxa para 0.50:\n";
echo "  arrow (guardou 0.15):  " . $comTaxaArrow(100.00) . "\n";
echo "  closure (guardou 0.15):" . $comTaxaClosure(100.00) . "\n\n";

// Captura por REFERENCIA com & : a closure enxerga o valor atual.
$contador = 0;
$incrementar = function () use (&$contador): void {
    $contador++;
};

$incrementar();
$incrementar();
$incrementar();
echo "Contador depois de 3 chamadas com use(&): $contador\n\n";

// Arrow function NAO tem &, entao para modificar de fora ela nao serve.
$total = 0;
$somar = fn (int $n): int => $n * 10;
echo "Arrow function devolvendo valor: " . $somar(5) . " (a \$total de fora continua $total)\n\n";

// --- Onde as duas aparecem na pratica: callbacks ---
$notas = [7.5, 4.0, 9.0, 6.5, 8.0];

$bonus = 1.0;
echo "Com bonus de {$bonus}:\n";
echo "  arrow:   " . implode(', ', array_map(fn (float $n): float => min($n + $bonus, 10.0), $notas)) . "\n";

// Com mais de um passo, a closure (com {}) e a que cabe.
echo "  closure: " . implode(', ', array_map(function (float $n) use ($bonus): float {
    $limitado = $n + $bonus;

    return min($limitado, 10.0);
}, $notas)) . "\n\n";

// --- Funcao que devolve uma funcao: a fabrica de callbacks ---
function multiplicador(int $fator): callable
{
    return fn (int $n): int => $n * $fator;
}

$por2 = multiplicador(2);
$por5 = multiplicador(5);
echo "Fabrica de funcoes: por2(10) = " . $por2(10) . ", por5(10) = " . $por5(10) . "\n";
echo "Elas sao funcoes diferentes: " . var_export($por2 === $por5, true) . "\n\n";

// --- Closure ligada a um objeto: o padrao mais comum em aplicacao real ---
class Validador
{
    private array $erros = [];

    public function campo(string $nome, callable $regra, mixed $valor): self
    {
        if (!$regra($valor)) {
            $this->erros[] = $nome;
        }

        return $this;   // permite encadear chamadas
    }

    public function erro(): ?string
    {
        return $this->erros === [] ? null : 'invalido: ' . implode(', ', $this->erros);
    }
}

// A regra e uma funcao que recebe o valor do campo. O valor e passado
// como terceiro argumento de campo(), nunca dentro da arrow function.
$erro = (new Validador())
    ->campo('nome', fn (mixed $valor): bool => is_string($valor) && mb_strlen($valor) > 2, 'Ana')
    ->campo('email', fn (mixed $valor): bool => is_string($valor) && str_contains($valor, '@'), '[email protected]')
    ->campo('idade', fn (mixed $valor): bool => is_int($valor) && $valor > 0, 'trinta')
    ->erro();

echo "Validador com cadeia de callbacks: " . ($erro ?? 'todos os campos validos') . "\n";

Saída real

Closure: 42

Arrow function: 42

Arrow com $taxa capturada:  115
Closure com use($taxa):     115

Depois de mudar $taxa para 0.50:
  arrow (guardou 0.15):  115
  closure (guardou 0.15):115

Contador depois de 3 chamadas com use(&): 3

Arrow function devolvendo valor: 50 (a $total de fora continua 0)

Com bonus de 1:
  arrow:   8.5, 5, 10, 7.5, 9
  closure: 8.5, 5, 10, 7.5, 9

Fabrica de funcoes: por2(10) = 20, por5(10) = 50
Elas sao funcoes diferentes: false

Validador com cadeia de callbacks: invalido: idade
Aula 2

Funções internas de string e array

Funções internas de string e array

É o conjunto de funções que mais aparece em código pronto, e nenhuma delas exige laço. A parte de string tem as de busca (strpos, str_contains, str_starts_with), as de recorte (substr, str_pad, str_repeat) e as de expressão regular (preg_match, preg_replace, preg_match_all). A de array tem as agregações e as de comparação.

String: o catálogo curto

FunçãoAssinaturaExemplo
strposstrpos(string $haystack, string $needle, int $offset = 0): int ou falseposição da primeira ocorrência, ou false
strrposstrrpos(string $haystack, string $needle, int $offset = 0): int ou falseposição da última ocorrência
str_containsstr_contains(string $haystack, string $needle): boolstr_contains($s, '@')
str_starts_withstr_starts_with(string $haystack, string $needle): bool
str_ends_withstr_ends_with(string $haystack, string $needle): bool
substrsubstr(string $string, int $offset, ?int $length = null): stringsubstr($s, 0, 10), substr($s, -4)
str_padstr_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT): stringcompletar com zeros à esquerda
str_repeatstr_repeat(string $string, int $times): string
explodeexplode(string $separator, string $string, int $limit = PHP_INT_MAX): arraycortar string em array
implodeimplode(string $separator, array $array): stringjuntar array em string
str_word_countstr_word_count(string $string, int $format = 0, ?string $characters = null): int ou arrayconta palavras
similar_textsimilar_text(string $string1, string $string2, float &$percent = null): intquão parecidas são duas strings
levenshteinlevenshtein(string $string1, string $string2): intdistância: quantos caracteres trocar

Expressão regular com preg

preg_* trabalha com padrão (a regra) e subject (o texto). O padrão vai entre barras, com modificadores depois.

FunçãoAssinaturaDevolve
preg_matchpreg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int ou false1 se casou, 0 se não
preg_match_allpreg_match_all(string $pattern, string $subject, array &$matches = null, int $flags = PREG_PATTERN_ORDER, int $offset = 0): int ou falsequantas vezes casou
preg_replacepreg_replace($pattern, $replacement, $subject, int $limit = -1, int &$count = null)texto com as trocas feitas; padrão, troca e texto aceitam string ou array
preg_splitpreg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array ou falsefatia por padrão
preg_quotepreg_quote(string $string, ?string $delimiter = null): stringescapa os caracteres especiais

O que o preg_match entrega

preg_match devolve 1 se casou e 0 se não, e escreve o que casou na variável passada por referência: $matches[0] é o caso inteiro e $matches[1], $matches[2] são os grupos entre parênteses. preg_match_all devolve quantas vezes casou e preenche $matches[0] com todos os casos e $matches[1] com todos os primeiros grupos.

ModificadorO que faz
iignora maiúsculas e minúsculas
m^ e $ passam a valer no início e no fim de cada linha
utrata o padrão e o texto como UTF-8
so . passa a casar com quebra de linha
xignora espaços em branco no padrão

preg_match devolve int, não bool: if (preg_match($p, $s)) funciona porque 1 é truthy e 0 é falsy. A exceção é o padrão malformado, que devolve false — e false é falsy, então o if não passa. Sem o modificador u, texto com acento casa na posição de byte errada: todo padrão que trabalha com português precisa de /u.

Exemplo

<?php
// ============================================================
// AULA 2 - Funcoes internas de string e array
// ============================================================

declare(strict_types=1);

// --- strpos devolve a POSICAO (ou false); str_contains devolve BOOL ---
$email = '[email protected]';
echo "strpos de '@':        " . strpos($email, '@') . "\n";
echo "strrpos de '.':       " . strrpos($email, '.') . " (ultima ocorrencia)\n";
echo "strpos ausente:       " . var_export(strpos($email, '#'), true) . "\n";
echo "str_contains '@':     " . var_export(str_contains($email, '@'), true) . "\n";
echo "str_starts_with 'co': " . var_export(str_starts_with($email, 'co'), true) . "\n";
echo "str_ends_with '.br':  " . var_export(str_ends_with($email, '.br'), true) . "\n\n";

// --- substr recorta: inicio positivo, tamanho, ou negativo do fim ---
$cpf = '123.456.789-09';
echo "CPF: $cpf\n";
echo "  substr(0, 3):   " . substr($cpf, 0, 3) . "\n";
echo "  substr(4, 3):   " . substr($cpf, 4, 3) . "\n";
echo "  substr(-2):     " . substr($cpf, -2) . "\n";
echo "  so os digitos:  " . str_replace('.', '', substr($cpf, 0, -2)) . "\n\n";

// --- explode e implode: a ponte entre string e array ---
$csv = 'Ana;Bruno;Carla;Davi';
$nomes = explode(';', $csv);
echo "explode: " . implode(', ', $nomes) . "\n";
echo "count do explode: " . count($nomes) . "\n";
echo "join com quebra: " . implode("\n", $nomes) . "\n";
echo "explode com limite 2: " . json_encode(explode(';', $csv, 2)) . "\n\n";

// --- str_pad completa com zeros; str_repeat repete ---
echo "str_pad a esquerda com zero: " . str_pad('42', 6, '0', STR_PAD_LEFT) . "\n";
echo "str_pad a direita:           " . str_pad('PHP', 8, '.') . "\n";
echo "str_repeat:                 " . str_repeat('=', 20) . "\n\n";

// --- str_word_count e similar_text ---
$texto = 'O cliente pediu o relatorio antes da reuniao de sexta';
echo "str_word_count:   " . str_word_count($texto) . " palavras\n";
similar_text('caderno', 'caderno', $porcentagem);
echo "similar_text (igual): $porcentagem%\n";
similar_text('caderno', 'cadernu', $porcentagem);
echo "similar_text (parecido): " . round($porcentagem, 2) . "%\n";
echo "levenshtein: " . levenshtein('caderno', 'cadernu') . " trocas de caractere\n\n";

// --- preg_match: casa ou nao, e guarda o que casou ---
$cpfLimpo = '12345678909';
$padraoCpf = '/^(\d{3})(\d{3})(\d{3})(\d{2})$/';

echo "preg_match (casou): " . preg_match($padraoCpf, $cpfLimpo, $m) . "\n";
echo "  match inteiro: {$m[0]}\n";
echo "  grupos:        {$m[1]}.{$m[2]}.{$m[3]}-{$m[4]}\n";

$padraoEmail = '/^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i';
echo "\npreg_match email valido:   " . preg_match($padraoEmail, '[email protected]') . "\n";
echo "preg_match email invalido: " . preg_match($padraoEmail, 'ana@@escola') . "\n";
echo "Com modificador i:         " . preg_match($padraoEmail, '[email protected]') . " (1 = casou)\n";

// Pattern malformado devolve false, e nao 0: o if passa a ser verdadeiro.
// O @ segura o aviso, porque aqui o pattern quebrado e o objeto da aula.
echo "\nPattern malformado: " . var_export(@preg_match('/[abc/', 'texto'), true) . " (false, nao 0)\n\n";

// --- preg_match_all: conta e coleta todas as ocorrencias ---
$frase = 'Pedido #102, orcamento #205 e entrega #389';
preg_match_all('/#(\d+)/', $frase, $achados);

echo "preg_match_all: " . preg_match_all('/#(\d+)/', $frase) . " ocorrencias\n";
echo "  todos:   " . implode(', ', $achados[0]) . "\n";
echo "  grupos:  " . implode(', ', $achados[1]) . "\n\n";

// --- preg_replace troca ---
$telefone = '(11) 98765-4321';
$soDigitos = preg_replace('/\D+/', '', $telefone);
echo "preg_replace para so digitos: $soDigitos\n";

$dataTexto = 'Reuniao em 12/03/2026 as 14h30';
$dataIso = preg_replace('#(\d{2})/(\d{2})/(\d{4})#', '$3-$2-$1', $dataTexto);
echo "Data convertida para ISO: $dataIso\n\n";

// --- preg_split fatia por padrao ---
echo "preg_split por virgula: " . json_encode(preg_split('/\s*,\s*/', 'Ana , Bruno ,Carla')) . "\n";
echo "preg_quote protege:    " . preg_quote('preço (v1.0)', '/') . "\n\n";

// --- Funcoes internas de array: agregacao e comparacao ---
$notas = [8.5, 6.0, 9.5, 7.0];
echo "array_sum:    " . array_sum($notas) . "\n";
echo "array_product: " . array_product([2, 3, 4]) . "\n";
echo "array_diff:   " . json_encode(array_values(array_diff([1, 2, 3, 4], [2, 4]))) . "\n";
echo "array_intersect: " . json_encode(array_values(array_intersect([1, 2, 3, 4], [3, 4, 5]))) . "\n";
echo "in_array:     " . var_export(in_array(9.5, $notas, true), true) . "\n";
echo "array_unique: " . json_encode(array_values(array_unique(['a', 'b', 'a', 'c']))) . "\n";
echo "range:        " . implode(', ', range(1, 10, 2)) . "\n\n";

// compact: monta um array com as variaveis nomeadas e descarta as vazias.
$titulo = 'Fatura de marco';
$subtitulo = '';
$mês = 'março';
echo "compact mantem as preenchidas: " . json_encode(compact('titulo', 'subtitulo', 'mês'), JSON_UNESCAPED_UNICODE) . "\n";

Saída real

strpos de '@':        7
strrpos de '.':       19 (ultima ocorrencia)
strpos ausente:       false
str_contains '@':     true
str_starts_with 'co': true
str_ends_with '.br':  true

CPF: 123.456.789-09
  substr(0, 3):   123
  substr(4, 3):   456
  substr(-2):     09
  so os digitos:  123456789-

explode: Ana, Bruno, Carla, Davi
count do explode: 4
join com quebra: Ana
Bruno
Carla
Davi
explode com limite 2: ["Ana","Bruno;Carla;Davi"]

str_pad a esquerda com zero: 000042
str_pad a direita:           PHP.....
str_repeat:                 ====================

str_word_count:   10 palavras
similar_text (igual): 100%
similar_text (parecido): 85.71%
levenshtein: 1 trocas de caractere

preg_match (casou): 1
  match inteiro: 12345678909
  grupos:        123.456.789-09

preg_match email valido:   1
preg_match email invalido: 0
Com modificador i:         1 (1 = casou)

Pattern malformado: false (false, nao 0)

preg_match_all: 3 ocorrencias
  todos:   #102, #205, #389
  grupos:  102, 205, 389

preg_replace para so digitos: 11987654321
Data convertida para ISO: Reuniao em 2026-03-12 as 14h30

preg_split por virgula: ["Ana","Bruno","Carla"]
preg_quote protege:    preço \(v1\.0\)

array_sum:    31
array_product: 24
array_diff:   [1,3]
array_intersect: [3,4]
in_array:     true
array_unique: ["a","b","c"]
range:        1, 3, 5, 7, 9

compact mantem as preenchidas: {"titulo":"Fatura de marco","subtitulo":"","mês":"março"}
Aula 3

Funções de data e matemática

Funções de data e matemática

Data em PHP tem duas famílias que não se misturam: as funções procedurais (date, time, mktime, checkdate), que recebem e devolvem inteiros, e a classe DateTime, com objeto, métodos e DateInterval. Para cálculo de prazo, idade e diferença entre duas datas, a DateTime é a que se usa. Para matemática, o conjunto é pequeno: arredondar, absoluto e extremos.

A data como objeto

$hoje = new DateTime('2026-09-29 14:30:00');
echo $hoje->format('d/m/Y');              // 29/09/2026
$somar = $hoje->add(new DateInterval('P2D'));
$falta  = (new DateTime('2026-12-31'))->diff($hoje)->days;
MétodoAssinaturaExemplo
formatformat(string $format): stringformat('d/m/Y H:i')
addadd(DateInterval $interval): staticsoma P1D, P2M, P1Y
subsub(DateInterval $interval): staticsubtrai
diffdiff(DateTimeInterface $targetObject, bool $absolute = false): DateIntervaldevolve um DateInterval
modifymodify(string $modifier): staticmodify('+1 week'), modify('last day of this month')
setDatesetDate(int $year, int $month, int $day): static
getTimestampgetTimestamp(): into *timestamp* Unix
createFromFormatDateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime ou falseinterpreta "29/09/2026" com d/m/Y
DateIntervalnew DateInterval(string $spec)'P1Y2M3D', 'P2W', 'PT4H'

O diff devolve um DateInterval, e é dele que saem os campos: ->days, ->y, ->m, ->d, ->h, ->i, ->invert e o método format('%a dias').

Procedurais

FunçãoAssinaturaExemplo
timetime(): inttimestamp atual
datedate(string $format, ?int $timestamp = null): stringdate('d/m/Y')
mktimemktime(int $hour, int $minute, int $second = 0, int $month = 1, int $day = 1, int $year = null): intmonta um timestamp
strtotimestrtotime(string $datetime, ?int $baseTimestamp = null): int ou falseinterpreta texto: '-1 week', 'next monday'
checkdatecheckdate(int $month, int $day, int $year): boolcheckdate(2, 29, 2024) dá true, ano bissexto
mktime com dia 29 de fevereiroano não bissextomktime(0,0,0,2,29,2023) rola para 1º de março

A tabela de formatos do date é a mesma do format: d dia, m mês, Y ano com 4 dígitos, y com 2, H hora 24h, i minuto, s segundo, D dia da semana abreviado, l por extenso, M mês abreviado, F por extenso, j dia sem zero à esquerda, n mês sem zero, w dia da semana em número (0 = domingo).

Matemática

FunçãoAssinaturaExemplo
ceilceil(int ou float $num): floatceil(4.1) = 5.0
floorfloor(int ou float $num): floatfloor(4.9) = 4.0
roundround(int ou float $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): floatround(4.567, 2) = 4.57
absabs($num): int ou floatabs(-10) = 10
maxmax(mixed $value, mixed ...$values): mixedmax($array)
minmin(mixed $value, mixed ...$values): mixedmin($array)
intdivintdiv(int $num1, int $num2): intdivisão inteira, sem real
fdivfdiv(float $num1, float $num2): floatdivisão que não dá erro por zero
number_formatnumber_format(float $num, int $decimals = 0, ?string $decimal_separator = '.', ?string $thousands_separator = ','): stringpadrão brasileiro
randrand(int $min, int $max): intlento, não serve para criptografia
mt_randmt_rand(int $min, int $max): intmais rápido, igual em todos os sistemas
random_intrandom_int(int $min, int $max): intlento, mas criptograficamente seguro

rand e mt_rand não devem ser usados para senha, token ou chave: são previsíveis a partir de algumas saídas. random_int é o que serve para segredo. Para valor monetário, o tipo certo é int de centavos ou float com round — misturar soma de float com exibição gera 0.1 + 0.2 = 0.30000000000000004.

Exemplo

<?php
// ============================================================
// AULA 3 - Funcoes de data e matematica
// ============================================================

declare(strict_types=1);

// --- Data como objeto: DateTime e DateInterval ---
$hoje = new DateTime('2026-09-29 14:30:00');
echo "format d/m/Y H:i: " . $hoje->format('d/m/Y H:i') . "\n";
echo "dia da semana:     " . $hoje->format('l') . "\n";
echo "timestamp Unix:    " . $hoje->getTimestamp() . "\n\n";

// Formatos de data mais usados.
echo "Formatos:\n";
echo "  d/m/Y  -> " . $hoje->format('d/m/Y') . "\n";
echo "  D/m/Y  -> " . $hoje->format('D/m/Y') . "\n";
echo "  Y-m-d  -> " . $hoje->format('Y-m-d') . " (formato de banco e ISO)\n";
echo "  d/m/Y H:i:s -> " . $hoje->format('d/m/Y H:i:s') . "\n";
echo "  j/n/Y (sem zero a esquerda) -> " . $hoje->format('j/n/Y') . "\n\n";

// A ISO 8601 -> P = periodo de data, T = periodo de hora.
$somenteData = new DateInterval('P2D');
$doisMeses = new DateInterval('P2M');
$umaHora = new DateInterval('PT4H');

$entrega = (clone $hoje)->add($somenteData);
echo "Soma de 2 dias:  " . $hoje->format('d/m/Y') . " -> " . $entrega->format('d/m/Y') . "\n";
echo "Soma de 2 meses: " . $hoje->format('d/m/Y') . " -> " . (clone $hoje)->add($doisMeses)->format('d/m/Y') . "\n";
echo "Soma de 4 horas: " . $hoje->format('d/m H:i') . " -> " . (clone $hoje)->add($umaHora)->format('d/m H:i') . "\n\n";

// --- diff entre duas datas ---
$inicio = new DateTime('2026-01-15');
$fim = new DateTime('2026-09-29');
$diferenca = $inicio->diff($fim);

echo "Diff:\n";
echo "  total de dias: " . $diferenca->days . "\n";
echo "  anos/meses/dias: {$diferenca->y} anos, {$diferenca->m} meses, {$diferenca->d} dias\n";
echo "  format %a: " . $diferenca->format('%a dias') . "\n\n";

// --- Idade, o uso mais comum de diff ---
$nascimento = new DateTime('1995-03-12');
$hoje2 = new DateTime('2026-09-29');
$idade = $nascimento->diff($hoje2)->y;
echo "Idade calculada: $idade anos\n\n";

// --- createFromFormat: interpretar data vinda de texto ---
$texto = '15/08/2026';
$data = DateTime::createFromFormat('d/m/Y', $texto);
echo "createFromFormat: " . ($data ? $data->format('Y-m-d') : 'formato invalido') . "\n";
var_dump(DateTime::createFromFormat('d/m/Y', '2026-08-15'));
echo "\n";

// --- checkdate: valida a data e trata ano bissexto ---
echo "checkdate(2, 29, 2024): " . var_export(checkdate(2, 29, 2024), true) . " (2024 e bissexto)\n";
echo "checkdate(2, 29, 2023): " . var_export(checkdate(2, 29, 2023), true) . " (2023 nao e)\n";
echo "checkdate(13, 1, 2026): " . var_export(checkdate(13, 1, 2026), true) . " (mes 13 nao existe)\n\n";

// --- Funcoes procedurais: date, time, mktime, strtotime ---
echo "time():        " . time() . "\n";
echo "date():        " . date('d/m/Y') . "\n";

$timestamp = mktime(9, 30, 0, 12, 25, 2026);
echo "mktime -> date: " . date('d/m/Y H:i', $timestamp) . "\n";

echo "strtotime '+1 week':  " . date('d/m/Y', strtotime('+1 week', $hoje->getTimestamp())) . "\n";
echo "strtotime 'next monday': " . date('d/m/Y', strtotime('next monday', $hoje->getTimestamp())) . "\n";
echo "strtotime '-3 months':  " . date('d/m/Y', strtotime('-3 months', $hoje->getTimestamp())) . "\n\n";

// --- Matematica: arredondar, absoluto, extremos ---
$num = 4.5678;
echo "Arredondar $num:\n";
echo "  round (2 casas): " . round($num, 2) . "\n";
echo "  floor:           " . floor($num) . "\n";
echo "  ceil:            " . ceil($num) . "\n";
echo "  abs de -42.7:    " . abs(-42.7) . "\n\n";

$notas = [7.5, 9.0, 4.25, 8.75];
echo "max: " . max($notas) . ", min: " . min($notas) . "\n";
echo "intdiv(10, 3): " . intdiv(10, 3) . " (divisao inteira)\n";
echo "fdiv(10, 3):   " . fdiv(10.0, 3.0) . " (com real)\n\n";

// --- number_format no padrao brasileiro ---
$valor = 1234.5678;
echo "number_format: " . number_format($valor, 2, ',', '.') . "\n";
echo "Com 3 casas:   " . number_format($valor, 3, ',', '.') . "\n\n";

// --- O detalhe do float: dinheiro nunca se soma em float ---
echo "0.1 + 0.2 = " . (0.1 + 0.2) . " (float: erro de representacao)\n";
echo "Em centavos (int): " . ((10 + 20) / 100) . " (int: exato)\n\n";

// --- Sorteio: random_int e seguro, rand e mt_rand nao sao ---
echo "random_int(1, 6): " . random_int(1, 6) . " (criptograficamente seguro)\n";
echo "mt_rand(1, 6):    " . mt_rand(1, 6) . " (rapido, previsivel)\n";
echo "rand(1, 6):       " . rand(1, 6) . " (o mais lento dos tres)\n";

Saída real

format d/m/Y H:i: 29/09/2026 14:30
dia da semana:     Tuesday
timestamp Unix:    1790692200

Formatos:
  d/m/Y  -> 29/09/2026
  D/m/Y  -> Tue/09/2026
  Y-m-d  -> 2026-09-29 (formato de banco e ISO)
  d/m/Y H:i:s -> 29/09/2026 14:30:00
  j/n/Y (sem zero a esquerda) -> 29/9/2026

Soma de 2 dias:  29/09/2026 -> 01/10/2026
Soma de 2 meses: 29/09/2026 -> 29/11/2026
Soma de 4 horas: 29/09 14:30 -> 29/09 18:30

Diff:
  total de dias: 257
  anos/meses/dias: 0 anos, 8 meses, 14 dias
  format %a: 257 dias

Idade calculada: 31 anos

createFromFormat: 2026-08-15
bool(false)

checkdate(2, 29, 2024): true (2024 e bissexto)
checkdate(2, 29, 2023): false (2023 nao e)
checkdate(13, 1, 2026): false (mes 13 nao existe)

time():        1790959977
date():        02/10/2026
mktime -> date: 25/12/2026 09:30
strtotime '+1 week':  06/10/2026
strtotime 'next monday': 05/10/2026
strtotime '-3 months':  29/06/2026

Arredondar 4.5678:
  round (2 casas): 4.57
  floor:           4
  ceil:            5
  abs de -42.7:    42.7

max: 9, min: 4.25
intdiv(10, 3): 3 (divisao inteira)
fdiv(10, 3):   3.3333333333333 (com real)

number_format: 1.234,57
Com 3 casas:   1.234,568

0.1 + 0.2 = 0.3 (float: erro de representacao)
Em centavos (int): 0.3 (int: exato)

random_int(1, 6): 5 (criptograficamente seguro)
mt_rand(1, 6):    3 (rapido, previsivel)
rand(1, 6):       3 (o mais lento dos tres)
Aula 4

Include, require e organização de arquivos

Include, require e organização de arquivos

include e require trazem outro arquivo PHP para dentro do atual, na posição em que aparecem. É o que modulariza o projeto: conexão, funções, cabeçalho, rodapé e views ficam em arquivos separados. As quatro variantes diferem em dois pontos — o que acontece quando o arquivo não existe e se ele pode ser carregado mais de uma vez.

As quatro formas

InstruçãoArquivo faltaCarrega de novo
include 'x.php'avisa e segue a execuçãosim
include_once 'x.php'avisa e segue a execuçãonão, só na primeira vez
require 'x.php'erro fatal, script parasim
require_once 'x.php'erro fatal, script paranão, só na primeira vez

A regra prática: require para o que é obrigatório (conexão com o banco, arquivo de funções, autoload) e include para o que é opcional (um bloco de texto, um template que pode não existir). Com require, o erro de arquivo ausente não passa silencioso: ele mata o script, e é essa a intenção quando a dependência é essencial.

O erro clássico: include dentro de laço

// ERRADO: o arquivo e incluido uma vez por volta do laco
foreach ($paginas as $pagina) {
    include "views/$pagina.php";
}

Com include puro, cada volta do foreach executa o arquivo inteiro outra vez. Com require_once, é executado só na primeira volta, e as chamadas seguintes são ignoradas. require_once e include_once guardam o caminho absoluto do arquivo já carregado, então include 'x.php' e require_once __DIR__ . '/x.php' contam como o mesmo arquivo só se o caminho resolvido for idêntico.

O outro lado do mesmo problema é o require dentro de função: o que foi incluído ali só existe no escopo da função, e a função importada some quando ela termina. Para ter acesso de qualquer lugar, o require fica no topo do arquivo, ou dentro de um arquivo de bootstrap.

Caminhos com __DIR__ e dirname

Caminho relativo depende de onde o script foi chamado, e com chdir ou com o *front controller* isso quebra. A forma estável usa a pasta do arquivo atual:

require_once __DIR__ . '/config.php';
require_once dirname(__DIR__) . '/vendor/autoload.php';
Constante e funçãoDevolve
__DIR__caminho absoluto da pasta do arquivo atual
__FILE__caminho absoluto do arquivo atual
dirname($caminho)a pasta acima
dirname($caminho, 2)duas pastas acima
basename($caminho)só o nome do arquivo
realpath($caminho)caminho absoluto normalizado, ou false se não existir

Com __DIR__, mover o projeto de pasta não quebra nada: o caminho é recalculado em relação ao próprio arquivo.

Como organizar

ArquivoConteúdo
config.phpconstantes de ambiente: host, usuário, nome do banco
funcoes.phpfunções que não pertencem a nenhuma classe
bootstrap.phpo que sempre roda: declare, timezone, sessão, autoload
conexao.phpo new PDO e a devolução da instância
views/arquivos que imprimem HTML, sem lógica de negócio
parcials/cabeçalho, rodapé, menu — incluídos por várias views

Função declarada em dois arquivos incluídos com include (sem once) causa *Cannot redeclare*. A sequência correta é require_once no bootstrap para o que é global, e include dentro da view para o parcial, porque view só é renderizada uma vez por requisição. A falha de configuração em produção é quase sempre a mesma: caminho relativo que funciona em localhost e não funciona no servidor.

Exemplo

<?php
// ============================================================
// AULA 4 - Include, require e organizacao de arquivos
// ============================================================

declare(strict_types=1);

// O exemplo cria um arquivo temporario para poder mostrar o que acontece
// quando o mesmo arquivo e incluido varias vezes. No projeto real, o
// arquivo incluido existe de verdade na pasta do projeto.

// Este e o conteudo do arquivo que sera incluido: cada execucao soma 1.
// As tags sao obrigatorias: sem elas o include escreve o codigo na tela em
// vez de executar.
$arquivo = sys_get_temp_dir() . '/aula8_parcial_' . getmypid() . '.php';
file_put_contents($arquivo, <<<'PARCIAL'
    <?php
    $contador = ($contador ?? 0) + 1;
    echo "      arquivo executado, contador = $contador\n";
PARCIAL);

// --- include puro: executa de novo a cada chamada ---
echo "include duas vezes (executa as duas):\n";
$contador = 0;
include $arquivo;
include $arquivo;
echo "  contador final: $contador\n\n";

// --- include_once: executa so na primeira vez ---
echo "include_once duas vezes (executa so a primeira):\n";
$contador = 0;
include_once $arquivo;
include_once $arquivo;
echo "  contador final: $contador\n\n";

// --- O ERRO CLASSICO: include dentro de laco, sem _once ---
echo "include dentro de foreach, tres voltas (o arquivo roda 3 vezes):\n";
$contador = 0;
foreach (['a', 'b', 'c'] as $letra) {
    include $arquivo;
}
echo "  contador final: $contador\n\n";

echo "require_once dentro de foreach, tres voltas (roda so na 1a):\n";
$contador = 0;
foreach (['a', 'b', 'c'] as $letra) {
    require_once $arquivo;
}
echo "  contador final: $contador\n\n";

// --- include devolve 1 em caso de sucesso e false se o arquivo faltar ---
// (com @ para nao imprimir o aviso na tela)
$carregado = @include $arquivo;
echo "include com arquivo existente devolve: " . var_export($carregado, true) . "\n";
$falhou = @include sys_get_temp_dir() . '/arquivo_que_nao_existe_12345.php';
echo "include com arquivo ausente devolve:  " . var_export($falhou, true) . " (o script segue)\n";
// require com arquivo ausente e erro FATAL e para o script: por isso ele
// nao e executado aqui, senao nada abaixo rodaria.
// require 'arquivo_que_nao_existe.php';
echo "\n";

// O include dentro da funcao roda no escopo LOCAL dela: a variavel criada
// pelo arquivo incluido nao existe para o codigo de fora.
$arquivoFuncao = sys_get_temp_dir() . '/aula8_escopo_' . getmypid() . '.php';
file_put_contents($arquivoFuncao, <<<'ESCOPO'
    <?php
    $definidoDentroDoInclude = 'visivel apenas aqui dentro';
ESCOPO);

function carregarDentroDaFuncao(string $caminho): void
{
    include $caminho;
    echo "  dentro da funcao: " . $definidoDentroDoInclude . "\n";
}

echo "include dentro de funcao:\n";
carregarDentroDaFuncao($arquivoFuncao);
echo "  fora da funcao: " . (isset($definidoDentroDoInclude) ? 'ainda existe' : 'a variavel NAO existe mais') . "\n";
echo "  (por isso funcoes globais vao no topo do arquivo, com require_once)\n\n";

// --- __DIR__, dirname e basename: caminho estavel ---
echo "Caminhos:\n";
echo "  __DIR__:            " . __DIR__ . "\n";
echo "  __FILE__:           " . __FILE__ . "\n";
echo "  dirname(__DIR__):   " . dirname(__DIR__) . "\n";
echo "  basename(__FILE__): " . basename(__FILE__) . "\n";
echo "  realpath(__DIR__):  " . realpath(__DIR__) . "\n";
echo "  realpath de algo inexistente: " . var_export(realpath(__DIR__ . '/nao_existe'), true) . "\n\n";

// Montar o caminho com __DIR__ e o que evita o include funcionar na sua
// maquina e quebrar no servidor, porque o caminho relativo depende de
// onde o script foi chamado.
$arquivoConfig = __DIR__ . '/config.php';
echo "Caminho montado: require_once __DIR__ . '/config.php' => $arquivoConfig\n";
echo "Com dirname para subir: " . dirname(__DIR__) . "/vendor/autoload.php\n\n";

// Limpeza dos arquivos temporarios do exemplo.
unlink($arquivo);
unlink($arquivoFuncao);
echo "Arquivos temporarios do exemplo removidos.\n";

Saída real

include duas vezes (executa as duas):
          arquivo executado, contador = 1
          arquivo executado, contador = 2
  contador final: 2

include_once duas vezes (executa so a primeira):
  contador final: 0

include dentro de foreach, tres voltas (o arquivo roda 3 vezes):
          arquivo executado, contador = 1
          arquivo executado, contador = 2
          arquivo executado, contador = 3
  contador final: 3

require_once dentro de foreach, tres voltas (roda so na 1a):
  contador final: 0

          arquivo executado, contador = 1
include com arquivo existente devolve: 1
include com arquivo ausente devolve:  false (o script segue)

include dentro de funcao:
      dentro da funcao: visivel apenas aqui dentro
  fora da funcao: a variavel NAO existe mais
  (por isso funcoes globais vao no topo do arquivo, com require_once)

Caminhos:
  __DIR__:            /w
  __FILE__:           /w/aula4.php
  dirname(__DIR__):   /
  basename(__FILE__): aula4.php
  realpath(__DIR__):  /w
  realpath de algo inexistente: false

Caminho montado: require_once __DIR__ . '/config.php' => /w/config.php
Com dirname para subir: //vendor/autoload.php

Arquivos temporarios do exemplo removidos.