如何避免 isset() 和 empty()

How to avoid isset() and empty()

提问人:Pekka 提问时间:12/25/2009 最后编辑:Pekka 更新时间:8/18/2020 访问量:26380

问:

我有几个较旧的应用程序在E_NOTICE错误级别运行时会抛出大量“xyz is undefined”和“undefined offset”消息,因为没有使用 和 consorts 显式检查变量的存在。isset()

我正在考虑通过它们使它们E_NOTICE兼容,因为关于缺少变量或偏移量的通知可以挽救生命,可能会获得一些小的性能改进,而且总体上是更干净的方式。

但是,我不喜欢对我的代码施加数百个和 s 的影响。它变得臃肿,变得不那么可读,在价值或意义方面没有任何收获。isset()empty()array_key_exists()

如何在不进行过多变量检查的情况下构建代码,同时又E_NOTICE兼容?

PHP 错误报告

评论

6赞 Richard Knop 11/6/2010
我完全同意。这就是为什么我非常喜欢 Zend Framework,那里的请求模块非常好。如果我正在开发一些小应用程序,我通常会用类似于 ZF 请求的神奇方法编写一些简单的请求类__set和__get。这样,我就可以避免在代码中出现所有 isset 和 empty。这样,在迭代数组之前,您需要使用的就是数组上的 if (count($arr) > 0),以及 if (null !== $variable) 在几个关键位置。它看起来干净多了。

答:

37赞 BalusC 12/25/2009 #1

只需为此编写一个函数即可。像这样:

function get_string($array, $index, $default = null) {
    if (isset($array[$index]) && strlen($value = trim($array[$index])) > 0) {
        return get_magic_quotes_gpc() ? stripslashes($value) : $value;
    } else {
        return $default;
    }
}

您可以将其用作

$username = get_string($_POST, 'username');

对琐碎的东西(如 、 等)执行相同的操作。get_number()get_boolean()get_array()

评论

5赞 Pekka 12/25/2009
这看起来不错,并且magic_quotes检查。好!
0赞 Mike Moore 8/10/2010
很棒的功能!非常感谢您的分享。
3赞 Ciantic 7/10/2012
请注意,$_POST['something'] 可能会返回数组,例如带有 .使用上述代码会导致错误(因为 trim 不能应用于数组),在这种情况下,应该使用 and 可能。这不仅仅是一个应该使用的情况,因为用户输入(恶意)可能是任何东西,用户输入解析器无论如何都不应该抛出错误。<input name="something[]" />is_stringstrvalget_array
1赞 Mat 8/14/2012
我使用相同类型的函数,但定义如下: function get_value(&$item, $default = NULL) { return isset($item) ? $item : $default; }此函数的优点是您可以使用数组、变量和对象来调用它。缺点是,如果不是,$item 会在之后初始化(为 null)。
0赞 Kayla 8/15/2014
您应该全局关闭魔术引号,而不是在 1 个函数中处理它们。互联网上有很多来源解释魔术名言。
130赞 deceze 12/25/2009 #2

对于那些感兴趣的人,我已将这个主题扩展为一篇小文章,它以结构更好的形式提供了以下信息: PHP isset 的权威指南 和空的


恕我直言,您不仅应该考虑使应用程序“E_NOTICE兼容”,还应该考虑重组整个事情。在代码中有数百个点经常尝试使用不存在的变量,这听起来像是一个结构相当糟糕的程序。尝试访问不存在的变量永远不应该发生,其他语言在编译时对此犹豫不决。PHP允许你这样做的事实并不意味着你应该这样做。

这些警告是为了帮助你,而不是为了惹恼你。如果你收到警告“你正在尝试使用不存在的东西!”,你的反应应该是“哎呀,我的错,让我尽快修复它。你怎么能区分“未定义工作的变量”可能导致严重错误的错误代码?这也是为什么你总是,总是,在开发时将错误报告调到11,并不断插入你的代码,直到没有一个发出。关闭错误报告仅适用于生产环境,以避免信息泄露,即使在面对有缺陷的代码时也能提供更好的用户体验。NOTICE


