PHP 7.2.0 Beta 3 Released

extract

(PHP 4, PHP 5, PHP 7)

extract配列からシンボルテーブルに変数をインポートする

説明

int extract ( array &$array [, int $flags = EXTR_OVERWRITE [, string $prefix = NULL ]] )

配列からシンボルテーブルに変数をインポートします。

各キーについて変数名として有効であるかどうか、 そして、シンボルテーブルの既存の変数と衝突しないかどうかを確認します。

パラメータ

array

この関数は連想配列 var_array を引数とし、そのキーを変数名、値を変数の値として処理します。 各キー/値の組に関して、flags および prefix パラメータに基づき、 現在のシンボルテーブルに変数を一つ作成します。

連想配列を使用する必要があります。EXTR_PREFIX_ALL または EXTR_PREFIX_INVALID を使用しない限り、 数値添字の配列には結果は出力されません。

flags

無効または数値キーおよび衝突に関する対処法は、 flags で定義されます。 これは以下の値のどれかとなります。

EXTR_OVERWRITE
衝突があった場合、存在する変数が上書きされます。
EXTR_SKIP
衝突があった場合、存在する変数は上書きされません。
EXTR_PREFIX_SAME
衝突があった場合、prefix を前につけた新しい変数となります。
EXTR_PREFIX_ALL
全ての変数の前に prefix を付けます。
EXTR_PREFIX_INVALID
無効または数値の変数名のみに接頭辞 prefix を付けます。
EXTR_IF_EXISTS
現在のシンボルテーブルに既に存在する場合にのみ上書きします。 例えば $_REQUEST 以外にあなたが定義した変数のみを展開し 有効な変数としたいような場合に有用です。
EXTR_PREFIX_IF_EXISTS
同じ変数だが接頭辞をつけていないバージョンの変数が 現在のシンボルテーブルに存在する場合にのみ 変数を生成します。
EXTR_REFS
変数を参照として展開します。 これはインポート済みの変数が、 array パラメータの値に常に参照付けられることを意味します。 このフラグを単独で使用するか、 あるいはflagsと和算することにより、 他のフラグとそれを組み合わせることができます。

flags が指定されない場合、 EXTR_OVERWRITE とみなされます。

prefix

prefix は、 flagsEXTR_PREFIX_SAMEEXTR_PREFIX_ALLEXTR_PREFIX_INVALID あるいは EXTR_PREFIX_IF_EXISTS の場合にのみ必要であることに注意してください。 接頭辞を付けた変数名が有効な変数名でない場合、 この変数はシンボルテーブルにインポートされません。接頭辞は、 アンダースコア文字で配列のキーから自動的に分割されます。

返り値

シンボルテーブルにインポートした変数の数を返します。

例1 extract() の例

extract の使用例としては、シンボルテーブルに wddx_deserialize() から返された連想配列を インポートすることが考えられます。

<?php

/* $var_array はwddx_deserializeから返された配列と仮定します */

$size "large";
$var_array = array("color" => "blue",
                   
"size"  => "medium",
                   
"shape" => "sphere");
extract($var_arrayEXTR_PREFIX_SAME"wddx");

echo 
"$color$size$shape$wddx_size\n";

?>

上の例の出力は以下となります。

blue, large, sphere, medium

EXTR_PREFIX_SAME を指定したため、$size は上書きされず、$wddx_size が作成されます。 EXTR_SKIP が指定された場合、$wddx_size は作成されません。 EXTR_OVERWRITE の場合は、$size の値は "medium" となります。 EXTR_PREFIX_ALL の場合は新規の変数 $wddx_color, $wddx_size, $wddx_shape が作成されます。

注意

警告

extract() をユーザー入力 ($_GET$_FILES など) のような信頼できないデータについて使用しないでください。 もし行う場合、例えば register_globals を信頼しているような古いコードを一時的に実行したい場合、 EXTR_SKIP のような flags の値が上書きされていないことを確認してください。そして php.inivariables_order で定義されたものと同じ順で展開すべきであることに留意してください。

注意:

register_globals が on の状態で $_FILES に対して extract() を実行して EXTR_SKIP を指定すると、 その結果に驚くことでしょう。

警告

