11

有哪些更好的方法可以避免应用程序逻辑过多,并在需要isset()保留查看调试消息 (E_NOTICE) 的能力?

首先假设:E_NOTICE 不是错误,它是用词不当,实际上应该是 E_DEBUG。然而,虽然这对于未设置的变量是正确的(PHP 仍然是一种脚本语言),但一些文件系统函数等也会抛出它们。因此,最好使用 E_NOTICEs进行开发。

然而,并非所有调试通知都是有用的,这就是为什么在整个应用程序逻辑中引入和 @ 是一个常见的(不幸的)PHP 习惯用法。isset()isset/empty 肯定有许多有效的用例,但总体而言,它似乎是语法盐,实际上会阻碍调试。

这就是我目前使用 error_reporting 小书签和哑开/关开关的原因:

// javascript:(function(){document.cookie=(document.cookie.match(/error_reporting=1/)?'error_reporting=0':'error_reporting=1')})()

if (($_SERVER["REMOTE_ADDR"] == "127.0.0.1")
    and $_COOKIE["error_reporting"])
{
    error_reporting(E_ALL|E_STRICT);
}
else {/* less */}

但是,这仍然给我留下了启用后要搜索的通知太多的问题。作为解决方法,我可以使用@错误抑制运算符。与 isset() 不同,它不会完全终止调试选项,因为自定义错误处理程序仍然可以接收抑制的 E_NOTICE。因此,将预期的调试通知与潜在问题区分开来可能会有所帮助。

然而,这同样不能令人满意。因此问题。有没有人使用或知道更复杂的 PHP 错误处理程序。我在想象这样的事情:

  • 输出未经过滤的错误/警告/通知(使用 CSS 绝对定位?)
  • 和 AJAX-whatnot 允许客户端检查和抑制
  • 而且还保存了预期和“批准通知或警告的过滤列表。

当然,某些框架必须已经有这样的用户错误处理程序。

  • 基本上我对警告/通知管理感兴趣。
  • 完全不需要 E_NOTICE 抑制。
  • 需要E_NOTICES 。只是少了些。默认情况下,突出显示我可能关心的那些,而不是预期的。
  • 如果我在没有 ?order= 参数的情况下运行,则会出现预期的 NOTICE。这是意料之中的,我不需要多次通知。
  • 但是,在完全调试模式下,我确实希望通过所述调试通知的存在(或更有趣的是不存在)来查看未定义变量的存在。-> 这就是我认为他们的目的。避免 isset 会带来语言隐含的打印语句。
  • 还要意识到这是关于普通 PHP 表单处理语义适用的用例,而不是必须严格要求的应用程序领域。

哦,我的,有人请帮助重写这个。冗长的解释失败。

4

9 回答 9

9

可以开发一个从不发出任何 E_NOTICE 的大型 PHP 应用程序。您所要做的就是避免所有可以发出通知的情况,其中绝大多数是未初始化的变量和不存在的数组键。不幸的是,这与您避免的愿望相冲突isset()- 并且通过扩展array_key_exists()- 因为它们是为处理该确切问题而设计的。

充其量,您可以通过仔细构建框架来尽量减少它们的使用。这通常意味着(例如)一个输入层,它被告知GET期望什么变量以及默认缺少的变量是什么。这样,特定于页面的代码将始终具有要查看的值。一般来说,这是一种有价值的技术,可以应用于各种 API。但我质疑这是否应该是一个高优先级的设计目标。

与其他一些语言不同,PHP 区分不存在的变量和包含通常“空”值(通常为null)的变量。它可能是早期版本的设计工件,但它仍然存在,因此您无法真正避免它。

于 2010-11-15T00:11:48.987 回答
6

isset()只使用 for$_GET$_SERVER变量,其中数据来自我的应用程序控制之外。当我没有时间编写适当的 OOP 解决方案来避免它时,我会在其他情况下使用它,但我确信在大多数地方(如果不是所有地方)都可以避免它。例如,最好使用类而不是关联数组,这样您就不需要检查数组键的存在。

