PHP Arrays Associativos

Na aula Arrays Indexados, cada gaveta do array tinha um número: 0, 1, 2… Isso é ótimo para listas, mas péssimo para uma ficha de cadastro: quem vai lembrar que a idade está na gaveta 1 e a cidade na 2? O array associativo troca os números por nomes. Você associa cada valor a uma etiqueta: "nome", "idade", "cidade". É, de longe, o tipo de array mais usado no PHP: formulários, sessões, linhas do banco de dados e JSON chegam todos assim.

Você vai aprender
  • O que é um par chave => valor e como criar um array associativo.
  • Como ler, mudar, acrescentar e apagar itens pelo nome.
  • Como saber se uma chave existe: isset × array_key_exists (e por que o null faz diferença).
  • Como percorrer o array com foreach ($a as $chave => $valor).
  • Por que a chave "1" vira o número 1 sozinha, e outras surpresas das chaves.
  • Que o PHP guarda os itens na ordem em que você os colocou.

A ideia: uma ficha com campos

Pense numa ficha de matrícula em papel. Ela tem campos com nome e, ao lado de cada um, o que foi preenchido:

Campo (chave)Preenchido (valor)
"nome""Ana"
"idade"29
"cidade""Recife"

Para saber a cidade, você não pergunta "o que está na linha 3?", e sim "o que está no campo cidade?". É exatamente assim que um array associativo funciona. Outros nomes que você vai encontrar para a mesma ideia: dicionário (Python), mapa (Java), objeto (JavaScript), hash.


Criar e ler

<?php
$aluna = [
    "nome"   => "Ana",
    "idade"  => 29,
    "cidade" => "Recife",
];
echo $aluna["nome"], " tem ", $aluna["idade"], " anos e mora em ", $aluna["cidade"], ".\n";
print_r($aluna);
[ … ]
Os colchetes criam o array, como antes.
"nome"
A chave: o nome do campo. Normalmente é uma string entre aspas.
=>
A "setinha dupla" (sinal de igual e maior, sem espaço entre eles). Lê-se "aponta para": "nome" aponta para "Ana".
"Ana"
O valor guardado naquela chave. Pode ser de qualquer tipo.
,
Separa um par do outro. A vírgula depois do último par é opcional (e prática).
$aluna["nome"]
Lê o valor da chave "nome": o nome do array e, entre colchetes, a chave com aspas.
$ php aluna.php
Ana tem 29 anos e mora em Recife.
Array
(
    [nome] => Ana           « no lugar dos números, os nomes das chaves
    [idade] => 29
    [cidade] => Recife
)

Alinhar as setas => com espaços, como no exemplo, é só estética: o PHP ignora esses espaços. Muita gente alinha porque fica mais fácil de ler.

Indexado ou associativo?

Para o PHP, os dois são o mesmo tipo: array. A diferença está só nas chaves. Um array indexado é um array cujas chaves são 0, 1, 2… criadas automaticamente. Escrever ["arroz", "café"] é o mesmo que escrever [0 => "arroz", 1 => "café"]. Use o indexado para listas de coisas do mesmo tipo (vários produtos) e o associativo para os dados de uma coisa (um produto: nome, preço, estoque).


Mudar, acrescentar e apagar

Com chaves de nome, as três operações usam a mesma escrita: $array["chave"] = valor;. Se a chave já existe, o valor é trocado; se não existe, ela é criada.

<?php
$aluna = ["nome" => "Ana", "idade" => 29];
$aluna["idade"] = 30;                    // a chave existe: troca
$aluna["email"] = "ana@exemplo.com";     // a chave não existe: cria
unset($aluna["nome"]);                   // apaga a chave e o valor
print_r($aluna);
echo count($aluna), "\n";
$ php mudar-assoc.php
Array
(
    [idade] => 30
    [email] => ana@exemplo.com
)
2

O count funciona igual: conta os pares. E o $aluna[] = … (colchetes vazios) também funciona aqui, mas cria uma chave numérica; você vai ver isso na seção sobre chaves.

Usar dentro de aspas duplas

Para colocar um valor no meio de um texto, há três jeitos:

<?php
$aluna = ["nome" => "Ana", "idade" => 29];
echo "Olá, $aluna[nome]!\n";
echo "Olá, {$aluna['nome']}! Você tem {$aluna['idade']} anos.\n";
echo "Olá, " . $aluna["nome"] . "!\n";
$ php interp.php
Olá, Ana!
Olá, Ana! Você tem 29 anos.
Olá, Ana!
  • "$aluna[nome]": dentro das aspas duplas, a chave vai sem aspas. Funciona, mas só nessa situação, e confunde.
  • "{$aluna['nome']}": com chaves { } em volta, a chave vai com aspas simples, como no resto do código. É a forma recomendada.
  • A concatenação com . sempre funciona (aula Operadores de String).

O que não funciona é misturar: "$aluna['nome']", com aspas e sem as chaves, é erro de sintaxe (veja Quando dá errado). Os detalhes estão na aula Interpolação.

Crie a ficha de outra pessoa, acrescente uma chave "profissao" e mostre uma frase com os dados. Experimente também apagar uma chave com unset e ver o print_r.

<?php
$pessoa = [
    "nome"   => "Bruno",
    "idade"  => 34,
    "cidade" => "Olinda",
];

$pessoa["idade"] = 35;
$pessoa["profissao"] = "padeiro";

echo "{$pessoa['nome']} tem {$pessoa['idade']} anos e é {$pessoa['profissao']}.\n";
print_r($pessoa);

Uma chave que não existe

Pedir uma chave que não existe dá o mesmo aviso dos arrays indexados, agora com o nome da chave entre aspas:

<?php
$aluna = ["nome" => "Ana", "idade" => 29];
echo "E-mail: ", $aluna["email"], "\n";
echo "E-mail: ", $aluna["email"] ?? "não informado", "\n";
echo $aluna["Nome"], "\n";
$ php chave-falta.php
E-mail:
Warning: Undefined array key "email" in /home/ana/site/chave-falta.php on line 3

E-mail: não informado

Warning: Undefined array key "Nome" in /home/ana/site/chave-falta.php on line 5
  • Linha 3: a chave "email" não existe. Aviso, e o valor vira null.
  • Linha 4: com o ??, nenhum aviso: se a chave faltar, vale o texto da direita (aula ?? e ?->).
  • Linha 5: "Nome" com N maiúsculo é outra chave. As chaves diferenciam maiúsculas de minúsculas.

Perguntar se a chave existe: isset e array_key_exists

Muitas vezes você precisa decidir algo conforme a chave exista ou não: "se o formulário mandou o e-mail, faça isso". Há duas funções para perguntar, e elas quase sempre respondem igual:

  • isset($a["chave"]): "está definido?" (is set). Responde true se a chave existe e o valor não é null.
  • array_key_exists("chave", $a): "a chave existe no array?". Responde true se a chave existe, seja qual for o valor, até null.

A diferença só aparece quando o valor guardado é null. Veja com uma ficha em que o telefone foi deixado em branco de propósito:

<?php
$aluna = ["nome" => "Ana", "telefone" => null];

var_dump(isset($aluna["nome"]));
var_dump(isset($aluna["email"]));
var_dump(isset($aluna["telefone"]));

var_dump(array_key_exists("nome", $aluna));
var_dump(array_key_exists("email", $aluna));
var_dump(array_key_exists("telefone", $aluna));

var_dump($aluna["telefone"] ?? "sem telefone");
$ php isset.php
bool(true)
bool(false)
bool(false)             « isset: a chave existe, mas vale null → false
bool(true)
bool(false)
bool(true)              « array_key_exists: a chave existe → true
string(12) "sem telefone"
Situaçãoisset($a["k"])array_key_exists("k", $a)$a["k"] ?? "x"
Chave existe, com valor ("Ana", 0, "", false)truetrueo valor
Chave existe, valor nullfalsetrue"x"
Chave não existefalsefalse"x"

Qual usar?

  • isset no dia a dia: é curta, rápida e, na maioria dos casos, um valor null é mesmo "não tem nada". É a que você mais vai ver em formulários (isset($_POST["email"]), na aula $_GET e $_POST).
  • array_key_exists quando null tem significado e você precisa distinguir "o campo veio vazio" de "o campo nem veio".
  • ?? quando você só quer um valor padrão. Ele segue a mesma regra do isset.
Repare na ordem dos argumentos

No isset você escreve o acesso inteiro: isset($aluna["email"]). No array_key_exists, primeiro a chave, depois o array: array_key_exists("email", $aluna). Trocar a ordem dá erro (veja Quando dá errado).


