E_NOTICE ?== E_DEBUG,避免 isset() 和 @ 使用更复杂的 error_handler

发布于 2024-10-02 17:10:49 字数 1616 浏览 7 评论 0原文

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

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

然而,并非所有调试通知都是有用的,这就是为什么它是 的常见(不幸的)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= 参数的情况下运行,则会出现预期的通知。这是预料之中的,我不需要多次告知。
  • 然而,在完全调试模式下,我确实希望通过所述调试通知的存在(或更有趣的是不存在)来查看未定义变量的存在。 ->我认为这就是他们的目的。避免 isset 会带来语言隐式打印语句。
  • 还要认识到这是关于适合普通 PHP 表单处理语义的用例,而不是必须严格的应用程序领域。

天哪,请有人帮忙重写这个。冗长的解释失败。

Which better ways exist to avoid an abundance of isset() in the application logic, and retain the ability to see debug messages (E_NOTICE) when required?

Presumption first: E_NOTICE is not an error, it's a misnomer and should actually be E_DEBUG. However while this is true for unset variables (PHP is still a scripting language), some file system functions etc. throw them too. Hence it's desirable to develop with E_NOTICEs on.

Yet not all debug notices are useful, which is why it's a common (unfortunate) PHP idiom to introduce isset() and @ throughout the application logic. There are certainly many valid use cases for isset/empty, yet overall it seems syntactic salt and can actually obstruct debugging.

That's why I currently use an error_reporting bookmarklet and a dumb on/off switch:

// 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 */}

However that still leaves me with the problem of having too many notices to search through once enabled. As workaround I could utilize the @ error suppression operator. Unlike isset() it does not completely kill debugging options, because a custom error handler could still receive suppressed E_NOTICEs. So it might help to separate expected debug notices from potential issues.

Yet that's likewise unsatisfactory. Hence the question. Does anyone use or know of a more sophisticated PHP error handler. I'm imagining something that:

  • outputs unfiltered errors/warnings/notices (with CSS absolute positioning?)
  • and AJAX-whatnot to allow client-side inspection and suppression
  • but also saves away a filtering list of expected and "approved" notices or warnings.

Surely some framework must already have a user error handler like that.

  • Basically I'm interested in warning / notice management.
  • Full E_NOTICE supression is really not desired.
  • E_NOTICES are wanted. Just less of them. Per default highlight the ones I might care about, not the expected.
  • If I run without ?order= parameter, an expected NOTICE occours. Which due to be expected I do not need to informed about more than once.
  • However when in full debugging mode, I do wish to see the presence of undefined variables through the presence (or more interestingly absence) of said debug notices. -> That's what I think they are for. Avoiding isset brings language-implicit print statements.
  • Also realize this is about use cases where ordinary PHP form handling semantics are suitable, not application areas where strictness is a must.

Oh my, someone please help rewrite this. Lengthy explanation fail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

天涯离梦残月幽梦 2024-10-09 17:10:49

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

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

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

It is possible to develop a large PHP application that never emits any E_NOTICEs. All you have to do is avoid all situations where a Notice can be emitted, the vast majority of which are un-initialized variables and non-existist array keys. Unfortunately, this clashes with your wish to avoid isset() - and by extension array_key_exists() - because they are designed for handling that exact problem.

At best, you can minimize their use by careful framework building. This generally means (for example) an input layer which is told what GET variables to expect and what to default missing ones to. That way the page-specific code will always have values to look at. This is, in general, a worthwhile technique that can be applied to a variety of APIs. But I question whether this should be a high-priority design goal.

Unlike some other languages, PHP distinguishes between a variable not existing and containing a generally "empty" value (usually null). It is probably a design artifact from an earlier version, but it nonetheless is still present, so you cannot really avoid it.

舂唻埖巳落 2024-10-09 17:10:49

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

我的建议是:

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

我正在使用 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 文件末尾的结束 ?> 标记,因为它可能会在禁用输出缓冲的配置上导致 headers already sent 错误默认情况下。

I am using isset() only for $_GET and $_SERVER variables, where the data comes from outside the control of my application. And I am using it in some other situation when I don't have time to write a proper OOP solution to avoid it, but I'm sure that it can be avoided in most if not all places. For example it's better to use classes instead of associative arrays, this way you don't need to check the existence of an array key.

