extract

(PHP 4, PHP 5, PHP 7, PHP 8)

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

描述

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

将变量从数组导入当前符号表。

检查每个键以查看它是否具有有效的变量名。它还检查与符号表中现有变量的冲突。

警告

不要对不可信数据(如用户输入(例如 $_GET$_FILES))使用 extract()

参数

array

一个关联数组。此函数将键视为变量名,将值视为变量值。对于每个键/值对,它将在当前符号表中创建一个变量,受 flagsprefix 参数的影响。

您必须使用关联数组;除非您使用 EXTR_PREFIX_ALLEXTR_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 进行 OR 运算将其与任何其他标志组合。

如果未指定 flags,则假定为 EXTR_OVERWRITE

prefix

请注意,只有当 flagsEXTR_PREFIX_SAMEEXTR_PREFIX_ALLEXTR_PREFIX_INVALIDEXTR_PREFIX_IF_EXISTS 时才需要 prefix。如果前缀后的结果不是有效的变量名,则不会将其导入符号表。前缀会自动用下划线字符与数组键隔开。

返回值

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

示例

示例 #1 extract() 示例

extract() 的一个可能的用途是将由 wddx_deserialize() 返回的关联数组中包含的变量导入到符号表中。

<?php

/* 假设 $var_array 是从
wddx_deserialize 返回的数组 */

$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_sizeEXTR_OVERWRITE 将导致 $size 的值为 “medium”,而 EXTR_PREFIX_ALL 将导致新变量名为 $wddx_color$wddx_size$wddx_shape

注意

警告

不要对不可信数据(例如用户输入(即 $_GET$_FILES 等))使用 extract()。如果这样做,请确保使用非覆盖 flags 值之一,例如 EXTR_SKIP,并注意您应该按 variables_orderphp.ini 中定义的顺序进行提取。

另请参阅

  • compact() - 创建包含变量及其值的数组
  • list() - 将变量分配为数组

添加笔记

用户贡献的笔记 31 笔

up
19
Michael Newton
19 年前
他们说“如果结果不是有效的变量名,则不会将其导入符号表。”

他们应该说的是,如果 _任何_ 结果具有无效的名称,则_所有_ 变量都不会被提取。

在 Windows 2000 上的 4.3.10 下,我正在提取一些 mySQL 记录,但需要将两个字段转换为 IP 地址
<?
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')));
?>

我在 SQL 查询中忘记了第二个 AS 修饰符。因为它无法将名为 INET_NTOA(baz) 的变量提取到符号表中,所以它没有执行任何操作。

(顺便说一句,我通常不会像那样堆叠函数!只是为了做一个简短的例子!)
up
9
FredLawl
11 年前
可以将此用作为创建类公共属性的一种方法。

<?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
6
Dan O'Donnell
17 年前
继续讨论 ktwombley 在 gmail dot com 的帖子

可以预见的是,处理此安全问题的一种简单方法是使用 EXTR_IF_EXISTS 标志,并确保

a) 提前定义可接受的输入变量(例如,作为空变量)
b) 对任何用户输入进行清理,以避免出现不可接受的变量内容。

如果你做了这两件事,我不确定 extract($_REQUEST,EXTR_IF_EXISTS); 和手动分配每个变量之间有什么区别。

我这里不是在谈论将变量存储在数据库中的想法,而只是为了允许你相对安全地对 REQUEST 数组使用 extract 的立即必要步骤。
up
2
amolocaleb 在 gmail dot com
6 年前
如果将一个对象强制转换为数组并“提取”,则只有公共属性可以访问。方法当然会被忽略。
<?php
class Test{
public
$name = '';

protected
$age = 10;

public
$status = 'disabled';

private
$isTrue = false;

public function
__construct()
{
$this->name = 'Amolo';
$this->status = 'active';
}

public function
getName()
{
return
$this->name;
}

public function
getAge()
{
return
$this->age;
}

public function
getStatus()
{
return
$this->status;
}

}

$obj = (array) new Test();
var_dump($obj);
/* array(4) { ["name"]=> string(5) "Amolo" ["*age"]=> int(10) ["status"]=> string(6) "active" ["TestisTrue"]=> bool(false) } */
extract((array)new Test());
echo
$name; //Amolo
echo $status; //active
echo $age;//Notice: Undefined variable: age
echo $isTrue;//Notice: Undefined variable: isTrue
up
5
Csaba 在 alum dot mit dot edu
18 年前
有时你可能想只提取数组中键值对的一个命名子集。这可以让事情井然有序,并可以防止无关变量因错误的键而被覆盖。例如,

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

