99

我有几个较旧的应用程序在 E_NOTICE 错误级别上运行时会抛出很多“xyz 未定义”和“未定义偏移”消息,因为变量的存在没有使用isset()和 consorts 显式检查。

我正在考虑通过它们使它们与 E_NOTICE 兼容,因为有关丢失变量或偏移量的通知可能是救命稻草,可能会获得一些小的性能改进,并且总体上是更清洁的方式。

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

如何在没有过多变量检查的情况下构建我的代码,同时又与 E_NOTICE 兼容?

4

11 回答 11

130

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


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

这些警告是为了帮助你,而不是为了惹恼你。如果您收到警告“您正在尝试使用不存在的东西!” ,你的反应应该是“糟糕,我的错,让我尽快解决这个问题。” 您还要如何区分“未定义的工作正常的变量”可能导致严重错误的真正错误的代码?这也是为什么你总是,总是,将错误报告转向 11并不断插入代码直到没有一个的原因NOTICE发出。关闭错误报告仅适用于生产环境,以避免信息泄漏,即使面对错误代码也能提供更好的用户体验。


详细说明:

您将始终需要issetempty在代码中的某个位置,减少它们发生的唯一方法是正确初始化您的变量。根据情况有不同的方法来做到这一点:

函数参数:

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

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

任何地方的常规变量:

$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

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

于 2009-12-25T05:57:10.940 回答
37

只需为此编写一个函数。就像是:

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()

于 2009-12-25T05:30:47.857 回答
13

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

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

之后,您可以自信地以这种方式访问​​数组值:$POST->username并在需要时进行任何验证,而无需使用任何isset()s 或empty()s。如果username对应的全局数组中不存在则null返回,因此不会产生警告或通知。

于 2009-12-25T13:40:57.990 回答
6

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


但是,我确实使用了一个简单的函数,在这方面很方便,以及在处理数组索引的其他一些情况下:

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

整齐吧?;)

于 2009-12-25T07:33:43.547 回答
3

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

于 2009-12-25T06:04:30.643 回答
3

我使用这些功能

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";
}
于 2011-07-17T12:59:20.577 回答
3

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

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

PHP 说:

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

于 2018-01-05T11:57:24.393 回答
1

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

<?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"];
}
?>
于 2014-09-07T19:26:08.113 回答
0

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

如果您的 E_NOTICE 来自 $_GET 或 $_POST,则应检查它们是否与 empty() 以及该数据必须通过的所有其他安全检查一起。

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

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

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

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

于 2009-12-25T06:35:35.650 回答
0

软件并不是靠上帝的恩典神奇地运行的。如果您期望缺少某些东西,则需要正确处理它。

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

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

于 2009-12-25T11:52:00.393 回答
-2

使用@运算符怎么样?

例如:

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

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

if(isset($foo) && $foo) { /* ... */ }
于 2012-08-13T20:13:26.380 回答