详细说明:

您将始终需要或在代码中的某个地方,减少它们出现的唯一方法是正确初始化变量。根据具体情况,有不同的方法可以做到这一点:issetempty

函数参数:

function foo ($bar, $baz = null) { ... }

无需检查函数内部是否设置了 or,因为您只需设置它们,您需要担心的是它们的值是否计算为 or(或其他任何值)。$bar$baztruefalse

任何地方的常规变量:

$foo = null;
$bar = $baz = 'default value';

在要使用变量的代码块的顶部初始化变量。这解决了问题,确保变量始终具有已知的默认值,让读者了解以下代码将用于什么,从而也是一种自我文档。!isset

阵 列:

$defaults = array('foo' => false, 'bar' => true, 'baz' => 'default value');
$values = array_merge($defaults, $incoming_array);

与上面的事情一样,您正在使用默认值初始化数组并用实际值覆盖它们。

在其余情况下,假设您输出的值可能由控制器设置,也可能不是由控制器设置的模板,您只需要检查:

<table>
    <?php if (!empty($foo) && is_array($foo)) : ?>
        <?php foreach ($foo as $bar) : ?>
            <tr>...</tr>
        <?php endforeach; ?>
    <?php else : ?>
        <tr><td>No Foo!</td></tr>
    <?php endif; ?>
</table>

如果您发现自己经常使用 ,您应该评估您使用它的目的。它唯一有所作为的时间是这里:array_key_exists

$array = array('key' => null);
isset($array['key']); // false
array_key_exists('key', $array); // true

如上所述,如果您正确地初始化了变量,则无需检查密钥是否存在,因为您知道它确实存在。如果您从外部源获取数组,则该值很可能不是 , , , 或类似的东西,即您可以根据自己的意图使用 或 计算的值。如果您经常将数组键设置为并希望它表示除 之外的任何含义,即如果在上面的示例中,不同的结果 和 对您的程序逻辑产生影响,您应该问自己为什么。变量的存在本身并不重要,只有它的值才有意义。如果键是 / 标志,则使用 或 ,而不是 。唯一的例外是想要表示某种含义的第三方库,但由于在 PHP 中很难检测到,我还没有找到任何这样做的库。null''0'0'falseissetemptynullfalseissetarray_key_existstruefalsetruefalsenullnullnull

评论

4赞 Pekka 12/25/2009
没错,但大多数失败的访问尝试都是沿着而不是或我发现有点合理的路线,当然不是结构问题(如果我弄错了,请纠正我)。添加对我来说似乎是一种可怕的浪费。if ($array["xyz"])isset()array_key_exists()array_key_exists()
9赞 deceze 12/25/2009
我想不出我会使用任何情况来代替简单的 or .当然,两者都会在代码中添加 7 或 8 个字符,但我很难称之为问题。它还有助于澄清您的代码:表示此变量确实是可选的,并且可能不存在,而 just 表示“如果为真”。如果你收到后一个的通知,你就知道你的逻辑在某个地方搞砸了。array_key_existsisset($array['key'])!empty($array['key'])if (isset($array['key']))if ($array['key'])
6赞 Htbaa 12/25/2009
我相信 isset() 和 array_key_exists() 之间的区别在于,如果值为 NULL,后者将返回 true。isset() 不会。
1赞 deceze 12/25/2009
没错,但我想不出一个理智的用例,我需要区分不存在的变量和值为 null 的集合键。如果值的计算结果为 FALSE,则区别应该没有区别。:)
1赞 kijin 11/3/2010
数组键肯定比未定义的变量更烦人。但是,如果您不确定数组是否包含键,则意味着您没有自己定义数组,或者您正在从您无法控制的源中提取它。这两种情况都不应该经常发生;如果它发生,你完全有理由检查数组是否包含你认为它的作用。这是IMO的一项安全措施。
3赞 vava 12/25/2009 #3