Percorrer com foreach

O foreach tem uma forma com duas variáveis, feita para arrays associativos: uma recebe a chave e a outra o valor, a cada volta. A Ana usa isso na tabela de preços da lojinha:

<?php
$precos = [
    "café"   => 18.9,
    "pão"    => 0.75,
    "leite"  => 5.49,
];
foreach ($precos as $produto => $preco) {
    echo $produto, ": R$ ", number_format($preco, 2, ",", "."), "\n";
}
$total = 0;
foreach ($precos as $preco) {
    $total += $preco;
}
echo "Total: R$ ", number_format($total, 2, ",", "."), "\n";
foreach ($precos as $produto => $preco)
"Para cada par de $precos, coloque a chave em $produto e o valor em $preco". A seta => aqui tem o mesmo sentido da criação: chave à esquerda, valor à direita. Os nomes $produto e $preco são escolha sua.
number_format($preco, 2, ",", ".")
Escreve o número com 2 casas, vírgula decimal e ponto de milhar (aula Formatar Números).
foreach ($precos as $preco)
A forma curta, com uma variável só, recebe só os valores. Serve quando a chave não interessa, como na soma.
$ php foreach-assoc.php
café: R$ 18,90
pão: R$ 0,75
leite: R$ 5,49
Total: R$ 25,14

Repare que o for com $i de 0 a count - 1, que funcionava nos arrays indexados, não serve aqui: não existe $precos[0]. Para arrays associativos, o caminho é sempre o foreach. (Para somar, existe ainda array_sum($precos), que você vai ver em Funções de Array, junto com array_keys e array_values, que pegam só as chaves ou só os valores.)


O que pode ser chave (e as conversões automáticas)

Uma chave só pode ser de dois tipos: int (número inteiro) ou string (texto). Se você usar outro tipo, ou uma string que "parece" um número inteiro, o PHP converte sozinho. Isso surpreende muita gente:

<?php
$a = [
    "1"   => "um",
    "01"  => "zero-um",
    "1.5" => "um e meio",
    "-5"  => "menos cinco",
];
var_dump($a);
echo $a[1], " / ", $a["1"], "\n";
$ php chaves-conv.php
array(4) {
  [1]=>                 « sem aspas: a string "1" virou o número 1
  string(2) "um"
  ["01"]=>              « com aspas: continua string ("01" não é a forma normal de escrever 1)
  string(7) "zero-um"
  ["1.5"]=>             « continua string: não é inteiro
  string(9) "um e meio"
  [-5]=>                « "-5" virou o número -5
  string(11) "menos cinco"
}
um / um                 « $a[1] e $a["1"] são a mesma gaveta

No var_dump, chave com aspas é string, chave sem aspas é número. A regra: uma string que é um inteiro escrito do jeito "normal" (sem zero à esquerda, sem +, sem espaço, sem ponto) vira int. Por isso "1" e 1 são a mesma chave, e o último a ser escrito vence:

<?php
$a = ["cor" => "azul", "tamanho" => "M", "cor" => "verde"];
print_r($a);
$b = ["1" => "a", 1 => "b"];
print_r($b);
$ php repetida.php
Array
(
    [cor] => verde          « a chave "cor" apareceu duas vezes: ficou o último valor
    [tamanho] => M
)
Array
(
    [1] => b                « "1" e 1 são a mesma chave
)

Na prática, isso importa quando as chaves vêm de fora, como códigos de produto lidos de um formulário ou de um arquivo CSV: o código "1042" vira o número 1042, mas o código "0042" continua texto.

Os outros tipos também são convertidos, e o PHP 8.5 avisa nos casos mais perigosos:

Você usa como chaveViraAviso no PHP 8.5
"7"7nenhum
"07", "7.0", " 7"continuam stringsnenhum
true / false1 / 0nenhum
1.7 (float)1 (corta as decimais)Deprecated: Implicit conversion from float 1.7 to int loses precision
null"" (string vazia)Deprecated: Using null as an array offset is deprecated, use an empty string instead
um array ou um objetonão podeerro (TypeError)
<?php
$a = [];
$a[1.7] = "x";
var_dump($a);
$ php chave-float.php

