文字

extract

(PHP 4, PHP 5, PHP 7)

extract从数组中将变量导入到当前的符号表

说明

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

本函数用来将变量从数组中导入到当前的符号表中。

检查每个键名看是否可以作为一个合法的变量名,同时也检查和符号表中已有的变量名的冲突。

参数

var_array

一个关联数组。此函数会将键名当作变量名,值作为变量的值。 对每个键/值对都会在当前的符号表中建立变量,并受到 extract_typeprefix 参数的影响。

必须使用关联数组,数字索引的数组将不会产生结果,除非用了 EXTR_PREFIX_ALL 或者 EXTR_PREFIX_INVALID

extract_type

对待非法/数字和冲突的键名的方法将根据 extract_type 参数决定。可以是以下值之一:

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
将变量作为引用提取。这有力地表明了导入的变量仍然引用了 var_array 参数的值。可以单独使用这个标志或者在 extract_type 中用 OR 与其它任何标志结合使用。

如果没有指定 extract_type,则被假定为 EXTR_OVERWRITE

prefix

注意 prefix 仅在 extract_type 的值是 EXTR_PREFIX_SAME EXTR_PREFIX_ALL EXTR_PREFIX_INVALID EXTR_PREFIX_IF_EXISTS 时需要。 如果附加了前缀后的结果不是合法的变量名,将不会导入到符号表中。前缀和数组键名之间会自动加上一个下划线。

返回值

返回成功导入到符号表中的变量数目。

更新日志

版本 说明
4.3.0 增加了 EXTR_REFS
4.2.0 引进 EXTR_IF_EXISTS EXTR_PREFIX_IF_EXISTS
4.0.5 函数可以返回被提取的变量数目。 增加了 EXTR_PREFIX_INVALID 。 从这时起 EXTR_PREFIX_ALL 也包括了对数字索引的处理。

范例

Example #1 extract() 例子

extract() 的一种可能用法是将 wddx_deserialize() 返回的结合数组中的内容导入到符号表变量中去。

<?php



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

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

?>

以上例程会输出:

blue, large, sphere, medium

$size 没有被覆盖,因为指定了 EXTR_PREFIX_SAME ,这使得 $wddx_size 被建立。如果指定了 EXTR_SKIP ,则 $wddx_size 也不会被建立。 EXTR_OVERWRITE 将使 $size 的值为“medium”, EXTR_PREFIX_ALL 将建立新变量 $wddx_color $wddx_size $wddx_shape

注释

Warning

不要对不能信任的数据使用 extract() ,例如用户的输入( $_GET $_FILES ,...)。如果这样做,举例说,要临时运行依赖于 register_globals 的老代码,要确保使用不会覆盖的 extract_type 值,例如 EXTR_SKIP ,并且要留意应该按照 variables_order 在 php.ini 里 定义的顺序来提取。

Note:

If you have register_globals turned on and you use extract() on $_FILES and specify EXTR_SKIP , you may be surprised at the results.

Warning

This is not recommended practice and is only documented here for completeness. The use of register_globals is deprecated and calling extract() on untrusted data such as $_FILES is, as noted above, a potential security risk. If you encounter this issue, it means that you are using at least two poor coding practices.

<?php



var_dump ( $testfile );
extract ( $_FILES EXTR_SKIP );
var_dump ( $testfile );
var_dump ( $testfile [ 'tmp_name' ]);

?>
You might expect to see something like the following:
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"
However, you would instead see something like this:
string(14) "/tmp/phpgCCPX8"
string(14) "/tmp/phpgCCPX8"
string(1) "/"

This is due to the fact that since register_globals is turned on, $testfile already exists in the global scope when extract() is called. And since EXTR_SKIP is specified, $testfile is not overwritten with the contents of the $_FILES array so $testfile remains a string. Because strings may be accessed using array syntax and the non-numeric string tmp_name is interpreted as 0, PHP sees $testfile['tmp_name'] as $testfile[0] .

参见

  • compact() - 建立一个数组,包括变量名和它们的值
  • list() - 把数组中的值赋给一些变量

用户评论:

[#1] bob [2015-01-18 20:35:56]

Note that extract() will only create or overwrite variables in the current scope, so
<?php
function test(){
 
$a=Array('b'=>1,'c'=>2);
 
extract($a);
}
test();
exit(
"$b");
?>

will produce no output, whereas
<?php
function test(){
 global 
$b;
 
$a=Array('b'=>1,'c'=>2);
 
extract($a);
}
test();
exit(
"$b");
?>

will output 1.

[#2] dmikam [2014-09-18 10:18:25]

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.

[#3] CertaiN [2014-01-10 16:38:25]

[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) ""
  }
}

[#4] CertaiN [2014-01-10 16:37:25]

[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

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;
}
?>

[#5] FredLawl [2013-05-13 02:56:25]

It is possible to use this as a way to create public attributes for a class.

<?php
class Foo {

  public function 
__construct ($array) {

      
extract($arrayEXTR_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
?>

[#6] dotslash.lu at gmail.com [2013-04-29 12:55:44]

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

[#7] Robc [2011-11-01 06:30:51]

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.

[#8] Rodrigo [2010-06-15 21:41:38]

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.

[#9] auto493097 at hushmail dot com [2010-02-23 10:07:41]

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.

[#10] danbettles at yahoo dot co dot uk [2009-08-24 04:04:58]

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'
?>

[#11] benjaminATwebbutvecklarnaDOTse [2008-03-16 19:15:28]

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;
}
}

[#12] benjaminATwebbutvecklarnaDOTse [2008-03-16 18:19:36]

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.

[#13] Hayley Watson [2008-03-13 15:18:00]

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.

[#14] Dan O'Donnell [2007-04-21 12:25:56]

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.

[#15] Dutchdavey [2007-03-13 00:26:20]

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.

[#16] ktwombley at gmail dot com [2006-08-31 10:05:10]

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)

[#17] nicolas zeh [2006-03-16 09:24:12]

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==? 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==)
                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$myobjectEXTR_IF_EXISTS );
?>

[#18] owk dot ch199_ph at gadz dot org [2006-03-08 04:21:59]

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.

[#19] moslehi<atsign>gmail<d0t>c0m [2006-01-20 13:32:23]

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)

[#20] Csaba at alum dot mit dot edu [2005-11-27 11:41:22]

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.

[#21] anon at anon dot org [2005-05-30 12:02:01]

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!

[#22] kake26 at gmail dot com [2005-04-30 20:59:19]

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($resultMYSQL_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($myassocEXTR_OVERWRITE);
// now you're key and value pairs from $_POST have been restored
// instead of $_POST
?>

[#23] pg dot perfection at gmail dot com [2005-03-14 14:33:43]

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

function extract_nested (&$array$type EXTR_OVERWRITE$prefix '')
{
    

    
if (!is_array ($array))
    {
        return 
trigger_error ('extract_nested (): First argument should be an array'E_USER_WARNING);
    }

    

    
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);
    }

    

    
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);
    }

    

    
$prefix $prefix '_';

    

    
foreach ($array as $key => $val)
    {
        

        
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;
            }
        }
        

        
else
        {
            
extract_nested ($array[$key], $type$prefix);
        }
    }
}
?>

[#24] Michael Newton [2005-03-02 17:23:13]

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:
<?php
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!)

[#25] [2005-02-21 19:31:36]

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';

[#26] Aaron Stone [2004-11-17 10:44:41]

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.

[#27] Adam Monsen <adamm at wazamatta dot com> [2004-10-02 17:03:25]

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.

上一篇: 下一篇: