【问题标题】:How to avoid 'undefined index' errors? [duplicate]如何避免“未定义索引”错误? [复制]
【发布时间】:2010-11-14 19:08:28
【问题描述】:

我正在处理一些由以前的开发人员完成的代码。我对 PHP 很陌生,所以我想知道这个问题是否有任何众所周知的模式或解决方案。

基本上,原作者在尝试使用它们之前不会检查任何数组索引。我知道我可以使用isset() 在使用之前检查每一个,但现在有数百行出现这些错误。在我放一些音乐并开始用头撞键盘之前,我想确保没有一些很好的快捷方式来处理这个问题。这是我正在查看的典型代码部分:

    /* snip */
"text" => $link . $top_pick_marker . $output['author'] . " " .  " " . 
                              $output['new_icon'] . $output['rec_labels'] . "   " 
                    . $output['admin_link']
                    . $output['alternate_title'] 
                    . $output['access_info'] 
                    . $output['description'] 
                    . $output['url']
                    . $output['subject_terms'] 
                    . $output['form_subdivisions'] 
                    . $output['dates_of_coverage']  
                    . $output['update_frequency']  
                    . $output['place_terms'],
    /* snip */

我知道我可以在这里为每个项目使用isset()。我将不得不重新安排一些事情并像现在一样删除所有连接。有没有其他简单的方法可以做到这一点,还是我只是坚持下去?

【问题讨论】:

  • +1 - 这实际上是一个很好的问题。在 PHP 的“旧时代”,不会抛出这些 E_NOTICE 错误,并且引用未初始化的数组索引非常普遍。显然这是一个坏习惯,但使用 PHP 的松散类型很容易做到这一点。 E_NOTICES 现在是帮助收紧代码的好工具。但是,我发现在涉及数组的所有检查中必须调用 isset()empty() 非常单调乏味。
  • @$output['author'] 绕过检查并使用数据,如果未设置,则使用 null。

标签: php arrays


【解决方案1】:

找出 $output 数组中有哪些键,并用空字符串填充缺失的键。

$keys = array_keys($output);
$desired_keys = array('author', 'new_icon', 'admin_link', 'etc.');

foreach($desired_keys as $desired_key){
   if(in_array($desired_key, $keys)) continue;  // already set
   $output[$desired_key] = '';
}

【讨论】:

  • 对于大量索引键来说,这是一个很好的解决方案。我会用这样的东西来解决海报的问题。
  • 我更喜欢将默认值设置为 null
  • 这很好,除非你的 $output 数组是多维的。
  • @cdmo 在这种情况下,您可能会受益于 Laravel 的 array_set($array, 'foo.bar.baz', $value) 助手之类的东西。 laravel.com/docs/4.2/helpers
  • 使用 array_key_exists 代替 in_array 会更合适见php.net/manual/en/function.array-key-exists.php
【解决方案2】:

您可以使用 isset() 而不会丢失连接:

//snip
$str = 'something'
 . ( isset($output['alternate_title']) ? $output['alternate_title'] : '' )
 . ( isset($output['access_info']) ? $output['access_info'] : '' )
 . //etc.

您也可以编写一个函数来返回已设置的字符串 - 这可能不是很有效:

function getIfSet(& $var) {
    if (isset($var)) {
        return $var;
    }
    return null;
}

$str = getIfSet($output['alternate_title']) . getIfSet($output['access_info']) //etc

您不会收到通知,因为该变量是通过引用传递的。

【讨论】:

  • 他确实说过他不想使用isset,但这仍然是使用isset 的“最简洁”方式。
  • 这种方式也不难实现,因为您可能会使用正则表达式搜索和替换来自动完成。
  • @MitMaro - 同意。我发现三元运算符对于处理字符串输出或连接的这个问题非常方便。对于少量的数组键,这种方法效果很好。
【解决方案3】:

SquareRootOf2 答案的变体,但这应该放在第一次使用 $output 变量之前:

$keys = array('key1', 'key2', 'etc');
$output = array_fill_keys($keys, '');

【讨论】:

  • 我喜欢“不使用循环”的想法,更喜欢使用 PHP 函数。我的建议是像 $output = array_merge($incomplete_output, $output) 那样使用 array_merge() 吗?来自我的 +1
【解决方案4】:

如果您要维护旧代码,您可能无法以“有史以来最好的代码”为目标……在我看来,这是一种可以降低error_reporting 级别的情况。

这些“未定义索引”应该只是通知;因此,您可以设置error_reporting 级别以排除通知。

一种解决方案是使用error_reporting 函数,如下所示:

// Report all errors except E_NOTICE
error_reporting(E_ALL ^ E_NOTICE);