Deprecated: Implicit conversion from float 1.7 to int loses precision in /home/ana/site/chave-float.php on line 3
array(1) {
  [1]=>
  string(1) "x"
}

Deprecated quer dizer "desaprovado": funciona hoje, mas pode virar erro numa versão futura. Corrija quando ver um (mais em Tipos de Erro). A regra prática é simples: use como chave só strings ou inteiros.

Misturando chaves com nome e números

Um mesmo array pode ter as duas coisas. Os colchetes vazios [], e os itens sem chave, sempre pegam o maior número usado até ali + 1 (ou 0, se ainda não houver nenhum número):

<?php
$a = [5 => "cinco", "nome" => "Ana", "seis?"];
$a[] = "mais um";
print_r($a);
$b = ["a", "b"];
$b["x"] = "xis";
print_r($b);
$ php misto.php
Array
(
    [5] => cinco
    [nome] => Ana
    [6] => seis?            « sem chave: maior número (5) + 1
    [7] => mais um
)
Array
(
    [0] => a
    [1] => b
    [x] => xis
)

Funciona, mas é raro ser uma boa ideia. Prefira arrays que sejam claramente uma coisa ou outra: lista (chaves 0, 1, 2…) ou ficha (chaves com nome).


A ordem é a da inserção

Em algumas linguagens, um "dicionário" não tem ordem garantida. No PHP tem: os itens ficam na ordem em que foram colocados. Trocar o valor de uma chave que já existe não a muda de lugar.

<?php
$notas = [];
$notas["Pedro"] = 7.5;
$notas["Ana"] = 9.0;
$notas["Carla"] = 8.0;
$notas["Ana"] = 9.5;      // troca o valor; a Ana continua em 2º
print_r($notas);
$ php ordem.php
Array
(
    [Pedro] => 7.5
    [Ana] => 9.5
    [Carla] => 8              « 8.0 aparece como 8 no print_r
)

O PHP não ordena nada sozinho: nem por chave, nem por valor. Para colocar em ordem alfabética ou da maior nota para a menor, use as funções da aula Ordenar Arrays (ksort, arsort…).


Quando dá errado

Warning: Undefined array key "email"
$ php chave-falta.php
E-mail:
Warning: Undefined array key "email" in /home/ana/site/chave-falta.php on line 3

O que aconteceu: a chave não existe no array. Pode ser um erro de digitação ("emial"), maiúscula trocada ("Email") ou um dado que nem sempre vem, como um campo opcional de formulário.

Como resolver: confira as chaves que existem com print_r. Se o dado é opcional, use $a["email"] ?? "padrão" ou teste antes com isset.

Fatal error: Uncaught Error: Undefined constant "nome"
<?php
$aluna = ["nome" => "Ana"];
echo $aluna[nome];
$ php semaspas.php

Fatal error: Uncaught Error: Undefined constant "nome" in /home/ana/site/semaspas.php:3
Stack trace:
#0 {main}
  thrown in /home/ana/site/semaspas.php on line 3

O que aconteceu: fora das aspas duplas, uma palavra sem aspas e sem $ é lida como o nome de uma constante. Não existe constante nome, e o programa para. (Em PHPs muito antigos isso era só um aviso, por isso você ainda encontra $a[nome] em código velho.)

Como resolver: chave de texto sempre entre aspas: $aluna["nome"] ou $aluna['nome'].

Parse error: syntax error, unexpected string content ""
<?php
$aluna = ["nome" => "Ana"];
echo "Olá, $aluna['nome']!\n";
$ php aspasinterp.php

Parse error: syntax error, unexpected string content "", expecting "-" or identifier or variable or number in /home/ana/site/aspasinterp.php on line 3

O que aconteceu: dentro de aspas duplas, a forma simples ($aluna[nome]) não aceita aspas na chave. A mensagem é confusa: o PHP diz que esperava um nome (identifier), uma variável ou um número entre os colchetes.

Como resolver: envolva com chaves: "Olá, {$aluna['nome']}!". Ou concatene: "Olá, " . $aluna["nome"] . "!".

Parse error: syntax error, unexpected token "="
<?php
$aluna = ["nome" = "Ana"];
$ php igual.php

Parse error: syntax error, unexpected token "=", expecting "]" in /home/ana/site/igual.php on line 2

O que aconteceu: dentro da criação do array, o que liga a chave ao valor é =>, não =. Também acontece com -> (seta simples, que é de objetos) ou com espaço no meio (= >).

