提问人:mario 提问时间:11/12/2010 最后编辑:Communitymario 更新时间:4/12/2015 访问量:1482
E_NOTICE ?== E_DEBUG,避免使用更复杂的error_handler isset() 和 @
E_NOTICE ?== E_DEBUG, avoiding isset() and @ with more sophisticated error_handler
问:
有哪些更好的方法可以避免应用程序逻辑中出现过多的问题,并在需要时保留查看调试消息 (E_NOTICE) 的能力?isset()
首先推定:E_NOTICE不是错误,而是用词不当,实际上应该E_DEBUG。然而,虽然这对于未设置的变量是正确的(PHP仍然是一种脚本语言),但一些文件系统函数等也会抛出它们。因此,最好在E_NOTICEs的情况下进行开发。
然而,并非所有的调试通知都是有用的,这就是为什么在整个应用程序逻辑中引入 isset(
) 和 @ 是一个常见(不幸的)PHP 习语。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_NOTICEs。因此,将预期的调试通知与潜在问题分开可能会有所帮助。
然而,这同样不能令人满意。因此问题来了。有没有人使用或知道更复杂的PHP错误处理程序。我正在想象一些事情:
- 输出未经过滤的错误/警告/通知(使用 CSS 绝对定位?
- 和 AJAX-whatnot 允许客户端检查和抑制
- 但还可以保存预期和“批准”通知或警告的过滤列表。
当然,某些框架必须已经有这样的用户错误处理程序。
- 基本上我对警告/通知管理感兴趣。
- 完全E_NOTICE压制是真的不可取的。
- E_NOTICES被通缉。只是少了。默认情况下,突出显示我可能关心的那些,而不是预期的。
- 如果我在没有 ?order= 参数的情况下运行,则会出现预期的 NOTICE。由于意料之中,我不需要多次通知。
- 但是,在完全调试模式下,我确实希望通过存在(或更有趣的是不存在)所述调试通知来查看未定义变量的存在。-> 这就是我认为他们的目的。避免 isset 会带来语言隐式 print 语句。
- 还要注意,这是关于普通PHP表单处理语义的用例,而不是必须严格性的应用领域。
哦,天哪,有人请帮忙重写这个。冗长的解释失败。
答:
在我看来,避免的最好方法是在使用变量之前定义变量。我不喜欢它不是因为它丑陋,而是因为它促进了糟糕的编程实践。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;
}
}
?>
包含类,然后可以从该方法获取和设置 POST 数据。这也是将验证合并到输入数据中的好方法。作为奖励,无需检查所有内容(已经是)。param()
isset()
评论
isset()
不是因为它丑陋,而是因为它促进了糟糕的编程实践吗? ?我很好奇它促进了什么不良做法......
isset()
isset()
isset
尝试 - http://www.xdebug.org/docs/stack_tracexdebug
大量检查不会伤害你,isset
事实上,它鼓励在使用之前声明变量
我认为遵循最佳实践不是浪费时间。没错,通知不是错误,但通过正确的变量声明和验证,您的代码可以更具可读性和安全性。 但是,编写一个用户定义的错误处理程序debug_backtrace用正则表达式对 E_NOTICE(8) 进行排序并不是那么复杂。
可以开发一个从不发出任何E_NOTICEs的大型 PHP 应用程序。您所要做的就是避免所有可以发出通知的情况,其中绝大多数是未初始化的变量和不存在的数组键。不幸的是,这与您避免的愿望相冲突 - 并且扩展 - 因为它们是为处理该确切问题而设计的。isset()
array_key_exists()
充其量,您可以通过仔细构建框架来最大程度地减少它们的使用。这通常意味着(例如)一个输入层,该输入层被告知要期望哪些变量以及默认缺少哪些变量。这样,特定于页面的代码将始终具有要查看的值。一般来说,这是一种有价值的技术,可以应用于各种 API。但我质疑这是否应该是一个高优先级的设计目标。GET
与其他一些语言不同,PHP 区分不存在的变量和包含通常“空”值(通常)的变量。它可能是早期版本的设计工件,但它仍然存在,所以你无法真正避免它。null
评论
E_NOTICE
isset()
isset
isset()
我也有类似的愿望。因此,我开始使用自定义错误处理程序。
http://php.net/manual/en/function.set-error-handler.php
然后,您可以创建自己的过滤器/机制来显示/记录错误/通知。
干杯!
我只使用 for 和变量,其中数据来自我的应用程序控制之外。当我没有时间编写适当的 OOP 解决方案来避免它时,我正在其他一些情况下使用它,但我相信它可以在大多数地方(如果不是所有地方)避免。例如,最好使用类而不是关联数组,这样你就不需要检查数组键的存在。isset()
$_GET
$_SERVER
我的建议是:
- 避免使用
@
运算符。 - 使用 Xdebug。首先,它打印有关每个通知/警告的易于阅读和易于注意的消息,并在异常时打印非常有用的堆栈跟踪(您可以将其配置为打印出每个方法参数和每个局部变量(和配置参数)。其次,它可以禁用带有配置值的运算符。还可以使用 Xdebug 进行分析和代码覆盖率分析。这是开发机器上的必备工具。
xdebug.collect_params=4
xdebug.show_local_vars=on
@
xdebug.scream=1
- 对于调试,我也在使用 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
评论
?>
@
fopen()
现在这个答案有点过时了,但我当时最初使用的是灵活的日志调度器,https://github.com/grosser/errorhandler(不完全是我想要的 IIRC,但至少比在完全和部分抑制之间交替更复杂一些。
无论如何,我同时在最常见的情况下使用 $_GET->int[“input”]
包装器。这只是一个微不足道的 ArrayAccess 包装器,隐式捕获不存在的变量,从而可以更轻松地恢复通知。(只是一个副产品。不过,主要是为了立即过滤。
对于另一个项目,我什至使用 preproccessor 宏 ,以允许根据构建参数启用/禁用或日志重定向。IFSET@($var)
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 7,你将可以访问 null coalesce 三元运算符,除了拥有现存最酷的运算符名称(我将我的下一个孩子命名为“Null Coalesce”)之外,它还允许您这样做:
$var = $some_array[$some_value] ?? "default value";
它取代了无处不在(和丑陋的)
$var = isset( $some_array[$some_value] ) ? $some_array[$some_value] : "default_value";
评论
isset
isset
$foo = $_GET['bar']
$foo = $request->get('bar');
isset()
$array = $array + array('optional1' => '', 'optional2' => '');