将提取
$my = 'is';
$important = 'test';
$info = 2;

但会保留某些
$things = 'unsaid'

来自维也纳的 Csaba Gabor
注意:当然,来自网页的组合请求位于 $_REQUEST 中。
up
7
Robc
12 年前
在使用例如数据库查询后从行中提取时

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

我发现结果变量可能与数据库中的变量类型不匹配。我特别发现数据库中的整数在提取的变量上可能 gettype() 为字符串。
up
9
dmikam
9 年前
我做了一些测试来比较以下结构的速度
<?php

extract
($ARRAY);

// vs.

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

令我惊讶的是,extract 比 foreach 结构慢 20%-80%。我不太明白为什么,但事实如此。
up
1
Hayley Watson
16 年前
Dan O'Donnell 的建议需要第三个条件才能像描述的那样工作

c) 没有其他变量被定义 - 特别是包含潜在敏感信息的变量。

如果没有这个条件,extract() 和手动分配变量(以及由此产生的安全隐患)之间的区别应该很明显。

那里唯一有效的安全步骤是 (b) - 但你应该始终这样做。
up
1
owk dot ch199_ph 在 gadz dot org
18 年前
如果你想用 PHP 5 以简单的方式按引用提取 $V,试试这个
<?php
foreach ($V as $k => &$v) {
$
$k =& $v;
}
?>
它可以用来创建特殊类型的“自由参数”函数,这些函数让你在调用它们时可以选择发送变量的方式,以及哪些变量。此外,由于引用,它们调用速度非常快
<?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);
?>
请注意,你不能写:“<?php free_args (array ('arg1' => 'val1')); ?>",因为数组不能被函数引用,因为它在函数开始时还没有创建。
up
3
benjaminATwebbutvecklarnaDOTse
16 年前
回复:anon 在 anon dot org,关于 extract() 和空值

我个人发现从数据库中提取多个结果集很有用,因为在变量不为空时,后面的结果集会覆盖前面的结果集(可选地,如果它不为>0)。

如果 $extract_type 在这两个的基础上扩展,将会很有用

EXTR_OVERWRITE
EXTR_SKIP

像这样

EXTR_OVERWRITE_NULL
- 如果发生冲突,如果现有变量为空,则覆盖它。

EXTR_OVERWRITE_0
- 相同的,但 == 0 或空。

EXTR_SKIP_NULL
- 如果发生冲突,如果现有变量不为空,则跳过新变量。

EXTR_SKIP_0
- 相同的,但 == 0 或空。

- 这些应该涵盖目前未涵盖的几个好情况。
up
8
CertaiN
10 年前
[新版本]

示例用法
<?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,
)));
?>

示例结果
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
4
nicolas zeh
18 年前
此函数提供与 extract 相同的功能,只是添加了一个参数来定义提取目标。
如果您的 PHP 安装不支持所需的 Flags,或者更重要的是您希望将数组提取到除 $GLOBALS 之外的其他目标(例如其他数组或对象),则可以使用此函数。
与 extract 的唯一区别是 extract_to 将 $arr 的数组指针移动到末尾,因为 $arr 是通过引用传递的,以支持 EXTR_REFS 标志。

<?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(): 第一个参数应该是数组", E_USER_WARNING );

if(
is_array( $to ) ) $t=0;
else if(
is_object( $to ) ) $t=1;
else return
trigger_error("extract_to(): 第二个参数应该是数组或对象", 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(): 预期前缀被指定", 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;
}

// 例如:
extract_to( $myarray, $myobject, EXTR_IF_EXISTS );
?>
up
6
CertaiN
10 年前
[新版本]
此函数对于过滤复杂的数组结构非常有用。
此外,还提供了一些整数位掩码和无效 UTF-8 序列检测。

代码
<?php
/**
* @param integer $type 常量,例如 INPUT_XXX。
* @param array $default 指定超级全局变量的默认结构。
* 以下位掩码可用:
* + FILTER_STRUCT_FORCE_ARRAY - 强制一维数组。
* + FILTER_STRUCT_TRIM - 按 ASCII 控制字符修剪。
* + FILTER_STRUCT_FULL_TRIM - 按 ASCII 控制字符修剪,
* 全角和不间断空格。
* @return array 过滤后的超级全局变量的值。
*/
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
7
dotslash.lu at gmail.com
11 年前
您无法提取数字索引的数组(例如非关联数组)。
<?php
$a
= array(
1,
2
);
extract($a);
var_dump(${1});
?>

