Arrays são a estrutura de dados mais usada no PHP. Praticamente tudo que você fará com o PHP — ler dados de um banco, processar um formulário, construir uma resposta de API — vai envolver arrays de alguma forma. Entender arrays com profundidade não é opcional: é o que separa quem apenas usa PHP de quem realmente o domina.
Neste artigo vamos cobrir criação, acesso, modificação, arrays associativos, multidimensionais e o conjunto de funções nativas que tornam os arrays do PHP tão poderosos.
Criando arrays
O PHP suporta duas sintaxes para criar arrays. A sintaxe com colchetes [] é a moderna e preferida:
<?php
declare(strict_types=1);
// Sintaxe moderna — preferida desde PHP 5.4
$frutas = ["maçã", "banana", "laranja"];
// Sintaxe antiga — ainda funciona, mas evite em código novo
$frutas = array("maçã", "banana", "laranja");
// Array vazio — comum para inicializar antes de preencher
$resultado = [];
// Array com índices explícitos
// Os índices não precisam ser sequenciais
$sparse = [0 => "zero", 5 => "cinco", 10 => "dez"];
// PHP atribui o próximo índice inteiro após o maior existente
$sparse[] = "onze"; // receberá índice 11
// Verificando se é array e seu tamanho
var_dump(is_array($frutas)); // bool(true)
echo count($frutas); // 3
echo count([]); // 0
Arrays associativos
Arrays associativos usam strings como chaves em vez de índices numéricos. São equivalentes a dicionários ou mapas em outras linguagens:
<?php
declare(strict_types=1);
// Criando com chaves string
$usuario = [
"nome" => "Ana Silva",
"email" => "ana@email.com",
"idade" => 28,
"ativo" => true,
];
// Acessando por chave
echo $usuario["nome"]; // Ana Silva
echo $usuario["idade"]; // 28
// Modificando um valor existente
$usuario["idade"] = 29;
// Adicionando uma nova chave
$usuario["perfil"] = "administrador";
// Removendo uma chave
unset($usuario["ativo"]);
// Verificando se uma chave existe
var_dump(isset($usuario["email"])); // bool(true)
var_dump(isset($usuario["ativo"])); // bool(false) — removido
var_dump(array_key_exists("email", $usuario)); // bool(true) — mais preciso que isset
// Listando chaves e valores separadamente
$chaves = array_keys($usuario); // ["nome", "email", "idade", "perfil"]
$valores = array_values($usuario); // ["Ana Silva", "ana@email.com", 29, "administrador"]
A diferença entre isset() e array_key_exists() é sutil mas importante: isset() retorna false se a chave existir mas seu valor for null. array_key_exists() retorna true independentemente do valor — verifique apenas se a chave existe.
Arrays multidimensionais
Arrays podem conter outros arrays como valores, criando estruturas multidimensionais. Esse é o padrão usado para representar dados relacionais — exatamente o que vem de um banco de dados:
<?php
declare(strict_types=1);
// Array de arrays associativos — estrutura típica de resultado de banco
$usuarios = [
[
"id" => 1,
"nome" => "Ana",
"email" => "ana@email.com",
"cargo" => ["titulo" => "Desenvolvedora", "nivel" => "Senior"],
],
[
"id" => 2,
"nome" => "Bruno",
"email" => "bruno@email.com",
"cargo" => ["titulo" => "Designer", "nivel" => "Pleno"],
],
];
// Acessando dados aninhados com múltiplos colchetes
echo $usuarios[0]["nome"]; // Ana
echo $usuarios[0]["cargo"]["titulo"]; // Desenvolvedora
echo $usuarios[1]["cargo"]["nivel"]; // Pleno
// Iterando estrutura multidimensional
foreach ($usuarios as $usuario) {
// $usuario é um array associativo
echo "{$usuario['nome']} — {$usuario['cargo']['titulo']}\n";
}
// Ana — Desenvolvedora
// Bruno — Designer
// array_column — extrai uma coluna de um array multidimensional
// Equivale a: SELECT nome FROM usuarios
$nomes = array_column($usuarios, "nome");
print_r($nomes); // ["Ana", "Bruno"]
// Também pode usar uma coluna como índice do resultado
$porId = array_column($usuarios, null, "id");
// Agora $porId[1] é o array do usuário com id=1
echo $porId[1]["nome"]; // Ana
Adicionando e removendo elementos
<?php
declare(strict_types=1);
$pilha = ["a", "b", "c"];
// Adicionando ao final
$pilha[] = "d"; // forma mais comum
array_push($pilha, "e"); // equivalente, mas [] é preferido
// Removendo do final — retorna o elemento removido
$ultimo = array_pop($pilha); // "e"
// Adicionando ao início
array_unshift($pilha, "z"); // $pilha = ["z", "a", "b", "c", "d"]
// Removendo do início — retorna o elemento removido
$primeiro = array_shift($pilha); // "z"
// Removendo por índice específico
// unset remove o elemento mas não reindexia
unset($pilha[1]); // remove "b", índices ficam: 0=>"a", 2=>"c", 3=>"d"
// array_splice — remove e opcionalmente insere, reindexia automaticamente
// array_splice($array, $inicio, $quantidade, $substituicao)
$cores = ["vermelho", "verde", "azul", "amarelo"];
$removidas = array_splice($cores, 1, 2); // remove 2 a partir do índice 1
// $cores = ["vermelho", "amarelo"]
// $removidas = ["verde", "azul"]
// Inserindo no meio com array_splice
array_splice($cores, 1, 0, ["roxo", "laranja"]); // insere sem remover
// $cores = ["vermelho", "roxo", "laranja", "amarelo"]
Funções essenciais de array
O PHP tem mais de 70 funções nativas para arrays. Aqui estão as mais importantes:
Busca e verificação
<?php
declare(strict_types=1);
$numeros = [10, 20, 30, 40, 50];
$frutas = ["maçã", "banana", "laranja"];
// in_array — verifica se um valor existe
var_dump(in_array(30, $numeros)); // bool(true)
var_dump(in_array("banana", $frutas)); // bool(true)
var_dump(in_array("uva", $frutas)); // bool(false)
// Terceiro argumento true = comparação estrita (===)
var_dump(in_array("30", $numeros)); // bool(true) — coerção!
var_dump(in_array("30", $numeros, true)); // bool(false) — estrito
// array_search — retorna a chave do elemento encontrado, ou false
$posicao = array_search(30, $numeros);
var_dump($posicao); // int(2)
$ausente = array_search(99, $numeros);
var_dump($ausente); // bool(false)
// Cuidado: posição 0 e false são ambos falsy!
// Use === false para verificar ausência
if (array_search(10, $numeros) !== false) {
echo "Encontrado!\n";
}
Ordenação
<?php
declare(strict_types=1);
$numeros = [3, 1, 4, 1, 5, 9, 2, 6];
$nomes = ["Carlos", "Ana", "Beatriz", "David"];
// sort — ordena por valor, reindexia (destrói chaves originais)
sort($numeros); // [1, 1, 2, 3, 4, 5, 6, 9]
sort($nomes); // ["Ana", "Beatriz", "Carlos", "David"]
// rsort — ordem decrescente
rsort($numeros); // [9, 6, 5, 4, 3, 2, 1, 1]
// asort — ordena por valor, PRESERVA as chaves
$notas = ["Ana" => 9.5, "Bruno" => 7.0, "Carla" => 8.5];
asort($notas);
// ["Bruno" => 7.0, "Carla" => 8.5, "Ana" => 9.5]
// arsort — ordem decrescente, preserva chaves
arsort($notas);
// ["Ana" => 9.5, "Carla" => 8.5, "Bruno" => 7.0]
// ksort — ordena por chave
$config = ["z_timeout" => 30, "a_host" => "localhost", "m_port" => 3306];
ksort($config);
// ["a_host" => ..., "m_port" => ..., "z_timeout" => ...]
// usort — ordenação customizada com função de comparação
$usuarios = [
["nome" => "Carlos", "idade" => 35],
["nome" => "Ana", "idade" => 28],
["nome" => "Bruno", "idade" => 42],
];
// Ordena por idade usando spaceship operator
usort($usuarios, fn(array $a, array $b): int => $a["idade"] <=> $b["idade"]);
// Ana(28), Carlos(35), Bruno(42)
Transformação
<?php
declare(strict_types=1);
$numeros = [1, 2, 3, 4, 5];
$precos = [10.0, 25.5, 8.0, 42.0, 15.0];
// array_map — aplica função a cada elemento, retorna novo array
$dobrados = array_map(fn(int $n): int => $n * 2, $numeros);
// [2, 4, 6, 8, 10]
// array_map com múltiplos arrays
$a = [1, 2, 3];
$b = [10, 20, 30];
$soma = array_map(fn(int $x, int $y): int => $x + $y, $a, $b);
// [11, 22, 33]
// array_filter — mantém apenas os elementos que passam no teste
$pares = array_filter($numeros, fn(int $n): bool => $n % 2 === 0);
// [2, 4] — note: chaves originais são preservadas (1 e 3)
// array_values reindexia após filter
$pares = array_values(array_filter($numeros, fn(int $n): bool => $n % 2 === 0));
// [2, 4] com índices 0 e 1
// array_reduce — reduz o array a um único valor
$total = array_reduce($precos, fn(float $carry, float $item): float => $carry + $item, 0.0);
echo $total; // 100.5
// Calculando média com array_reduce
$media = array_reduce($precos, fn(float $carry, float $item): float => $carry + $item, 0.0) / count($precos);
echo $media; // 20.1
// array_unique — remove valores duplicados
$comDuplicatas = [1, 2, 2, 3, 3, 3, 4];
$unicos = array_unique($comDuplicatas);
// [1, 2, 3, 4]
Fatiamento e combinação
<?php
declare(strict_types=1);
$letras = ["a", "b", "c", "d", "e", "f"];
// array_slice — extrai uma porção do array
$parte = array_slice($letras, 1, 3); // ["b", "c", "d"]
$final = array_slice($letras, -2); // ["e", "f"] — índice negativo
$reindexa = array_slice($letras, 2, 3, true); // preserva chaves originais
// array_merge — une dois ou mais arrays
$a = ["maçã", "banana"];
$b = ["laranja", "uva"];
$c = array_merge($a, $b); // ["maçã", "banana", "laranja", "uva"]
// Com arrays associativos — chaves repetidas: o segundo sobrescreve
$padroes = ["tema" => "claro", "idioma" => "pt-BR", "itens" => 10];
$config = ["tema" => "escuro", "debug" => true];
$final = array_merge($padroes, $config);
// ["tema" => "escuro", "idioma" => "pt-BR", "itens" => 10, "debug" => true]
// Operador + — alternativo ao merge: a chave do primeiro vence
$final2 = $padroes + $config;
// ["tema" => "claro", "idioma" => "pt-BR", "itens" => 10, "debug" => true]
// array_combine — cria associativo a partir de dois arrays (chaves + valores)
$chaves = ["nome", "email", "cidade"];
$valores = ["Ana", "ana@email.com", "São Paulo"];
$pessoa = array_combine($chaves, $valores);
// ["nome" => "Ana", "email" => "ana@email.com", "cidade" => "São Paulo"]
// array_chunk — divide em pedaços
$numeros = [1, 2, 3, 4, 5, 6, 7];
$grupos = array_chunk($numeros, 3);
// [[1,2,3], [4,5,6], [7]]
Funções de contagem e matemática
<?php
declare(strict_types=1);
$valores = [5, 3, 8, 1, 9, 2, 7, 4, 6];
$precos = [10.5, 25.0, 8.75, 42.0];
echo count($valores); // 9 — número de elementos
echo array_sum($precos); // 86.25 — soma de todos os elementos
echo array_product([1,2,3,4]); // 24 — produto de todos os elementos
echo min($valores); // 1 — menor valor
echo max($valores); // 9 — maior valor
// Funções que funcionam com arrays
$arredondados = array_map(fn(float $p): float => round($p, 1), $precos);
// compact e extract — atalhos para criar arrays de variáveis
$nome = "Ana";
$idade = 28;
$cidade = "São Paulo";
// compact cria array associativo a partir de variáveis
$dados = compact("nome", "idade", "cidade");
// ["nome" => "Ana", "idade" => 28, "cidade" => "São Paulo"]
// extract faz o inverso — cria variáveis a partir de array
// Use com cuidado: pode sobrescrever variáveis existentes
extract($dados); // cria $nome, $idade, $cidade no escopo atual
Padrões práticos com arrays
<?php
declare(strict_types=1);
// Agrupando dados por critério
$transacoes = [
["tipo" => "entrada", "valor" => 500.0],
["tipo" => "saida", "valor" => 120.0],
["tipo" => "entrada", "valor" => 300.0],
["tipo" => "saida", "valor" => 80.0],
];
$grupos = [];
foreach ($transacoes as $t) {
// Agrupa pelo campo "tipo"
$grupos[$t["tipo"]][] = $t["valor"];
}
// $grupos = [
// "entrada" => [500.0, 300.0],
// "saida" => [120.0, 80.0]
// ]
$totalEntradas = array_sum($grupos["entrada"]); // 800.0
$totalSaidas = array_sum($grupos["saida"]); // 200.0
// Pipeline funcional — filtrar, transformar, reduzir
$produtos = [
["nome" => "Teclado", "preco" => 250.0, "ativo" => true],
["nome" => "Mouse", "preco" => 80.0, "ativo" => false],
["nome" => "Monitor", "preco" => 1200.0,"ativo" => true],
["nome" => "Headset", "preco" => 350.0, "ativo" => true],
];
// Soma o preço dos produtos ativos com preço abaixo de R$500
$totalAtivosBaratos = array_reduce(
array_filter(
$produtos,
fn(array $p): bool => $p["ativo"] && $p["preco"] < 500.0
),
fn(float $carry, array $p): float => $carry + $p["preco"],
0.0
);
echo $totalAtivosBaratos; // 600.0 (Teclado 250 + Headset 350)
Boa parte do trabalho diário em PHP é transformar array: o que vem do banco vira o que a tela precisa, o que chega do formulário vira o que a regra de negócio entende. Quem conhece bem esse punhado de funções escreve menos laço e erra menos. O cuidado que sobra é sempre o mesmo — saber se a chave foi preservada ou descartada —, porque é daí que vêm os defeitos que não dão erro: a lista que virou objeto no JSON, o vendedor que sumiu da ordenação, o índice que deixou de ser sequencial.
Fontes e leituras recomendadas
-
Funções de array — Manual oficial do PHP https://www.php.net/manual/pt_BR/ref.array.php Referência completa das mais de 70 funções nativas para arrays do PHP, com exemplos para cada uma.
-
array_map — php.net https://www.php.net/manual/pt_BR/function.array-map.php Documentação detalhada do
array_map, incluindo o comportamento com múltiplos arrays e com null como callback. -
array_reduce — php.net https://www.php.net/manual/pt_BR/function.array-reduce.php Documentação do
array_reducecom exemplos de acumuladores e a importância do valor inicial correto. -
PHP: The Right Way — Arrays https://phptherightway.com/#arrays Seção sobre boas práticas de arrays, quando usar funções funcionais e quando usar loops explícitos.
-
SPL Data Structures — Manual oficial do PHP https://www.php.net/manual/pt_BR/spl.datastructures.php Estruturas de dados especializadas da SPL: SplStack, SplQueue, SplHeap. Para quando arrays simples não são suficientes.
-
WENGROW, Jay. A Common-Sense Guide to Data Structures and Algorithms. 2ª ed. Pragmatic Bookshelf, 2020. https://pragprog.com/titles/jwdsal2/a-common-sense-guide-to-data-structures-and-algorithms-second-edition/ Introdução acessível a estruturas de dados e algoritmos, com foco em quando usar cada estrutura. Leitura recomendada após dominar os arrays do PHP.
Exercícios
Exercício 1
O front-end espera uma lista JSON e recebe um objeto, quebrando o .map() dele. O PHP não deu erro nenhum. O que aconteceu?
<?php
$notas = [8, 0, 7, 9];
$aprovadas = array_filter($notas, fn($n) => $n >= 7);
echo json_encode($aprovadas); // {"0":8,"2":7,"3":9}
Ver resposta
✓ Resposta: O array_filter preserva as chaves originais. Ao remover o elemento de índice 1, sobram as chaves 0, 2 e 3 — e um array PHP com chaves numéricas não sequenciais deixa de ser uma lista. O json_encode então não tem escolha: precisa preservar os índices, e a única forma de fazer isso em JSON é gerando um objeto. Do lado do JavaScript, {"0":8,"2":7,"3":9} não tem .map(), e o erro aparece lá, a três camadas de distância de onde foi causado. A correção é reindexar: array_values(array_filter(...)), que descarta as chaves e devolve uma lista de 0 a n. Vale a pena saber quais funções têm esse comportamento, porque a inconsistência é histórica e não segue regra: array_filter e array_unique preservam as chaves; array_map com um array preserva, mas com dois ou mais arrays reindexa; array_merge reindexa as chaves numéricas e mantém as de texto. Em PHP 8.1 existe array_is_list($a), que responde direto se o array ainda é uma lista — é a verificação que vale colocar num teste, ou antes de serializar uma resposta de API. E no json_encode há a saída de emergência JSON_FORCE_OBJECT para o caso inverso, quando você quer objeto sempre.
Exercício 2
Esta verificação de permissão liberava acesso indevido em PHP 7 e parou de liberar em PHP 8. O que mudou — e por que o código continua errado mesmo agora?
<?php
$permissoes = ['ler', 'escrever', 'publicar'];
$nivel = 0;
if (in_array($nivel, $permissoes)) {
echo "acesso liberado";
}
Ver resposta
✓ Resposta: O in_array compara com == por padrão. Até o PHP 7, comparar o número 0 com a string 'ler' convertia a string para número, 'ler' virava 0, e a comparação dava verdadeira — ou seja, o nível 0 "tinha" a permissão de ler, e também a de escrever, e a de publicar. Um erro de autorização causado por uma regra de coerção. No PHP 8, a comparação entre número e string não numérica passou a converter o número para string, e '0' == 'ler' é falso: o acesso deixou de ser liberado. Mas o código continua errado por dois motivos. Primeiro, ele ainda depende da comparação frouxa, e vai voltar a surpreender no dia em que $nivel chegar como '0' ou a lista contiver algo numérico — a correção é o terceiro parâmetro: in_array($nivel, $permissoes, true), que compara com === e deveria ser o padrão em qualquer verificação de segurança. Segundo, e mais grave, ele compara níveis com nomes de permissão: são conceitos diferentes, e nenhum valor de $nivel numérico faz sentido nessa lista. Verificação de autorização é justamente onde comparação frouxa não tem lugar — e onde vale considerar enum em vez de string solta, para que o próprio tipo impeça a confusão.
Exercício 3
Dois arrays precisam ser combinados. Qual a diferença entre as duas linhas, e quando cada uma é a correta?
<?php
$a = ['config' => 'padrão', 10, 20];
$b = ['config' => 'do usuário', 30, 40, 50];
$x = array_merge($a, $b);
$y = $a + $b;
Ver resposta
✓ Resposta: As duas tratam chave de texto e chave numérica de formas opostas. O array_merge faz a chave de texto do segundo vencer ('config' fica 'do usuário') e reindexa as numéricas, concatenando: sobram 10, 20, 30, 40, 50. Já o operador + é união de chaves, e nele o primeiro array vence em tudo o que já existe: 'config' continua 'padrão', os índices 0 e 1 continuam 10 e 20, e do segundo array só entra o índice 2, que não existia no primeiro — resultado [10, 20, 50], que quase nunca é o que alguém queria. Daí a regra prática: array_merge para juntar listas, + para aplicar valores padrão — $opcoes = $informado + $padrao; lê-se "use o que foi informado e complete com o padrão", e é um idioma comum e legítimo. Duas notas para fechar. Para juntar arrays associativos aninhados, nenhuma das duas serve: array_merge é raso e sobrescreve o sub-array inteiro; o que se quer nesse caso é array_replace_recursive, ou uma função própria, porque array_merge_recursive tem o comportamento surpreendente de transformar valores duplicados em array. E em PHP 7.4 ou superior, o operador ... também junta arrays ([...$a, ...$b]), com as mesmas regras do array_merge — só que aceitando chaves de texto apenas a partir do PHP 8.1.
Exercício 4
Uma listagem precisa ser ordenada pelo nome do cliente. Diga o que cada função faz e qual delas serve para um array de arrays vindo do banco: sort, asort, ksort, usort.
Ver resposta
✓ Resposta: sort ordena pelos valores e descarta as chaves, reindexando de 0 a n — serve para lista simples, e é destrutivo se as chaves importavam. asort ordena pelos valores mantendo a associação chave-valor, o que é o certo para um array associativo do tipo ['ana' => 30, 'bruno' => 25]. ksort ordena pelas chaves. E usort ordena por um critério que você escreve, sendo a única das quatro que serve ao caso do enunciado: com registros vindos do banco, cada elemento é um array, e não existe ordem natural entre arrays — é preciso dizer qual campo compara. A forma idiomática usa o operador spaceship, que já devolve o -1, 0 ou 1 que o usort espera: usort($clientes, fn($a, $b) => $a['nome'] <=> $b['nome']);. Para ordenar por dois critérios, compare arrays: [$a['cidade'], $a['nome']] <=> [$b['cidade'], $b['nome']] — o spaceship compara elemento a elemento e só passa ao segundo em caso de empate. Duas armadilhas comuns: todas essas funções recebem o array por referência e alteram no lugar, devolvendo apenas true — $ordenado = sort($a) guarda true, não o array; e comparar nomes com acento por <=> usa a ordem dos bytes, colocando "Ávila" depois de "Zanetti". Quando a ordem alfabética precisa estar correta para o usuário, o caminho é Collator, da extensão intl, ou deixar a ordenação a cargo do banco, com a collation apropriada.
Exercício 5
Desafio: você recebe uma lista plana de vendas e precisa do total por vendedor, ordenado do maior para o menor. Escreva a transformação e explique cada escolha.
<?php
$vendas = [
['vendedor' => 'ana', 'valor' => 1200],
['vendedor' => 'bruno', 'valor' => 800],
['vendedor' => 'ana', 'valor' => 300],
['vendedor' => 'carla', 'valor' => 1500],
];
Ver resposta
✓ Resposta: O agrupamento é um foreach que acumula em um array indexado pela chave de grupo — e a clareza aqui vale mais que a esperteza:
<?php
$totais = [];
foreach ($vendas as $v) {
// ??= cria a chave com 0 na primeira vez que o vendedor aparece,
// e não faz nada nas seguintes — evita o isset() antes de somar
$totais[$v['vendedor']] ??= 0;
$totais[$v['vendedor']] += $v['valor'];
}
// arsort: ordena pelo VALOR, decrescente, PRESERVANDO a chave.
// sort() aqui jogaria fora o nome do vendedor, que é justamente o resultado.
arsort($totais);
print_r($totais);
// ['carla' => 1500, 'ana' => 1500, 'bruno' => 800]
Três escolhas merecem defesa. A primeira é ??= em vez de isset(): sem ele, somar numa chave inexistente emite Warning: Undefined array key — que não quebra nada e por isso passa despercebido, até alguém ligar error_reporting em produção e encontrar milhares dele no log. A segunda é arsort e não sort: o nome do vendedor é a chave, e sort descartaria exatamente a informação que dá sentido ao número. A terceira é o foreach em vez de array_reduce: a versão com array_reduce existe e é uma linha, mas exige recriar o acumulador a cada passo, o que a torna mais lenta e menos legível — agrupamento é um dos poucos casos em que o laço explícito ganha da função de ordem superior.
Repare no empate entre Carla e Ana, ambas com 1500: arsort não garante qual vem primeiro em caso de empate (a ordenação não é estável por natureza, embora o PHP 8 tenha passado a garantir estabilidade nas funções de ordenação). Se o desempate importa — ordem alfabética, por exemplo —, ele precisa ser explícito: uasort($totais, fn($a, $b) => $b <=> $a); com um critério secundário, ou ordenar por array de pares.