我和你在一起。但是PHP设计者犯了比这更严重的错误。除了为任何值读取定义自定义函数之外,没有任何方法可以绕过它。

评论

1赞 vava 12/25/2009
isset() 的东西。默认情况下,将所有内容设置为空将省去很多麻烦。
2赞 Lotus Notes 5/20/2010
这个“一切”是什么?对于PHP来说,必须想象每个可以想象的变量名称并将每个变量名称设置为NULL,这样懒惰的开发人员就可以避免输入5个字符,这似乎是一种浪费。
5赞 vava 5/20/2010
@Byron,看,这真的很简单,很多其他语言都是这样做的,Ruby 和 Perl 是少数例子。VM知道以前是否使用过变量,不是吗?它始终可以返回 null,而不是失败,无论是否有错误消息。这不是关于糟糕的 5 个字符,而是关于编写设置默认值,而不是所有那些废话的调用。params["width"] = params["width"] || 5isset()
3赞 staticsan 11/22/2010
对不起,复活了一个旧线程。PHP 最严重的两个错误是 和 .相比之下,这些培养的问题使未初始化的变量看起来几乎无害。register_globalsmagic_quotes
0赞 Mlutz 12/25/2009 #4

我不确定您对可读性的定义是什么,但正确使用 empty()、isset() 和 try/throw/catch 块对整个过程非常重要。

如果您的E_NOTICE来自 _GET 美元或 _POST 美元,那么应该根据 empty() 检查它们以及该数据必须通过的所有其他安全检查。

如果它来自外部源或库,则应将其包装在 try/catch 中。

如果它来自数据库,则应检查 $db_num_rows() 或其等效项。

如果它来自内部变量,则应正确初始化它们。通常,这些类型的通知来自将新变量分配给函数的返回值,该函数在失败时返回 FALSE。这些应该包装在一个测试中,在发生故障时,可以为变量分配代码可以处理的可接受默认值,也可以引发代码可以处理的异常。

这些东西使代码更长,添加额外的块,并添加额外的测试,但我不同意你的观点,因为我认为它们肯定会增加额外的价值。

6赞 Alix Axel 12/25/2009 #5

我不介意使用这个功能。事实上,我更喜欢使用这个特定的函数,而不是依赖 hack 函数,这些函数将来可能会改变它们的行为,比如 emptyisset(删除线以避免敏感性)。array_key_exists()


但是,我确实使用了一个简单的函数,该函数在这方面派上用场,并且在处理数组索引时还有其他一些情况:

function Value($array, $key, $default = false)
{
    if (is_array($array) === true)
    {
        settype($key, 'array');

        foreach ($key as $value)
        {
            if (array_key_exists($value, $array) === false)
            {
                return $default;
            }

            $array = $array[$value];
        }

        return $array;
    }

    return $default;
}

假设您有以下数组:

$arr1 = array
(
    'xyz' => 'value'
);

$arr2 = array
(
    'x' => array
    (
        'y' => array
        (
            'z' => 'value',
        ),
    ),
);

如何从数组中获取“价值”?简单:

Value($arr1, 'xyz', 'returns this if the index does not exist');
Value($arr2, array('x', 'y', 'z'), 'returns this if the index does not exist');

我们已经涵盖了一维和多维数组,我们还能做什么呢?


以以下代码为例:

$url = 'https://stackoverflow.com/questions/1960509';
$domain = parse_url($url);

if (is_array($domain) === true)
{
    if (array_key_exists('host', $domain) === true)
    {
        $domain = $domain['host'];
    }

    else
    {
        $domain = 'N/A';
    }
}
else
{
    $domain = 'N/A';
}

很无聊,不是吗?这是使用 Value() 函数的另一种方法:

$url = 'https://stackoverflow.com/questions/1960509';
$domain = Value(parse_url($url), 'host', 'N/A');