结果
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
2
phatsk+php at gmail dot com
6 年前
使用 extract 的返回值参数会导致意想不到的结果,特别是使用 EXTR_REFS 时

<?php

$my_data
= [
'count' => 15,
'name' => 'foo',
];

$count = extract( $my_data, EXTR_REFS );

echo
$my_data['count']; // 2, 不是 15.
up
3
mrkhoa99 at gmail dot com
6 年前
我们可以使用 extract() 函数来实现模板引擎

<?php
#Template.php

class Template
{
protected
$viewVars;

public function
renderPage($tpl)
{
ob_start();
extract($this->viewVars, EXTR_SKIP);
include
$tpl;
return
ob_end_flush();
}

public function
assign($arr)
{
foreach (
$arr as $key => $value) {
$this->viewVars[$key] = $value;
}
return
$this;
}
}

$template = new Template();
$template->assign(
[
'pageHeader' => 'Page Header', 'content' => 'This is the content page']
);
$template->renderPage('tpl.php');

#tpl.php

<h1><?= $pageHeader; ?></h1>
<p><?= $content ;?></p>

输出

Page Header
This is the content page
up
2
Adam Monsen <adamm at wazamatta dot com>
19 年前
如示例所示,如果使用“前缀”,将在提取的变量名称前面添加一个下划线。这意味着前缀“p”将变成“p_”的前缀,因此“blarg”的前缀将是“p_blarg”。

如果您不确定通过提取创建了哪些变量,可以调用 get_defined_vars() 来查看当前作用域中定义的所有变量。
up
4
ktwombley at gmail dot com
17 年前
使用 extract() 在 $_REQUEST、$_GET 等上很容易打开巨大的安全漏洞。您必须非常确定自己在做什么,并使用 extract() 上的适当标志来避免破坏重要的变量。

例如,kake26 在 gmail dot com 提交的内容,不仅可以完美地模拟 register globals(这很糟糕),还会将其存储在数据库中,并在每次脚本运行时调用相同的变量(实际上允许攻击者通过一次攻击在每次脚本运行时攻击您的脚本)。 糟糕!

要修复它,您需要以创造性的方式使用标志。 例如,您可以使用 EXTR_PREFIX_ALL 而不是 EXTR_OVERWRITE。 当然,您还应该对表单元素进行清理,以确保其中没有 php 代码,以及确保任何非常重要的变量不在表单数据中。(例如经典的 $is_admin = true 攻击)
up
3
bob
9 年前
请注意,extract() 仅创建或覆盖当前范围内的变量,因此
<?
function test(){
$a=Array('b'=>1,'c'=>2);
extract($a);
}
test();
exit("$b");
?>
不会产生任何输出,而
<?
function test(){
global $b;
$a=Array('b'=>1,'c'=>2);
extract($a);
}
test();
exit("$b");
?>
将输出 1。
up
2
匿名
19 年前
为了使这一点非常清楚(希望如此),在字符串添加前缀时,始终添加一个下划线。
extract(array("color" => "blue"),EXTR_PREFIX_ALL,'');// 注意:前缀为空

$color='_blue';
up
1
anon at anon dot org
19 年前
关于 extract() 和空值的警告。

这可能是一个真正的 Zend2 引擎错误,但这是一个糟糕的编程实践,因此我在这里分享它。

我经常在 E_STRICT(会阻止像这样的错误)未开启的环境中工作,而且我无法更改它。 我还使用一个非常简单的模板类,它本质上是这样工作的

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

display() 基本上看起来像这样

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

如果任何分配的值为空(假设在这种情况下 $title 没有在上面初始化),它会导致引擎执行各种令人难以置信的奇怪操作,例如以极其不一致的方式完全失去对变量的跟踪。 我将问题追溯到它使用 EXTR_REFS 标志的事实。 我假设在 PHP 的内部变量存储或引用计数机制中,尝试提取空引用会使其丢失或计数某些东西。

简而言之,如果您在使用 extract() 时开始出现奇怪的行为,请确保您要从中获取变量的数组或对象不包含空键或值!
up
1
Dutchdavey
17 年前
我想提醒您注意本页末尾的用户说明,该说明涉及前缀。 用户指出 php 在您的前缀中添加了一个 '_'.
up
0
darrenforster99 at gmail dot com
3 年前
回应 Dan O'Donnell 的说明