My advices are:

  • Avoid using the @ operator.
  • Use Xdebug. First, it prints easily readable and easily noticeable messages about every notice/warnig, and it prints a very useful stack trace on exceptions (you can configure it to print out every method parameter and every local variable (xdebug.collect_params=4 and xdebug.show_local_vars=on configuration parameters). Second, it can disable the @ operator with xdebug.scream=1 config value. You can use Xdebug for profiling and for code coverage analysis as well. It's a must have on your development machine.
  • For debugging, I am also using FirePHP, because it works with Firebug, and is able to print messages to the Firebug console, so it can be used for AJAX debugging as well.
  • With a custom error handler, you can catch and filter any error and warning, and you can log them into a file or display them with FirePHP, or you can use for example jGrowl or Gritter to nicely display them on the web page.

I am using a modified version of the example in the PHP manual:

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

One more advice is not to use the closing ?> tag at the end of the php-only files, because it can cause headers already sent errors on configurations where the output buffering is disabled by default.

断念 2024-10-09 17:10:49

好吧,如果您等待 PHP 7,您将可以访问 空合并三元 运算符,除了拥有现有的最酷的操作员名称(我将我的下一个孩子命名为“Null Coalesce”)之外,它还可以让你做到这一点:

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

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

$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";

Well, if you wait for PHP 7, you'll have access to the null coalesce ternary operator, which, in addition to having the coolest operator name in existence (I'm naming my next kid "Null Coalesce") will let you do this:

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

Which replaces the ubiquitous (and ugly)

$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";
囍笑 2024-10-09 17:10:49

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

大量的 isset 检查不会伤害你,

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

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

lots of isset checking does not harm u,

in fact, it encourage declare variables before use it

删除会话 2024-10-09 17:10:49

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

I think following the best practice is not waste of time. That's true, a notice is not an error, but with correct variable declaration and validation your code could be more readable and secure.
But it's not so complex to write a user-defined error handler with debug_backtrace sort the E_NOTICE(8) with a regexp.

窝囊感情。 2024-10-09 17:10:49

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

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

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

干杯!

I had a similar desire. So I started using custom error handlers.

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

You can then create your own filters/mechanisms for displaying/logging errors/notices.

Cheers!

初相遇 2024-10-09 17:10:49

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

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

PHP is definitely broken around this making code less readible. "null" means "undefined" - simple enough.

Here is what I do when I run into this problem making code unreadable:

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

Cross posting this answer here. Does this help spam-checker?

人心善变 2024-10-09 17:10:49

我认为避免 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() 的所有内容(它已经是了)。

The best way to avoid isset() in my opinion is to define your variables before you use them. I dislike isset() not so much because it's ugly but because it promotes a bad programming practice.

As for error handling itself, I put all that information out to the server logs. I also use php -l on the command line to syntax check the programs before hand. I make pretty messages for the users by default.

You might look into one of the various frameworks to see if any of them would work for you. Most of them I've looked at have error handling routines to make things easier than what PHP offers out of the box.

EDIT:
@mario - my response to your comment was getting too long :-). I don't advocate defining types or going to some kind of strict format like Java or C. I just advocate declaring the variable in the context that it's used. ( $foo = null; is not the same as leaving the variable blank).

I think this is more of a problem with global variables in a lot of cases, especially the super globals for getting GET and POST data. I really wish that PHP would drop super globals in favor of an class for getting input data. Something like this (super simple but hey you wanted something concrete: :) )

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

Include the class then you can get and set POST data from the param() method. It would also be a nice way to incorporate validation into the input data. And as a bonus, no checking everything for isset() (it already is).

梦回旧景 2024-10-09 17:10:49

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

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

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

It's kind of an outdated answer now, but I originally used a flexible log dispatcher back then, https://github.com/grosser/errorhandler (Not exactly what I was looking for IIRC, but at least a little more sophisticated than alternating between complete and partial supression.)

Anyway, I'm meanwhile using a $_GET->int["input"] wrapper for the most common cases. Which is just a trivial ArrayAccess wrapper, implicitly catches non-existant vars, that allows easier notice recovery. (Just a by-product. Primarily meant for immediate filtering though.)

And for another project I'm even using a preproccessor macro IFSET@($var), to allow enabling/disabling or log-redirection depending on build parameters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文