再举一个例子,RealIP() 函数为例进行测试:

$ip = Value($_SERVER, 'HTTP_CLIENT_IP', Value($_SERVER, 'HTTP_X_FORWARDED_FOR', Value($_SERVER, 'REMOTE_ADDR')));

整洁,是吧?;)

评论

6赞 deceze 12/25/2009
“依靠未来可能改变其行为的黑客功能”?!对不起,但这是我整个星期听到的最荒谬的事情。首先,是语言结构,而不是函数。其次,如果任何核心库函数/语言结构改变了它们的行为,你可能会也可能不会被搞砸。如果改变它的行为怎么办?答案是它不会,只要你按照文档使用它。并且被记录为完全如此使用。最坏情况下的函数在一两个主要发布版本中被弃用。美国国立卫生研究院综合症很糟糕!issetemptyarray_key_existsisset
0赞 Alix Axel 12/25/2009
对不起,欺骗,但首先hack是斜体的,以防你没有注意到。=) 其次,你的意思是不应该依赖来检查数组中是否存在键?! 正是为此而创建的,我宁愿依靠它来实现此目的,而不是特别是其官方描述是:“确定变量是否为空”,如果它真的存在,则没有提到任何事情。你的评论和反对票是我整个目睹的最荒谬的事情之一。array_key_exists()array_key_exists()isset()empty()
3赞 deceze 12/25/2009
我是说,它们或多或少可靠,可以做完全相同的工作。你的第二个冗长的例子可以写成只有核心语言功能,不需要额外的函数调用或声明(请注意,我不一定提倡使用三元运算符;o))。对于普通标量变量,您仍然需要使用 或 ,并且您可以以完全相同的方式将它们用于数组。“可靠性”是不这样做的一个坏理由。issetemptyarray_key_exists$domain = isset($domain['host']) ? $domain['host'] : 'N/A';issetempty
1赞 Alix Axel 12/25/2009
你提出了你的观点,尽管我不同意你所说的大部分内容。我认为您在 90%+ 的情况下弄错了,例如我一直在表单的隐藏字段中使用“0”的值。尽管如此,我仍然相信我提供的解决方案不值得投反对票,并且很可能对 Pekka 有所用处。
2赞 Pekka 12/25/2009
虽然@deceze自定义函数有道理 - 我通常采取同样的立场 - value() 方法看起来很有趣,我将研究它。我认为答案和后续行动将使每个后来偶然发现它的人都能做出自己的决定。+1.
0赞 knoopx 12/25/2009 #6

软件不会神奇地靠上帝的恩典运行。如果您期待缺少某些东西,则需要妥善处理它。

如果忽略它,则可能会在应用程序中创建安全漏洞。在静态语言中,访问未定义的变量是不可能的。如果它为 null,它不会简单地编译或崩溃您的应用程序。

此外,它使您的应用程序无法维护,当意外事情发生时,您会发疯。语言的严格性是必须的,而PHP在设计上在很多方面都是错误的。如果你不知道,它会让你成为一个糟糕的程序员。

评论

0赞 Pekka 12/25/2009
我很清楚PHP的不足之处。正如我在问题中指出的那样,我说的是旧项目的大修。
0赞 Dzhuneyt 9/7/2012
同意。作为一个 PHP 开发人员,我很难冒险进入像 Java 这样的新语言,因为你需要声明所有内容。
13赞 Jamol 12/25/2009 #7

我相信解决这个问题的最佳方法之一是通过类访问 GET 和 POST(COOKIE、SESSION 等)数组的值。

为每个数组创建一个类,并声明 and 方法(重载)。 接受一个参数,该参数将是一个值的名称。此方法应在相应的全局数组中检查此值,使用 或 并返回该值(如果存在)或(或其他默认值)否则。__get__set__getisset()empty()null