これは決しておすすめする方法ではなく、 単にドキュメントとしての完全性を求めてここに書いているだけです。 register_globals を使ったり、$_FILES のような信頼できないデータに対して extract() を使ったりすることは非推奨となっています。 先に説明したようにセキュリティ上のリスクがあるからです。 もしこの問題に遭遇したならば、 まずいコーディング作法を少なくともふたつ使っているということになります。

<?php

/* $testfile にはアップロードされたファイルの名前が入っており、
   register_globals が on であるものとします */

var_dump($testfile);
extract($_FILESEXTR_SKIP);
var_dump($testfile);
var_dump($testfile['tmp_name']);

?>
おそらくこんな結果になることを期待しているでしょう。
string(14) "/tmp/phpgCCPX8"
array(5) {
  ["name"]=>
  string(10) "somefile.txt"
  ["type"]=>
  string(24) "application/octet-stream"
  ["tmp_name"]=>
  string(14) "/tmp/phpgCCPX8"
  ["error"]=>
  int(0)
  ["size"]=>
  int(4208)
}
string(14) "/tmp/phpgCCPX8"
しかし、実際にはこのようになります。
string(14) "/tmp/phpgCCPX8"
string(14) "/tmp/phpgCCPX8"
string(1) "/"

これは、register_globals が on の場合は extract() がコールされた時点で既にグローバルスコープに $testfile が存在するからです。 そして EXTR_SKIP が指定されているので、 $testfile$_FILES 配列の内容で上書きされることはありません。 $testfile はそのまま文字列として残るわけです。 文字列は配列構文を使ってもアクセスでき、 非数値の文字列 tmp_name0 と解釈されるので、PHP は $testfile['tmp_name']$testfile[0] とみなします。

参考

  • compact() - 変数名とその値から配列を作成する
  • list() - 配列と同様の形式で、複数の変数への代入を行う

add a note add a note

User Contributed Notes 27 notes

up
16
CertaiN
3 years ago
[New Version]

Example Usage:
<?php
$_GET
['A']['a'] = '  CORRECT(including some spaces)    ';
$_GET['A']['b'] = '  CORRECT(including some spaces)    ';
$_GET['A']['c'] = "Invalid UTF-8 sequence: \xe3\xe3\xe3";
$_GET['A']['d']['invalid_structure'] = 'INVALID';

$_GET['B']['a'] = '  CORRECT(including some spaces)    ';
$_GET['B']['b'] = "Invalid UTF-8 sequence: \xe3\xe3\xe3";
$_GET['B']['c']['invalid_structure'] = 'INVALID';
$_GET['B']["Invalid UTF-8 sequence: \xe3\xe3\xe3"] = 'INVALID';

$_GET['C']['a'] = '  CORRECT(including some spaces)    ';
$_GET['C']['b'] = "Invalid UTF-8 sequence: \xe3\xe3\xe3";
$_GET['C']['c']['invalid_structure'] = 'INVALID';
$_GET['C']["Invalid UTF-8 sequence: \xe3\xe3\xe3"] = 'INVALID';

$_GET['unneeded_item'] = 'UNNEEDED';

var_dump(filter_struct_utf8(INPUT_GET, array(
   
'A' => array(
       
'a' => '',
       
'b' => FILTER_STRUCT_TRIM,
       
'c' => '',
       
'd' => '',
    ),
   
'B' => FILTER_STRUCT_FORCE_ARRAY,
   
'C' => FILTER_STRUCT_FORCE_ARRAY | FILTER_STRUCT_TRIM,
)));
?>

Example Result:
array(3) {
  ["A"]=>
  array(4) {
    ["a"]=>
    string(36) "  CORRECT(including some spaces)    "
    ["b"]=>
    string(30) "CORRECT(including some spaces)"
    ["c"]=>
    string(0) ""
    ["d"]=>
    string(0) ""
  }
  ["B"]=>
  array(3) {
    ["a"]=>
    string(36) "  CORRECT(including some spaces)    "
    ["b"]=>
    string(0) ""
    ["c"]=>
    string(0) ""
  }
  ["C"]=>
  array(3) {
    ["a"]=>
    string(30) "CORRECT(including some spaces)"
    ["b"]=>
    string(0) ""
    ["c"]=>
    string(0) ""
  }
}
up
9
dmikam
2 years ago
I have made some tests to compare the speed of next constructions:
<?php