此解决方案的好处是您可以将其设置为仅在必要时排除通知(例如,如果只有一两个文件包含这种代码)

另一种解决方案是在 php.ini 中设置它(但是,如果您正在处理多个应用程序,这可能不是一个好主意,因为它可能会掩盖有用的通知);请参阅 php.ini 中的 error_reporting

但我坚持:这是可以接受的,因为您维护的是旧应用程序——在开发新代码时不应该这样做!

【讨论】:

  • 我个人认为即使在旧代码中也不应该隐藏任何“错误”。错误很可能会隐藏在旧代码中,并且没有动力去修复它们。
  • 我有点同意,但我也不喜欢那样做;但是,有时,如果你想能够工作(至少如果你没有足够的时间来纠正这些),真的没有其他办法;尤其是当应用程序是由不知道通知是什么的人开发的,并且要启用和避免它们......
【解决方案5】:

在数组的开头(或在使用 $output 数组之前)设置数组中的每个索引可能是您的情况最简单的解决方案。

例子

$output['admin_link'] = ""
$output['alternate_title'] = ""
$output['access_info'] = ""
$output['description'] = ""
$output['url'] = ""

也与您的情况无关,但您说您是 PHP 新手,这并不是很明显isset() 可以接受多个参数。所以代替这个:

if(isset($var1) && isset($var2) && isset($var3) ...){
    // all are set
}

你可以这样做:

if(isset($var1, $var2, $var3)){
   // all are set 
}

【讨论】:

    【解决方案6】:

    一个简短的解决方案是这样的(PHP 5.3+):

    $output['alternate_title'] = $output['alternate_title'] ?:'';
    

    如果变量的计算结果不为 false,您将获得变量的值,或者获得 false 表达式。 (':'后面的那个)

    使用三元运算符,不带“if true”参数,将返回测试表达式的结果(第一个)由于 undefined 计算结果为 false,因此将返回 false 表达式。

    在 PHP 7 中有更优雅的 Null 合并运算符:

    $output['alternate_title'] = $output['alternate_title'] ?? '';
    

    (如果有一个默认的赋值运算符比如'?='就好了)

    【讨论】:

    • ?: 绝对不是接收未声明变量通知的解决方案。是的,现代 PHP 有一个空值合并赋值运算符 -- ??=
    【解决方案7】:

    与迈克尔瀑布相同的想法

    来自CodeIgniter

    // Lets you determine whether an array index is set and whether it has a value.
    // If the element is empty it returns FALSE (or whatever you specify as the default value.)
    function element($item, $array, $default = FALSE)
    {
        if ( ! isset($array[$item]) OR $array[$item] == "")
        {
            return $default;
        }
    
        return $array[$item];
    }
    

    【讨论】:

    【解决方案8】:

    在我的例子中,如果提交的数据为空,我通过定义 默认值 来实现它。以下是我最终所做的(使用 PHP7.3.5)

    if(empty($_POST['auto'])){ $_POST['auto'] = ""; }

    【讨论】:

    • empty() 可能会损坏合法数据,因为它会消耗所有虚假值。这不是适用于所有情况的技术。
    【解决方案9】:

    您可以尝试使用一个不错的小函数,如果存在则返回该值,如果不存在则返回空字符串。这是我使用的:

    function arrayValueForKey($arrayName, $key) {
       if (isset($GLOBALS[$arrayName]) && isset($GLOBALS[$arrayName][$key])) {
          return $GLOBALS[$variable][$key];
       } else {
          return '';
       }
    }
    

    那么你可以这样使用它:

    echo ' Values: ' . arrayValueForKey('output', 'admin_link') 
                     . arrayValueForKey('output', 'update_frequency');
    

    而且它不会抛出任何错误!

    希望这会有所帮助!

    【讨论】:

    • 为什么使用GLOBALS 而不仅仅是使用参数?
    • 使用全局变量将是一个很好的方法,因为它避免了函数中的第三个参数,保持干净和简单易用。诚然,它在使用 OO PHP 时不起作用,但对于简单的事情,它会很好地工作,只需要与直接访问数组相比通常需要的代码不多。
    • 没有必要单独调用isset()isset()不仅可以接收多个参数,而且不需要在子元素之前检查父元素。
    【解决方案10】:
    foreach($i=0; $i<10; $i++){
        $v = @(array)$v;   
        // this could help defining $v as an array. 
        //@ is to supress undefined variable $v
    
        array_push($v, $i);
    }
    

    【讨论】:

    • 每当我看到使用 STFU 运算符的脚本时,我立即知道我正在查看非专业、维护不善的代码。
    猜你喜欢
    • 2015-05-23
    • 2016-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 1970-01-01
    • 2021-05-12
    • 2018-06-18
    相关资源
    最近更新 更多