之后,您可以放心地以这种方式访问数组值:并在需要时执行任何验证,而无需使用任何 s 或 s。如果相应的全局数组中不存在,则将返回,因此不会生成警告或通知。$POST->usernameisset()empty()usernamenull

评论

1赞 Pekka 12/25/2009
这是一个好主意,我准备为之重构代码。+1
0赞 ThiefMaster 10/24/2010
不幸的是,除非您将这些实例分配给 _GET 美元或 _POST 美元,否则您将无法使这些实例成为超全局实例,这将是非常丑陋的。但是你当然可以使用静态类......
1赞 Mat 8/14/2012
你不能在“静态类”上使用 getter 和 setter。为每个变量编写一个类是不好的做法,因为它意味着代码重复,这是不好的。我不认为这个解决方案是最充分的。
0赞 velcrow 8/20/2013
类的公共静态成员的行为类似于超全局变量,即:HTTP::$POST->username,您可以在使用之前的某个时间点实例化 HTTP::$POST,即。HTTP类 { public static $POST = array();...};HTTP::$POST = new someClass($_POST);...
3赞 Jan Turoň 7/17/2011 #8

我使用这些功能

function load(&$var) { return isset($var) ? $var : null; }
function POST($var) { return isset($_POST[$var]) ? $_POST[$var] : null; }

例子

$y = load($x); // null, no notice

// this attitude is both readable and comfortable
if($login=POST("login") and $pass=POST("pass")) { // really =, not ==
  // executes only if both login and pass were in POST
  // stored in $login and $pass variables
  $authorized = $login=="root" && md5($pass)=="f65b2a087755c68586568531ad8288b4";
}

评论

2赞 Mat 8/14/2012
我也使用它,但请记住,在某些情况下,您的变量将自动初始化:例如 load($array['FOO']) 将在 $array 中创建一个 FOO 键。
-2赞 Mat 8/14/2012 #9

使用运算符怎么样?@

例如:

if(@$foo) { /* Do something */ }

你可能会说这很糟糕,因为你无法控制“内部”$foo发生的事情(例如,如果它是一个包含 PHP 错误的函数调用),但如果你只对变量使用这种技术,这相当于:

if(isset($foo) && $foo) { /* ... */ }

评论

0赞 Dzhuneyt 9/7/2012
if(isset($foo))其实就够了。如果表达式的计算结果为 。TRUETRUE
2赞 Jon Hulka 11/22/2012
@ColorWP.com 如果表达式的计算结果为 false,它也将返回 true。
0赞 rubo77 6/24/2017
您应该只在未真正进行进一步开发的代码上使用 @ 参数(忽略通知),或者对现有项目的一次性代码或快速修复使用,您不想向其他人展示。但这是快速黑客攻击的常见解决方法。
1赞 dragonfire 9/8/2014 #10

创建一个函数,如果未设置,则返回,如果指定,则返回空。如果有效,则返回变量。您可以添加更多选项,如下面的代码所示:falsefalse

<?php
function isset_globals($method, $name, $option = "") {
    if (isset($method[$name])) {    // Check if such a variable
        if ($option === "empty" && empty($method[$name])) { return false; } // Check if empty 
        if ($option === "stringLength" && strlen($method[$name])) { return strlen($method[$name]); }    // Check length of string -- used when checking length of textareas
        return ($method[$name]);
    } else { return false; }
}

if (!isset_globals("$_post", "input_name", "empty")) {
    echo "invalid";
} else {
    /* You are safe to access the variable without worrying about errors! */
    echo "you uploaded: " . $_POST["input_name"];
}
?>
4赞 Alexandre Thebaldi 1/5/2018 #11

欢迎使用 null 合并运算符 (PHP >= 7.0.1):

$field = $_GET['field'] ?? null;

PHP 说道:

空合并运算符 (??) 已被添加为语法糖,用于需要将三元与 isset() 结合使用的常见情况。如果它存在且不为 NULL,则返回其第一个操作数;否则,它将返回其第二个操作数。