extract
($ARRAY);

// vs.

foreach($ARRAY as $key=>$value)
    $
$key = $value;
?>

Surprisingly for me extract is 20%-80% slower then foreach construction. I don't really understand why, but it's so.
up
8
dotslash.lu at gmail.com
4 years ago
You can't extract a numeric indexed array(e.g. non-assoc array).
<?php
$a
= array(
 
1,
 
2
);
extract($a);
var_dump(${1});
?>

result:
PHP Notice:  Undefined variable: 1 in /Users/Lutashi/t.php on line 7

Notice: Undefined variable: 1 in /Users/Lutashi/t.php on line 7
NULL
up
6
CertaiN
3 years ago
[New Version]
This function is very useful for filtering complicated array structure.
Also, Some integer bitmasks and invalid UTF-8 sequence detection are available.

Code:
<?php
/**
* @param  integer $type    Constant like INPUT_XXX.
* @param  array   $default Default structure of the specified super global var.
*                          Following bitmasks are available:
*  + FILTER_STRUCT_FORCE_ARRAY - Force 1 dimensional array.
*  + FILTER_STRUCT_TRIM        - Trim by ASCII control chars.
*  + FILTER_STRUCT_FULL_TRIM   - Trim by ASCII control chars,
*                                full-width and no-break space.
* @return array            The value of the filtered super global var.
*/
define('FILTER_STRUCT_FORCE_ARRAY', 1);
define('FILTER_STRUCT_TRIM', 2);
define('FILTER_STRUCT_FULL_TRIM', 4);
function
filter_struct_utf8($type, array $default) {
    static
$func = __FUNCTION__;
    static
$trim = "[\\x0-\x20\x7f]";
    static
$ftrim = "[\\x0-\x20\x7f\xc2\xa0\xe3\x80\x80]";
    static
$recursive_static = false;
    if (!
$recursive = $recursive_static) {
       
$types = array(
           
INPUT_GET => $_GET,
           
INPUT_POST => $_POST,
           
INPUT_COOKIE => $_COOKIE,
           
INPUT_REQUEST => $_REQUEST,
        );
        if (!isset(
$types[(int)$type])) {
            throw new
LogicException('unknown super global var type');
        }
       
$var = $types[(int)$type];
       
$recursive_static = true;
    } else {
       
$var = $type;
    }
   
$ret = array();
    foreach (
$default as $key => $value) {
        if (
$is_int = is_int($value)) {
            if (!(
$value | (
               
FILTER_STRUCT_FORCE_ARRAY |
               
FILTER_STRUCT_FULL_TRIM |
               
FILTER_STRUCT_TRIM
           
))) {
               
$recursive_static = false;
                throw new
LogicException('unknown bitmask');
            }
            if (
$value & FILTER_STRUCT_FORCE_ARRAY) {
               
$tmp = array();
                if (isset(
$var[$key])) {
                    foreach ((array)
$var[$key] as $k => $v) {
                        if (!
preg_match('//u', $k)){
                            continue;
                        }
                       
$value &= FILTER_STRUCT_FULL_TRIM | FILTER_STRUCT_TRIM;
                       
$tmp += array($k => $value ? $value : '');
                    }
                }
               
$value = $tmp;
            }
        }
        if (
$isset = isset($var[$key]) and is_array($value)) {
           
$ret[$key] = $func($var[$key], $value);
        } elseif (!
$isset || is_array($var[$key])) {
           
$ret[$key] = null;
        } elseif (
$is_int && $value & FILTER_STRUCT_FULL_TRIM) {
           
$ret[$key] = preg_replace("/\A{$ftrim}++|{$ftrim}++\z/u", '', $var[$key]);
        } elseif (
$is_int && $value & FILTER_STRUCT_TRIM) {
           
$ret[$key] = preg_replace("/\A{$trim}++|{$trim}++\z/u", '', $var[$key]);
        } else {
           
$ret[$key] = preg_replace('//u', '', $var[$key]);
        }
        if (
$ret[$key] === null) {
           
$ret[$key] = $is_int ? '' : $value;
        }
    }
    if (!
$recursive) {
       
$recursive_static = false;
    }
    return
$ret;
}
?>
up
4
ktwombley at gmail dot com
10 years ago
It's really easy to open gaping security holes using extract() on $_REQUEST, $_GET, etc. You have to be really sure of what you're doing, and use the proper flags on extract() to avoid clobbering important variables.

