Arrays7 min readExamples run on PHP 8.5.10

extract()

Learn how PHP 8.4 extract() turns array keys into local variables, how its flags handle collisions and prefixes, and why it is risky with untrusted data.

Signature

extract(array &$array, int $flags = EXTR_OVERWRITE, string $prefix = ""): int

Purpose

extract() copies the entries of an associative array into the current symbol table. Each key becomes a variable name and each value becomes that variable's value. It is handy when a function receives a small, well-known array of fields and you want to use them as plain variables. It is also easy to misuse, because it creates variables that are invisible to a reader scanning the code.

Signature

Parameters and defaults

  • $array: the source array. Keys are treated as variable names. The parameter is a reference because the EXTR_REFS flag can bind the new variables to the array's elements.
  • $flags: controls how collisions and invalid names are handled. The default is EXTR_OVERWRITE.
  • $prefix: text placed in front of a name, joined to it with an underscore. It is only needed for the prefix-related flags.

The flags are:

  • EXTR_OVERWRITE: replace an existing variable on collision.
  • EXTR_SKIP: keep the existing variable on collision.
  • EXTR_PREFIX_SAME: on collision, create a prefixed variable instead.
  • EXTR_PREFIX_ALL: prefix every name.
  • EXTR_PREFIX_INVALID: prefix only invalid or numeric names.
  • EXTR_IF_EXISTS: overwrite only variables that already exist.
  • EXTR_PREFIX_IF_EXISTS: create prefixed variables only when the unprefixed one already exists.
  • EXTR_REFS: import references rather than copies. You can combine it with another flag using |.

Return value and failures

The function returns an int: the number of variables successfully imported. Keys that are not valid variable names are skipped and not counted. Under EXTR_SKIP, a skipped collision is not counted either. An invalid flag value, a missing prefix when the chosen flag requires one, or a prefix that is not a valid identifier raises a ValueError.

Example 1: basic extraction inside a function

The array has one key that cannot be a variable name, so only two variables are created.

Example
<?php
function describe(array $row): string {
    $count = extract($row);
    return "$count: $name is $age";
}

echo describe(['name' => 'Ada', 'age' => 36, 'bad key' => 1]), "\n";
Output
2: Ada is 36

Calling extract() inside a function keeps the new variables local to that function, which limits the damage if names collide.

Example 2: handling collisions with a prefix

Here $size already exists. With EXTR_PREFIX_SAME, the colliding key is stored as $item_size while $size keeps its value.

Example
<?php
$size = "large";
$data = ["color" => "blue", "size" => "medium"];
$count = extract($data, EXTR_PREFIX_SAME, "item");
echo json_encode([$count, $color, $size, $item_size], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
[2,"blue","large","medium"]

Both entries were imported, so the count is 2.

Example 3: numeric keys and references

A numeric key is not a valid variable name by itself. EXTR_PREFIX_ALL fixes that by giving every name a prefix. Adding EXTR_REFS makes the new variables references into the original array.

Example
<?php
$settings = ["debug" => false, 5 => "five"];
$count = extract($settings, EXTR_PREFIX_ALL | EXTR_REFS, "cfg");
$cfg_debug = true;
echo json_encode([$count, $settings["debug"], $cfg_5]), "\n";
Output
[2,true,"five"]

Assigning to $cfg_debug changed $settings["debug"] because the two are bound together. The array must be a real variable for this to work, not a temporary value.

Edge cases

  • A plain list such as ['a', 'b'] imports nothing with the default flag, because numeric keys are invalid names. Use EXTR_PREFIX_ALL or EXTR_PREFIX_INVALID with a prefix to import them.
  • If the prefixed result is still not a valid variable name, that entry is not imported.
  • EXTR_IF_EXISTS is useful for whitelisting: declare the variables you accept, then only those are updated.
  • An empty array imports nothing and returns 0.

Common mistakes

  1. Extracting untrusted input. Running extract() on request data lets a user choose variable names and overwrite your own. If you must, use EXTR_SKIP or a whitelist approach with EXTR_IF_EXISTS, but reading the array keys directly is safer.
  2. Relying on invisible variables. Readers and static analysis tools cannot see where $name came from. Prefer $row['name'] for anything beyond a tiny scope.
  3. Forgetting the prefix. Flags such as EXTR_PREFIX_ALL need a prefix argument. Omitting it is an error.
  4. Ignoring the return value. The count tells you how many keys were usable. A lower number than count($array) signals skipped keys.
  5. Expecting lists to work. Numerically indexed arrays are not imported with the default flags.
  6. Mixing up copy and reference. Without EXTR_REFS, changing the variable does not alter the array.
  • compact() does the reverse: it builds an array from named variables.
  • list() and array destructuring assign array values to variables explicitly, which is usually clearer than extract().

Recap

extract() imports array entries as variables and returns how many it created. Flags decide what happens on collisions, with invalid names and with references. Use it sparingly, inside small scopes, with trusted arrays, and prefer explicit destructuring when you can.

Tested examples

Basic extraction inside a function
<?php
function describe(array $row): string {
    $count = extract($row);
    return "$count: $name is $age";
}

echo describe(['name' => 'Ada', 'age' => 36, 'bad key' => 1]), "\n";
Output
2: Ada is 36
Handling collisions with EXTR_PREFIX_SAME
<?php
$size = "large";
$data = ["color" => "blue", "size" => "medium"];
$count = extract($data, EXTR_PREFIX_SAME, "item");
echo json_encode([$count, $color, $size, $item_size], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
[2,"blue","large","medium"]
Numeric keys and references
<?php
$settings = ["debug" => false, 5 => "five"];
$count = extract($settings, EXTR_PREFIX_ALL | EXTR_REFS, "cfg");
$cfg_debug = true;
echo json_encode([$count, $settings["debug"], $cfg_5]), "\n";
Output
[2,true,"five"]