Como resolver: ["nome" => "Ana"]. Lembre: = é para guardar numa variável; => é para ligar chave e valor.

array_key_exists(): Argument #2 ($array) must be of type array, string given
<?php
$aluna = ["nome" => "Ana"];
var_dump(array_key_exists($aluna, "email"));
$ php existe.php

Fatal error: Uncaught TypeError: array_key_exists(): Argument #2 ($array) must be of type array, string given in /home/ana/site/existe.php:3
Stack trace:
#0 {main}
  thrown in /home/ana/site/existe.php on line 3

O que aconteceu: os argumentos foram trocados. O argumento nº 2 (chamado $array) deve ser um array, mas recebeu a string "email". TypeError é "erro de tipo": um valor do tipo errado foi passado a uma função (mais em Tipos e Retorno).

Como resolver: array_key_exists("email", $aluna): primeiro a chave, depois o array.


Referência rápida

Você quer…Código
Criar$a = ["nome" => "Ana", "idade" => 29];
Ler$a["nome"]
Ler com valor padrão$a["email"] ?? "não informado"
Mudar ou acrescentar$a["idade"] = 30;
Apagarunset($a["idade"]);
Existe e não é null?isset($a["email"])
Existe (mesmo que null)?array_key_exists("email", $a)
Quantos parescount($a)
Percorrerforeach ($a as $chave => $valor) { … }
Usar num texto"Olá, {$a['nome']}"
Só as chaves / só os valoresarray_keys($a) / array_values($a) (em Funções de Array)

Missão final

Missão: o estoque da lojinha da Ana está num array associativo. (1) Acrescente o produto "açúcar" com 7 unidades. (2) Mude o "leite" para 15. (3) Complete o foreach para mostrar uma linha produto: quantidade para cada item. (4) A última parte usa $estoque["arroz"] e gera um aviso: troque o teste por array_key_exists (ou isset) para mostrar Sem arroz no cadastro sem aviso nenhum.

<?php
$estoque = ["café" => 12, "pão" => 40, "leite" => 0];

// (1) e (2) aqui


foreach ($estoque as /* (3) */) {
    echo "\n";
}

if ($estoque["arroz"]) {
    echo "Tem arroz\n";
} else {
    echo "Sem arroz no cadastro\n";
}
<?php
$estoque = ["café" => 12, "pão" => 40, "leite" => 0];

$estoque["açúcar"] = 7;
$estoque["leite"] = 15;

foreach ($estoque as $produto => $quantidade) {
    echo "$produto: $quantidade\n";
}

if (array_key_exists("arroz", $estoque)) {
    echo "Tem arroz\n";
} else {
    echo "Sem arroz no cadastro\n";
}

A saída esperada, no PHP de verdade:

$ php estoque.php
café: 12
pão: 40
leite: 15                « mudou de valor, mas continua no 3º lugar
açúcar: 7                « o novo item entra no fim
Sem arroz no cadastro

Exercícios

1. A setinha

Complete o símbolo que liga a chave ao valor:

$produto = ["nome" "café"];

2. Chave e valor no foreach

Complete para $campo receber a chave e $valor o valor:

foreach ($aluna as ) { … }

3. Existe, mesmo que vazio?

Complete com a função que responde true mesmo quando o valor guardado é null:

("telefone", $aluna)

4. Com $a = ["x" => null];, quanto vale isset($a["x"])?

5. Quantos itens tem ["1" => "a", 1 => "b", "01" => "c"]?

6. Qual linha mostra Olá, Ana sem erro, sendo $u = ["nome" => "Ana"]?


Resumo

  • No array associativo, cada valor tem uma chave com nome: ["nome" => "Ana"]. Leia com $a["nome"].
  • $a["chave"] = valor; troca (se existe) ou cria (se não existe). unset apaga.
  • Chave que não existe: Warning: Undefined array key "…". As chaves diferenciam maiúsculas.
  • isset é false para valor null; array_key_exists só olha se a chave existe.
  • foreach ($a as $chave => $valor) percorre os pares, na ordem em que foram inseridos.
  • Chaves são só int ou string; "1" vira 1, true vira 1, e float e null geram Deprecated.
  • Em texto: "{$a['nome']}". Fora dele, a chave sempre entre aspas.