For instance, the submission by kake26 at gmail dot com will not only perfectly emulate register globals (that's bad), but it'll store it in a database and recall the same variables every time the script runs (essentially allowing an attacker to attack your script every time it runs via one attack). Oops!

To fix it, you'd have to get creative with flags. Maybe you could use EXTR_PREFIX_ALL instead of EXTR_OVERWRITE, for example. Of course, you should also sanitize the form elements to ensure there's no php code in them, and also to make sure any very important variables aren't in the form data. (like the classic $is_admin = true attack)
up
3
Csaba at alum dot mit dot edu
11 years ago
Sometimes you may want to extract only a named subset of the key/value pairs in an array.  This keeps things more orderly and could prevent an unrelated variable from getting clobbered from an errant key.  For example,

$things = 'unsaid';
$REQUEST = array(He=>This, said=>1, my=>is, info=>2, had=>a,
                 very=>3, important=>test, things=>4);
$aVarToExtract = array(my, important, info);
extract (array_intersect_key ($REQUEST, array_flip($aVarToExtract)));

will extract
$my = 'is';
$important = 'test';
$info = 2;

but will leave certain
$things = 'unsaid'

Csaba Gabor from Vienna
NB.  Of course the composite request coming in from a web page is in $_REQUEST.
up
7
Robc
5 years ago
When extracting from a row after a database query using for example:

$row = mysql_fetch_array($result, MYSQL_ASSOC)
extract($row);

I find that the resultant variables may not match the variable type in the database.  In particular I have found integers in the database may gettype() to string on the extracted variable.
up
4
Dan O'Donnell
10 years ago
Following up on ktwombley at gmail dot com's post:

Presumably one easy way of dealing with this security issue is to use the EXTR_IF_EXISTS flag and make sure

a) your define acceptable input variables beforehand (i.e. as empty variables)
b) Sanitise any user input to avoid unacceptable variable content.

If you do these two things, then I'm not sure I see the difference between extract($_REQUEST,EXTR_IF_EXISTS); and assigning each of the variables by hand.

I'm not talking here about the idea of storing the variables in a database, just the immediately necessary steps to allow you to use extract on REQUEST arrays with relative safety.
up
2
Anonymous
12 years ago
To make this perfectly clear (hopefully), an underscore is always added when the string is prefixed.
extract(array("color" => "blue"),EXTR_PREFIX_ALL,'');// note: prefix is empty
is the same as
$color='_blue';
up
4
Michael Newton
12 years ago
They say "If the result is not a valid variable name, it is not imported into the symbol table."

What they should say is that if _any_ of the results have invalid names, _none_ of the variables get extracted.

Under 4.3.10 on Windows 2000, I was pulling some mySQL records, but needed to convert two fields into IP addresses:
<?
extract
(mysql_fetch_assoc(mysql_query('SELECT * FROM foo')));
extract(mysql_fetch_assoc(mysql_query('SELECT INET_NTOA(bar) AS bar, INET_NTOA(baz) FROM foo')));
?>

I had forgotten the second AS modifier in the SQL query.  Because it couldn't extract a variable called INET_NTOA(baz) into the symbol table, it didn't do either of them.