"大概处理此安全问题的简单方法是使用 EXTR_IF_EXISTS 标志并确保"

不一定 - 即使使用 EXTR_IF_EXISTS 标志也可能极其危险 - 想象一下这段代码在运行...

<?php

global $sql ;

function
runSql ()
{
$result = $conn->query($sql);
$conn-> close();
return
$result;
}

function
extractGet ()
{
$name = '' ;
$address = '' ;
foreach (
$_GET as $key => $value )
$_GET[$key] = urldecode ( $value ) ;
extract($_GET,EXTR_IF_EXISTS);
$sql = str_replace ( '{NAME}', $name, $sql ) ;
$sql = str_replace ( '{ADDRESS}', $address, $sql ) ;
}

function
outputResult ( $res )
{
echo
'<pre>'.print_r ( $res->fetch_array(MYSQLI_NUM) ).'</pre>' ;
}

$sql = 'SELECT postcode FROM Customers WHERE name={NAME} AND address={ADDRESS}';
extractGet () ;
$res = runSql () ;
outputResult ( $res ) ;
?>

现在您可以看到这里存在一个巨大的安全问题吗?...

如果我们有类似的 url,这看起来很好

mycode.php?name=joe%20bloggs&address=20%20Any%20Street

因为这会专门找到 20 Any Street 的 joe bloggs。

但是,如果有人输入

mycode.php?sql=SELECT%20password%20FROM%20Customers

或者更糟糕的是

mycode.php?sql=DELETE%20from%20Customers

这里的问题是,尽管您知道您已将 name 和 address 定义为该函数中仅有的两个空变量 - 您可能忘记了全局变量,这些全局变量可能导致重大安全问题

在上面的示例中,$sql 被定义为 "SELECT postcode FROM Customers WHERE name={NAME} AND address={ADDRESS}",这看起来很好,很安全,然后在 extractGet 函数中,str_replace 后来用 $_GET 中的 name 和 address 变量替换了 {NAME} 和 {ADDRESS} - 但是,如果 GET 包含一个 SQL 变量,那么它将在 str_replace 函数之前覆盖全局 $sql 变量 - 如果 str_replace 函数没有找到匹配项,它只会返回原始字符串 - 在上面的两个示例中,SQL 字符串将是 "SELECT password FROM Customers",它在示例 outputResult 中只是打印从数据库中检索到的数据,因此在这个阶段,它可能会将所有客户的密码(希望已加密!)打印到屏幕上(糟糕!),或者在第二个示例中,SQL 字符串将是 "DELETE from Customers" - 没有 WHERE 子句,这将删除 Customers 表中的所有数据,当然,以下组合

SELECT table_name FROM information_schema.tables



DROP TABLE <table_name>

可能是一场真正的灾难!

当然,这只是一个基本示例,但很容易忘记全局变量,并且如果将 GET、REQUEST 或 POST 发送到 extract,这些全局变量很容易与 extract 一起使用,从而导致严重的安全风险!
up
0
auto493097 at hushmail dot com
14 年前
我使用 XDebug 和 NetbeansIDE 来分析和开发 PHP 代码。 调试 extract 语句时,变量列表中没有出现新的变量。 尽管所有由 extract 创建的变量都可以通过显式监视项进行检查,并且一旦 PHP 脚本使用这些变量,单个变量就会出现,但我不知道这是否是错误的配置、功能还是 XDebug 中的错误。
up
0
Aaron Stone
19 年前
如果您正在移植旧应用程序,并遵循上述建议,仅提取 _SERVER、_SESSING、_COOKIE、_POST、_GET,那么您忘记了提取 _FILES。 将 _FILES 放在最后并使用 EXTR_SKIP 不起作用,因为文件上传框的名称已设置为仅包含从先前提取中上传的文件的临时名称的变量(我没有测试过具体是哪一个,但是)。 一种解决方法是将 _FILES 放在最后并使用 EXTR_OVERWRITE。 这允许 extract 用包含文件上传信息的完整数组替换该仅包含临时名称的变量。
up
-2
pg dot perfection at gmail dot com
19 年前
以下是一个关于提取方法在需要递归工作(也处理嵌套数组)时应该如何工作的示例...

请注意,这只是一个示例,它可以用更简单、更高级的方式完成。

