Dia 8 — Funções avançadas e organização: PHP e MySQL
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
| Forma | Declara | Captura de fora | Vários comandos |
|---|---|---|---|
function anônima | function (int $n): int { return $n * 2; } | só com use | sim |
| Arrow function | fn (int $n): int => $n * 2 | automática, por valor | nã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$taxadepois 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 nousesimplesmente 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ção | Use |
|---|---|
| Callback de uma linha | fn ($v) => ... |
| Callback com mais de um passo | function ($v) { ... } |
| Precisa enxergar variável de fora | arrow function, ou use |
| Precisa modificar variável de fora | function () use (&$x) — arrow function não tem & |
| Precisa devolver referência | function &() { } — arrow function não devolve por referência |
Arrow function sempre captura por valor, e o
fnnão aceitause. 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
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ção | Assinatura | Exemplo |
|---|---|---|
strpos | strpos(string $haystack, string $needle, int $offset = 0): int ou false | posição da primeira ocorrência, ou false |
strrpos | strrpos(string $haystack, string $needle, int $offset = 0): int ou false | posição da última ocorrência |
str_contains | str_contains(string $haystack, string $needle): bool | str_contains($s, '@') |
str_starts_with | str_starts_with(string $haystack, string $needle): bool | |
str_ends_with | str_ends_with(string $haystack, string $needle): bool | |
substr | substr(string $string, int $offset, ?int $length = null): string | substr($s, 0, 10), substr($s, -4) |
str_pad | str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT): string | completar com zeros à esquerda |
str_repeat | str_repeat(string $string, int $times): string | |
explode | explode(string $separator, string $string, int $limit = PHP_INT_MAX): array | cortar string em array |
implode | implode(string $separator, array $array): string | juntar array em string |
str_word_count | str_word_count(string $string, int $format = 0, ?string $characters = null): int ou array | conta palavras |
similar_text | similar_text(string $string1, string $string2, float &$percent = null): int | quão parecidas são duas strings |
levenshtein | levenshtein(string $string1, string $string2): int | distâ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ção | Assinatura | Devolve |
|---|---|---|
preg_match | preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int ou false | 1 se casou, 0 se não |
preg_match_all | preg_match_all(string $pattern, string $subject, array &$matches = null, int $flags = PREG_PATTERN_ORDER, int $offset = 0): int ou false | quantas vezes casou |
preg_replace | preg_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_split | preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array ou false | fatia por padrão |
preg_quote | preg_quote(string $string, ?string $delimiter = null): string | escapa 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.
| Modificador | O que faz |
|---|---|
i | ignora maiúsculas e minúsculas |
m | ^ e $ passam a valer no início e no fim de cada linha |
u | trata o padrão e o texto como UTF-8 |
s | o . passa a casar com quebra de linha |
x | ignora espaços em branco no padrão |
preg_matchdevolveint, nãobool:if (preg_match($p, $s))funciona porque1é truthy e0é falsy. A exceção é o padrão malformado, que devolvefalse— efalseé falsy, então oifnão passa. Sem o modificadoru, 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"}
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étodo | Assinatura | Exemplo |
|---|---|---|
format | format(string $format): string | format('d/m/Y H:i') |
add | add(DateInterval $interval): static | soma P1D, P2M, P1Y |
sub | sub(DateInterval $interval): static | subtrai |
diff | diff(DateTimeInterface $targetObject, bool $absolute = false): DateInterval | devolve um DateInterval |
modify | modify(string $modifier): static | modify('+1 week'), modify('last day of this month') |
setDate | setDate(int $year, int $month, int $day): static | |
getTimestamp | getTimestamp(): int | o *timestamp* Unix |
createFromFormat | DateTime::createFromFormat(string $format, string $datetime, ?DateTimeZone $timezone = null): DateTime ou false | interpreta "29/09/2026" com d/m/Y |
DateInterval | new 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ção | Assinatura | Exemplo |
|---|---|---|
time | time(): int | timestamp atual |
date | date(string $format, ?int $timestamp = null): string | date('d/m/Y') |
mktime | mktime(int $hour, int $minute, int $second = 0, int $month = 1, int $day = 1, int $year = null): int | monta um timestamp |
strtotime | strtotime(string $datetime, ?int $baseTimestamp = null): int ou false | interpreta texto: '-1 week', 'next monday' |
checkdate | checkdate(int $month, int $day, int $year): bool | checkdate(2, 29, 2024) dá true, ano bissexto |
mktime com dia 29 de fevereiro | ano não bissexto | mktime(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ção | Assinatura | Exemplo |
|---|---|---|
ceil | ceil(int ou float $num): float | ceil(4.1) = 5.0 |
floor | floor(int ou float $num): float | floor(4.9) = 4.0 |
round | round(int ou float $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): float | round(4.567, 2) = 4.57 |
abs | abs($num): int ou float | abs(-10) = 10 |
max | max(mixed $value, mixed ...$values): mixed | max($array) |
min | min(mixed $value, mixed ...$values): mixed | min($array) |
intdiv | intdiv(int $num1, int $num2): int | divisão inteira, sem real |
fdiv | fdiv(float $num1, float $num2): float | divisão que não dá erro por zero |
number_format | number_format(float $num, int $decimals = 0, ?string $decimal_separator = '.', ?string $thousands_separator = ','): string | padrão brasileiro |
rand | rand(int $min, int $max): int | lento, não serve para criptografia |
mt_rand | mt_rand(int $min, int $max): int | mais rápido, igual em todos os sistemas |
random_int | random_int(int $min, int $max): int | lento, mas criptograficamente seguro |
randemt_randnã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 éintde centavos oufloatcomround— misturar soma de float com exibição gera0.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)
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ção | Arquivo falta | Carrega de novo |
|---|---|---|
include 'x.php' | avisa e segue a execução | sim |
include_once 'x.php' | avisa e segue a execução | não, só na primeira vez |
require 'x.php' | erro fatal, script para | sim |
require_once 'x.php' | erro fatal, script para | nã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ção | Devolve |
|---|---|
__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
| Arquivo | Conteúdo |
|---|---|
config.php | constantes de ambiente: host, usuário, nome do banco |
funcoes.php | funções que não pertencem a nenhuma classe |
bootstrap.php | o que sempre roda: declare, timezone, sessão, autoload |
conexao.php | o 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(semonce) causa *Cannot redeclare*. A sequência correta érequire_onceno bootstrap para o que é global, eincludedentro 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 emlocalhoste 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.