(BTW I don't normally stack functions up like that!  Just to make a short example!)
up
2
nicolas zeh
11 years ago
This function provides exactly the same functionality as extract except that a parameter was added defining the extract target.
This function can be used if your PHP installation does not support the required Flags or more important if you would like to extract arrays to another destination as to $GLOBALS, i.e. other arrays or objects.
The only difference to extract is that extract_to moves the array pointer of $arr to the end as $arr is passed by reference to support the EXTR_REFS flag.

<?php
   
if( !defined('EXTR_PREFIX_ALL') ) define('EXTR_PREFIX_ALL', 3);
    if( !
defined('EXTR_PREFIX_INVALID') ) define('EXTR_PREFIX_INVALID', 4);
    if( !
defined('EXTR_IF_EXISTS') ) define('EXTR_IF_EXISTS', 5);
    if( !
defined('EXTR_PREFIX_IF_EXISTS') ) define('EXTR_PREFIX_IF_EXISTS', 6);
    if( !
defined('EXTR_REFS') ) define('EXTR_REFS', 256);
   
   
    function
extract_to( &$arr, &$to, $type=EXTR_OVERWRITE, $prefix=false ){
       
        if( !
is_array( $arr ) ) return trigger_error("extract_to(): First argument should be an array", E_USER_WARNING );
       
        if(
is_array( $to ) ) $t=0;
        else if(
is_object( $to ) ) $t=1;
        else return
trigger_error("extract_to(): Second argument should be an array or object", E_USER_WARNING );
       
        if(
$type==EXTR_PREFIX_SAME || $type==EXTR_PREFIX_ALL || $type==EXTR_PREFIX_INVALID || $type==EXTR_PREFIX_IF_EXISTS )
            if(
$prefix===false ) return trigger_error("extract_to(): Prefix expected to be specified", E_USER_WARNING );
            else
$prefix .= '_';
       
       
$i=0;
        foreach(
$arr as $key=>$val ){
           
           
$nkey = $key;
           
$isset = $t==1 ? isset( $to[$key] ) : isset( $to->$key );
           
            if( (
$type==EXTR_SKIP && $isset )
                || (
$type==EXTR_IF_EXISTS && !$isset ) )
                    continue;
           
            else if( (
$type==EXTR_PREFIX_SAME && $isset )
                || (
$type==EXTR_PREFIX_ALL )
                || (
$type==EXTR_PREFIX_INVALID && !preg_match( '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $key ) ) )
                   
$nkey = $prefix.$key;
                   
            else if(
$type==EXTR_PREFIX_IF_EXISTS )
                if(
$isset ) $nkey = $prefix.$key;
                else continue;

            if( !
preg_match( '#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $nkey ) ) continue;
           
            if(
$t==1 )
                if(
$type & EXTR_REFS ) $to->$nkey = &$arr[$key];
                else
$to->$nkey = $val;
            else
                if(
$type & EXTR_REFS ) $to[$nkey] = &$arr[$key];
                else
$to[$nkey] = $val;
           
           
$i++;
        }
       
        return
$i;
    }
   
   
// e.g.:
   
extract_to( $myarray, $myobject, EXTR_IF_EXISTS );
?>
up
1
pg dot perfection at gmail dot com
12 years ago
Here is a little example of how an extraction method should look like when it needs to work recursive (work on nested_arrays too)...

Note that this is only an example, it can be done more easily, and more advanced too.

<?php
/**
* A nested version of the extract () function.
*
* @param    array    $array        The array which to extract the variables from
* @param    int        $type        The type to use to overwrite (follows the same as extract () on PHP 5.0.3
* @param    string    $prefix        The prefix to be used for a variable when necessary
*/
function extract_nested (&$array, $type = EXTR_OVERWRITE, $prefix = '')
{
   
/**
     *  Is the array really an array?
     */
   
if (!is_array ($array))
    {
        return
trigger_error ('extract_nested (): First argument should be an array', E_USER_WARNING);
    }

   
/**
     *  If the prefix is set, check if the prefix matches an acceptable regex pattern
     * (the one used for variables)
     */
   
if (!empty ($prefix) && !preg_match ('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#', $prefix))
    {
        return
trigger_error ('extract_nested (): Third argument should start with a letter or an underscore', E_USER_WARNING);
    }

   
/**
     * Check if a prefix is necessary. If so and it is empty return an error.
     */
   
if (($type == EXTR_PREFIX_SAME || $type == EXTR_PREFIX_ALL || $type == EXTR_PREFIX_IF_EXISTS) && empty ($prefix))
    {
        return
trigger_error ('extract_nested (): Prefix expected to be specified', E_USER_WARNING);
    }

   
/**
     * Make sure the prefix is oke
     */
   
$prefix = $prefix . '_';

   
/**
     *  Loop thru the array
     */
   
foreach ($array as $key => $val)
    {
       
/**
         *  If the key isn't an array extract it as we need to do
         */
       
if (!is_array ($array[$key]))
        {
            switch (
$type)
            {
                default:
                case
EXTR_OVERWRITE:
                   
$GLOBALS[$key] = $val;
                break;
                case
EXTR_SKIP:
                   
$GLOBALS[$key] = isset ($GLOBALS[$key]) ? $GLOBALS[$key] : $val;
                break;
                case
EXTR_PREFIX_SAME:
                    if (isset (
$GLOBALS[$key]))
                    {
                       
$GLOBALS[$prefix . $key] = $val;
                    }
                    else
                    {
                       
$GLOBALS[$key] = $val;
                    }
                break;
                case
EXTR_PREFIX_ALL:
                   
$GLOBALS[$prefix . $key] = $val;
                break;
                case
EXTR_PREFIX_INVALID:
                    if (!
preg_match ('#^[a-zA-Z_\x7f-\xff]$#', $key{0}))
                    {
                       
$GLOBALS[$prefix . $key] = $val;
                    }
                    else
                    {
                       
$GLOBALS[$key] = $val;
                    }
                break;
                case
EXTR_IF_EXISTS:
                    if (isset (
$GLOBALS[$key]))
                    {
                       
$GLOBALS[$key] = $val;
                    }
                break;
                case
EXTR_PREFIX_IF_EXISTS:
                    if (isset (
$GLOBALS[$key]))
                    {
                       
$GLOBALS[$prefix . $key] = $val;
                    }
                break;
                case
EXTR_REFS:
                   
$GLOBALS[$key] =& $array[$key];
                break;
            }
        }
       
/**
         *  The key is an array... use the function on that index
         */
       
else
        {
           
extract_nested ($array[$key], $type, $prefix);
        }
    }
}
?>
up
1
FredLawl
4 years ago
It is possible to use this as a way to create public attributes for a class.

<?php
class Foo {

  public function
__construct ($array) {

     
extract($array, EXTR_REFS);
      foreach (
$array as $key => $value) {
         
$this->$key = $$key;
         
// Do: $this->key = $key; if $key is not a string.
     
}

  }

}

$array = array(
   
'valueOne'      => 'Test Value 1',
   
'valueTwo'      => 'Test Value 2',
   
'valueThree'    => 'Test Value 3'
);

$foo = new Foo($array);

// Works
echo $foo->valueOne; // Test Value 1
echo $foo->valueTwo; // Test Value 2

// Does not work!
echo $foo::$valueOne; // Fatal error:  Access to undeclared static property: Test::$valueOne
?>
up
1
Adam Monsen <adamm at wazamatta dot com>
12 years ago
As shown in the example, if your 'prefix' is used, a single underscore is added to the name of the extracted variable. Meaning, a prefix of 'p' becomes a prefix of 'p_', so 'blarg' prefixed would be 'p_blarg'.

If you're not sure what variables you've created through extraction, you can call get_defined_vars() to see all defined variables in the current scope.
up
1
benjaminATwebbutvecklarnaDOTse
9 years ago
Re: anon at anon dot org, about extract() and null values

Personally I've found use extracting multiple resultsets from db where the latter would overwrite the previous when a variable is not null ( and optionally if its not >0 )

It would be useful if $extract_type was extended on top of these two:

EXTR_OVERWRITE
EXTR_SKIP

with something like this:

EXTR_OVERWRITE_NULL
- If there is a collision, overwrite the existing variable if it is null

EXTR_OVERWRITE_0
- Same thing but == 0 or null

EXTR_SKIP_NULL
- If there is a collision, skip the new variable if the existing is not null

EXTR_SKIP_0
- Same thing but == 0 or null

Those ought to cover a few good cases that aren't covered now.
up
1
anon at anon dot org
12 years ago
A warning about extract() and null values.

This might be an actual Zend2 Engine bug, but it's bad programming practice, so I'm sharing it here instead.

I often work in envrionments where E_STRICT (which would prevent errors like this) isn't on, and I don't have access to change it. I also use a very simple template class that in a nutshell works like this:

$t = new Template('somefile.php');
$t->title = $title;
$t->body = $body;
$t->display();

display() more or less looks like this:

function display(){
    extract(get_object_vars($this),EXTR_REFS);
    ob_start(); include $this->templateFileName;
    return ob_get_clean();
}

If any of the assigned values are null (let's say that in this case $title wasn't initialized above) it causes the engine to do all sorts of incredibly whacky stuff like certifiably lose track of variables in an incredibly inconsistent way. I traced the problem down to the fact that it's using the EXTR_REFS flag. I assume that in PHP's internal variable storage or reference counting mechanism, that trying to extract null references makes it lose track or count of something or rather.

In a nutshell, if you start getting wierd behavior when using extract() make sure that the array or object you are trying to get variables out of doesn't contain null keys or values!
up
1
Dutchdavey
10 years ago
I would draw your attention to the user note at the very end of this page regarding PREFIXES. The user points out that php adds a '_' to your prefixes.
up
1
bob
2 years ago
Note that extract() will only create or overwrite variables in the current scope, so
<?
function test(){
$a=Array('b'=>1,'c'=>2);
extract($a);
}
test();
exit(
"$b");
?>
will produce no output, whereas
<?
function test(){
global
$b;
$a=Array('b'=>1,'c'=>2);
extract($a);
}
test();
exit(
"$b");
?>
will output 1.
up
2
owk dot ch199_ph at gadz dot org
11 years ago
And if you want with PHP 5 an easy way to extract $V by reference, try this :
<?php
   
foreach ($V as $k => &$v) {
        $
$k =& $v;
    }
?>
It can be used to create special kind of "free args" functions that let you choose when you call them the way you send variables, and which ones. They are moreover very fast to call thanks to references :
<?php
   
function free_args (&$V) {
        foreach (
$V as $k => &$v) {
            $
$k =& $v;
        }
        unset (
$k);  unset ($v);  unset ($V);

       
// be careful that if you need to extract $k, $v or $V variables you should find other names for them in the lines above (ie. $__k, $__v and $__V)
   
}

   
$huge_text = '...';

   
$a = array ('arg1' => 'val1', 'arg2' => &$huge_text); // in this call, only $arg2 will be a true reference in the function
   
free_args ($a);
?>
Be warned that you can't write : "<?php free_args (array ('arg1' => 'val1')); ?>" because the array can't be referenced by the function, as it's not yet created when the function starts.
up
1
auto493097 at hushmail dot com
7 years ago
I use XDebug with NetbeansIDE to for analyzing and developing PHP Code. When debugging an extract statement no new variables appeared in the variable's list. Although all variables created by extract could be examined by explicit watch items and single variables appeared as soon as an PHP script makes use of them I am not sure weather it is a wrong configuration, a feature or a bug in XDebug.
up
1
Hayley Watson
9 years ago
Dan O'Donnell's suggestion needs a third requirement to work as described:

c) No other variables are defined - especially variables that contain potentially sensitive information.

Without that condition the difference between extract() and assigning variables by hand (and the resulting security implications) should be obvious.

The only valid security step there is (b) - but you should be doing that anyway.
up
1
Aaron Stone
12 years ago
If you are working porting an older application, and taking the advice above, extracting only _SERVER, _SESSING, _COOKIE, _POST, _GET, you have forgotten to extract _FILES. Putting _FILES last and using EXTR_SKIP doesn't work because the name of the file upload box is already set as a variable containing only the temporary name of the uploaded file from one of the earlier extracts (I haven't tested to see which one specifically, however). A workaround is to put _FILES last and use EXTR_OVERWRITE. This allows extract to replace that temp-name-only variable with the full array of file upload information.
up
-1
danbettles at yahoo dot co dot uk
7 years ago
When using EXTR_PREFIX_ALL - and probably all the other EXTR_PREFIX_* constants - and a numerically-indexed array, extract() will add an underscore ("_") between the prefix and the index.

<?php

extract
(array('foo', 'bar'), EXTR_PREFIX_ALL, 'var');

print_r(get_defined_vars());  // Reveals $var_0 = 'foo' and $var_1 = 'bar'
?>
up
-1
benjaminATwebbutvecklarnaDOTse
9 years ago
In the meantime, I'm using this:

// extract alternative
# extracts variables where new value is above the threshold or if old value is on or below the threshold (or var is not defined)
# an associative array is obviously the sane thing to pass
#
# I am absolutely certain someone will find obvious problems or errors with this
# I haven't even tried to compare other values than 0 so if you need to do that and surely finds obvious flaws,
# please mail me, I'd really like to know.
# benjaminATwebbutvecklarnaDOTse

// usage example:
# thrextract(mysql_fetch_assoc(mysql_query("SELECT preset_this,preset_that FROM site_preset WHERE ID = $site_id")));
# thrextract(mysql_fetch_assoc(mysql_query("SELECT preset_this,preset_that FROM category_preset WHERE ID = $category_id")));

function thrextract($arr,$thr = 0){
    foreach($arr as $key => $var){
        global $$key;
        if($var > $thr or $$key <= $thr) $$key = $var;
    }
}
up
-2
moslehi<atsign>gmail<d0t>c0m
11 years ago
Experimentally I found that calling extract() also shows the number of keys if the key is set and is not numeric ! Maybe there was a better definition than mine  . Please have a look to this scripts :

<?PHP
$var
["i"] = "a";
$var["j"] = "b";
$var["k"] = 1;
echo
extract($var); // returns 3
?>

<?PHP
$var2
["i"] = "a";
$var2[2] = "b";
$var2[] = 1;
echo
extract($var2); // returns 1
?>

(Arash Moslehi)
up
-11
kake26 at gmail dot com
12 years ago
The following is a neat use for extract to store and manipulate large amounts of form data from. I basically loop through the $_POST and implode it seperating the key and value pairs by a space. Then store it in a db, the reversing function basically explodes the string to a array. Then converts the indexed array to a associative array then uses extract to seal the deal and make it easily available within a program. My main reason for sharing these are the fact I make some big web applications that store allot of forum data in a DB and these functions make it very easy to quickly and easily store and recall the data. I've contributed it because I spent many hours creating this code and recall going "I wish someone had previously submitted it to the page notes". Would have saved me allot of time and agony and I'm sure I'm not the only person that could really benefit from it, so I decided to share.

<?php
$stack
= array();
foreach (
$_POST as $key => $value) {
array_push($stack, $key, $value);
}
// store it
$block = implode(" ",$stack); // yeilds a space delimited string
// insert query to store string in DB here, like the one below
$query = "INSERT INTO `sometable` VALUES('".$seluser."','".addslashes($block)."');";
$result = mysql_query($query) or die("Query failed for block insert: " . mysql_error());
// note $seluser in my case is a user ID associated with that block
// in one of my web apps
?>

The nice thing is with the above we can quickly create a string of key and value pairs from the data the script got. Without really caring what their names are. You know how if register globals are on you say $someformvar rather than $_POST["someformvar"]; , basically the code below reads this previous created block returns it to that state. Sort of like presistant register globals.

<?php
// insert query to grab the previously stored string here
$query = "SELECT * FROM `sometable` WHERE `blockid` = '".addslashes($bid)."';";
$result = mysql_query($query) or die("Query failed read: " . mysql_error());
$sql = mysql_fetch_array($result, MYSQL_ASSOC);
$array = eplode(" ",$sql["data"]);
for (
$i = 0; $i < sizeof($array); $i+=2) {
$myassoc[$array[$i]] = isset($array[$i+1])?$array[$i+1]:NULL;
}
extract($myassoc, EXTR_OVERWRITE);
// now you're key and value pairs from $_POST have been restored
// instead of $_POST
?>
up
-12
Rodrigo
7 years ago
Here's a way to use extract in $_FILES arrays without using register_gloabals on.

I started to use extract a few weeks ago, and my codes hasn't been so clean since then. The use of the arrays $_POST and $_GET is ok, but one missed doublequote causes a lot of trouble.

Besides I teach PHP in a school, and this function has made my examples easier.

<?php
if(isset($_FILES["file"])){
   
extract($_FILES);
   
extract($file);
    echo
$name."<br>";
    echo
$tmp_name."<br>";
    echo
$size."<br>";
}
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Documento sin t&iacute;tulo</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<form action="" method="post" enctype="multipart/form-data" name="form1">
  <p>
    <input type="file" name="file">
</p>
  <p>
    <input type="submit" name="Submit" value="Enviar">
</p>
</form>
</body>
</html>

Hope this can help anyone.
To Top