<?php
/**
* extract() 函数的嵌套版本。
*
* @param array $array 要从中提取变量的数组
* @param int $type 要用来覆盖的类型(遵循 PHP 5.0.3 中 extract() 的相同规则)
* @param string $prefix 当需要时,将用于变量的前缀
*/
function extract_nested (&$array, $type = EXTR_OVERWRITE, $prefix = '')
{
/**
* 数组是否真的是一个数组?
*/
if (!is_array ($array))
{
return
trigger_error ('extract_nested (): 第一个参数应该是数组', 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 (): 第三个参数应该以字母或下划线开头', E_USER_WARNING);
}

/**
* 检查是否需要前缀。如果需要并且为空,则返回错误。
*/
if (($type == EXTR_PREFIX_SAME || $type == EXTR_PREFIX_ALL || $type == EXTR_PREFIX_IF_EXISTS) && empty ($prefix))
{
return
trigger_error ('extract_nested (): 应该指定前缀', 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);
}
}
}
?>
up
-4
danbettles at yahoo dot co dot uk
14 年前
当使用 EXTR_PREFIX_ALL - 以及可能所有其他 EXTR_PREFIX_* 常量 - 以及数字索引的数组时,extract() 将在 前缀和索引之间添加一个下划线 ("_")。

<?php

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

print_r(get_defined_vars()); // 显示 $var_0 = 'foo' 和 $var_1 = 'bar'
?>
up
-5
moslehi<atsign>gmail<d0t>c0m
18 年前
通过实验,我发现调用 extract() 也会显示键的数量,即使键已设置且不是数字!也许我的定义更好。请查看以下脚本

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

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

(Arash Moslehi)
up
-7
benjaminATwebbutvecklarnaDOTse
16 年前
与此同时,我正在使用这个

// extract 替代方案
# 提取变量,其中新值高于阈值,或如果旧值在阈值或低于阈值(或变量未定义)
# 显然,关联数组是传递的明智之举
#
# 我完全确定有人会发现这个方法有明显的问题或错误
# 我甚至没有尝试比较 0 以外的其他值,所以如果你需要这样做,并且肯定会发现明显的缺陷,
# 请给我发邮件,我真的很想知道。
# benjaminATwebbutvecklarnaDOTse

// 使用示例
# 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
-16
kake26 at gmail dot com
19 年前
以下是如何使用 extract 来存储和操作大量表单数据的巧妙方法。我基本上遍历 $_POST,并将其用空格分隔键值对进行拼接。然后将其存储在数据库中,反转函数基本上将字符串拆分为数组。然后将索引数组转换为关联数组,然后使用 extract 来完成最终步骤,使其在程序中易于访问。我分享这些函数的主要原因是,我创建了一些大型的 Web 应用程序,这些应用程序在数据库中存储大量论坛数据,而这些函数使存储和检索数据变得非常快速和容易。我贡献了这些函数,因为我花了很多时间创建它们,并记得当时想“我希望有人之前提交到页面笔记”。这将为我节省大量时间和痛苦,而且我相信我不是唯一能真正从这些函数中受益的人,所以我决定分享。

<?php
$stack
= array();
foreach (
$_POST as $key => $value) {
array_push($stack, $key, $value);
}
// 存储它
$block = implode(" ",$stack); // 生成空格分隔的字符串
// 插入查询以将字符串存储在 DB 中,例如以下查询
$query = "INSERT INTO `sometable` VALUES('".$seluser."','".addslashes($block)."');";
$result = mysql_query($query) or die("块插入查询失败: " . mysql_error());
// 注意,在我的情况下,$seluser 是与该块关联的用户 ID
// 在我的一个 Web 应用程序中
?>

好的一点是,通过上面的方法,我们可以从脚本获取的数据中快速创建一个键值对字符串。而不用关心它们的名称。您知道,如果注册全局变量打开,您会说 $someformvar 而不是 $_POST["someformvar"];,基本上下面的代码读取这个之前创建的块并将其返回到那个状态。有点像持久的注册全局变量。

<?php
// 在这里插入查询以获取先前存储的字符串
$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);
// 现在您从 $_POST 获取的键值对已恢复
// 而不是 $_POST
?>
up
-23
Rodrigo
14 年前
以下是在不使用 register_gloabals 的情况下在 $_FILES 数组中使用 extract 的方法。

几周前我开始使用 extract,从那时起我的代码就变得非常干净。使用 $_POST 和 $_GET 数组是可以的,但一个丢失的双引号会导致很多问题。

另外,我在学校教授 PHP,这个函数让我的示例变得更容易。

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

希望这能帮助到大家。
To Top