我的建议是:

  • 避免使用@运算符
  • 使用Xdebug。首先,它打印关于每个通知/警告的易于阅读且易于注意到的消息,并且它打印一个非常有用的异常堆栈跟踪(您可以将其配置为打印出每个方法参数和每个局部变量(xdebug.collect_params=4xdebug.show_local_vars=on配置参数)。第二,它可以通过配置值禁用@操作符xdebug.scream=1。您可以使用 Xdebug 进行分析和代码覆盖率分析。它是您开发机器上的必备工具。
  • 对于调试,我也在使用FirePHP,因为它可以与Firebug一起使用,并且能够将消息打印到Firebug 控制台,因此它也可以用于AJAX 调试
  • 使用自定义错误处理程序,您可以捕获和过滤任何错误和警告,您可以将它们记录到文件中或使用 FirePHP 显示它们,或者您可以使用例如jGrowlGritter在网页上很好地显示它们。

我正在使用PHP 手册中示例的修改版本:

<?php
//error_reporting(0);
set_error_handler("errorHandler");

function errorHandler($errno, $errstr, $errfile, $errline)
{
    echo "errorHandler()<br />\n";

    // filter out getImageSize() function with non existent files (because I'am avoiding using file_exists(), which is a costly operation)
    if ( mb_stripos($errstr, 'getimagesize') !== false )
        return true;

    // filter out filesize() function with non existent files
    if ( mb_stripos($errstr, 'filesize') !== false )
        return true;

    // consoleWriter is my class which sends the messages with FirePHP
    if (class_exists('consoleWriter'))
        consoleWriter::debug(array('errno'=>$errno, 'errstr'=>$errstr, 'errfile'=>$errfile, 'errline'=>$errline, 'trace'=>debug_backtrace()), "errorHandler");

    switch ($errno) {
    case E_USER_ERROR:
        $out .= "<b>FATAL_ERROR</b> <i>$errno</i> $errstr<br />\n";
        $out .= "Fatal error on line $errline in file $errfile";
        echo "</script>$out";   // if we were in a script tag, then the print is not visible without this
        //writeErrorLog($out);

        echo "<pre>";
        var_export(debug_backtrace());
        echo "</pre>";

        exit(1);
        break;

    case E_USER_WARNING:
        $out .= "<b>WARNING</b> <i>$errno</i> $errstr<br />\n";
        $out .= "On line $errline in file $errfile<br />\n";
        break;

    case E_USER_NOTICE:
        $out .= "<b>NOTICE</b> <i>$errno</i> $errstr<br />\n";
        $out .= "On line $errline in file $errfile<br />\n";
        break;

    default:
        $out .= "<b>Unknown</b> <i>$errno</i> $errstr<br />\n";
        $out .= "On line $errline in file $errfile<br />\n";
        break;
    }

    if (!class_exists('consoleWriter'))
        echo $out;

    //writeErrorLog($out);
    //addJGrowlMessage($out);

    // Don't execute PHP internal error handler
    return true;
}

function testNotice($a)
{
    echo $a;
}
testNotice();

另一个建议是不要?>在 php-only 文件的末尾使用结束标记headers already sent,因为它可能会导致默认情况下禁用输出缓冲的配置出错。

于 2010-11-18T19:23:44.540 回答
3

好吧,如果您等待 PHP 7,您将可以访问null coalesce 三元运算符,除了拥有现有最酷的运算符名称(我将我的下一个孩子命名为“Null Coalesce”)之外,您还可以这样做这:

$var = $some_array[$some_value] ?? "default value";

它取代了无处不在的(和丑陋的)

$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";
于 2014-12-18T18:53:08.383 回答
2

试试xdebug- http://www.xdebug.org/docs/stack_trace

大量isset检查不会伤害你,

事实上,它鼓励在使用之前声明变量

于 2010-11-11T19:43:18.873 回答
2

我认为遵循最佳实践并不是浪费时间。没错,通知不是错误,但是通过正确的变量声明和验证,您的代码可能更具可读性和安全性。但是使用 debug_backtrace编写用户定义的错误处理程序并使用正则表达式对 E_NOTICE(8) 进行排序并不是那么复杂。

于 2010-11-14T23:58:48.413 回答
2

我也有类似的愿望。所以我开始使用自定义错误处理程序。

http://php.net/manual/en/function.set-error-handler.php

然后,您可以创建自己的过滤器/机制来显示/记录错误/通知。

干杯!

于 2010-11-15T22:15:01.713 回答
2

PHP 肯定会在这个问题上被打破,这会降低代码的可读性。“null”表示“未定义”——很简单。

当我遇到这个使代码不可读的问题时,我会这样做:

/**
 * Safely index a possibly incomplete array without a php "undefined index" warning.
 * @param <type> $array
 * @param <type> $index
 * @return <type> null, or the value in the index (possibly null)
 */
function safeindex($array, $index) {
  if (!is_array($array)) return null;
  return (isset($array[$index])) ? $array[$index] : null;
}

// this might generate a warning
$configMenus = $config['menus'];  

// WTF are you talking about!!  16 punctuation marks!!!
$configMenus = (isset($config['menus']) ? $config['menus'] : null;

// First-time awkward, but readible
$configMenus = safeindex($config, 'menus');

在此处交叉发布此答案。这对垃圾邮件检查器有帮助吗?

于 2011-04-06T17:30:18.830 回答
1

在我看来,最好的避免方法isset()是在使用变量之前定义它们。我不喜欢isset()它不是因为它很丑,而是因为它促进了一种糟糕的编程习惯。

至于错误处理本身,我将所有这些信息都放到了服务器日志中。我还使用php -l命令行来事先对程序进行语法检查。我默认为用户制作漂亮的消息。

您可能会查看各种框架之一,看看它们中的任何一个是否适合您。我看过的大多数都有错误处理例程,使事情比 PHP 提供的开箱即用更容易。

编辑: @mario - 我对你的评论的回复太长了:-)。我不主张定义类型或采用某种严格的格式,如 Java 或 C。我只是主张在使用它的上下文中声明变量。($foo = null;与将变量留空不同)。

我认为在很多情况下,这更多是全局变量的问题,尤其是用于获取 GET 和 POST 数据的超级全局变量。我真的希望 PHP 会放弃超级全局变量,转而使用用于获取输入数据的类。像这样的东西(超级简单,但是你想要一些具体的东西::))

<?php
class PostData {
     private $data;

     public function __construct() {
          $this->data = $_POST;
          unset($_POST);
     }

     public function param($name, $value = null) {
          if( $value !== null ) {
               $this->data[$name] = $value;
          }

          if( isset( $this->data[$name] ) ) {
               return $this->data[$name];
          }
          return null;
      }
}
?>  

包含该类,然后您可以从该param()方法获取和设置 POST 数据。这也是将验证合并到输入数据中的好方法。作为奖励,没有检查所有内容isset()(它已经是)。

于 2010-11-11T19:42:46.250 回答
1

现在这是一个过时的答案,但我最初使用了一个灵活的日志调度程序,https://github.com/grosser/errorhandler(不完全是我正在寻找的 IIRC,但至少比交替更复杂一点完全和部分抑制。)

无论如何,我同时在最常见的情况下使用$_GET->int["input"]包装器。这只是一个微不足道的 ArrayAccess 包装器,隐式捕获不存在的变量,从而可以更轻松地恢复通知。(只是一个副产品。不过主要是为了立即过滤。)

对于另一个项目,我什至使用了预处理宏IFSET@($var),以允许根据构建参数启用/禁用或日志重定向。

于 2010-11-22T04:52:30.803 回答