PHP 和未定义变量策略

Posted

技术标签:

【中文标题】PHP 和未定义变量策略【英文标题】:PHP and undefined variables strategy 【发布时间】:2011-04-03 15:30:02 【问题描述】:

我是一名从 php 开始的 C++ 程序员。我发现由于未定义的变量,我失去了大部分调试时间(以及我的自尊!)。据我所知,处理它们的唯一方法是在执行时观察输出。

是否有其他策略可以更早地注意到这些错误(例如使用 C++,一次编译就可以为您提供所需的所有线索)?

【问题讨论】:

这并不是您问题的直接答案,但是未定义变量遇到这么多麻烦意味着您正在生成意大利面条代码。使用更有条理的 OO 代码,更容易查看变量的范围和生命周期。 我理解您的担忧,但是,在我看来,您来自 C++,您将负责在使用前声明变量。 【参考方案1】:

这是对 PHP 的常见抱怨。以下是一些想法:

    使用code analysis tool。 NetBeans 等许多 IDE 也会有所帮助。

    只需运行代码即可。PHP 不像 C++ 那样具有昂贵的编译步骤。

    使用单元测试。常见的副作用包括:更好的代码。

    设置error_reporting(-1),或ini file中的等效项。

    获取xdebug。这不是预防性的,但堆栈跟踪有助于消除错误。

    isset()=== null(身份运算符)和保护子句是你的朋友。

松散和动态类型是该语言的一个特点。仅仅因为 PHP isn't strict about typing 并不意味着你不能。如果它确实让您感到困扰并且您可以选择,那么您可以尝试使用 Python — 它对输入的要求更严格。

【讨论】:

+1,对于error_reporting(-1);,不知道那个(减号)!【参考方案2】:

将您的 E_NOTICE 消息记录到文本文件中。然后,您可以使用自动化脚本处理日志,以指示引发这些问题的文件和行。

【讨论】:

这并不能真正解决 IMO 的问题。我们需要的是一个不同的工作程序,以防止这些通知出现在首位。 这是肯定的。浏览/解析日志只是“观察输出”的“改进”版本,以捕获来自遗留代码或以某种方式滑入新代码的错误。 任何网站都不应该抛出错误,永远不要 - 至少将用户重定向到静态错误页面,想象一下如果你在 youtube 上看到这个标题 "PHP Notice: X:/server/server_mounts/primary/public/data/index.php The variable $youtube_global_credit_card_details was not found" - 只是一个可怕的事情,特别是对于广大市民。专业精神始终是您应该采取的方法。 :) 同意这一点。幸运的是,您可以记录错误而不向用户显示它们。【参考方案3】:

没有。在 PHP 中,只有当您尝试访问某个变量时,您才能知道该变量不存在。

考虑:

if ($data = file('my_file.txt')) 
    if (count($data) >= 0)
        $line = reset($data);

var_dump($line);

你必须重构你的代码,让所有的代码路径都指向定义的变量,例如:

$line = "default value";
if ($data = file('my_file.txt')) 
    if (count($data) >= 0)
        $line = reset($data);

var_dump($line);

如果没有任何有意义的默认值,这仍然比isset 更好,因为如果您在最后的if 中的变量名中有拼写错误,您会收到警告:

$line = null;
if ($data = file('my_file.txt')) 
    if (count($data) >= 0)
        $line = reset($data);

if ($line !== null)  /* ... */ 

当然,您可以使用isset1 在给定点检查变量是否存在。但是,如果您的代码依赖于此,那么它的结构可能很差。我的观点是,与例如相反。在 C/Java 中,您无法在编译时确定对变量的访问是否有效。由于 PHP 中不存在块作用域,情况变得更糟。

1 严格来说,isset 不会告诉你变量是否被设置,它会告诉你它是否被设置并且不为空。否则,您将需要get_defined_vars

【讨论】:

那有什么好处呢?他将可见的运行时抱怨替换为对具有垃圾值的变量的访问,现在可能会给出一个无声的错误而不是诊断出的错误。 (0 是垃圾[就像初始化常量的任何其他选择一样],如果它不是变量在代码中那个点应该具有的值)。他需要的是静态分析来确定变量在哪里被使用但没有被初始化。可惜没有更多的 PHP 分析工具。 @Pekka 我称之为访问。我将进行编辑以使其更清晰。 @Ira 这只是导致始终定义变量的代码路径示例。我同意你的看法。 +1:来自 C++ 背景,这应该已经是习惯了。这将防止大量的错误。至于0是垃圾值,根本不是,因为你知道如果它是0,它还没有定义。我个人在使用它们之前声明了所有变量。这是一个好习惯,很少会导致错误(只要你不做任何愚蠢的事情,比如声明一个 int 然后再重新分配一个对象)...... @Ira 没有人说“嘿,它发出了通知——让我们将 var 初始化为垃圾以将其关闭”。我的意思是你应该重组你的流程,以便始终定义变量。偶尔,这意味着您必须将其初始化为一个无效的特殊标志值。在这种情况下,您可以争辩说它与使用isset 相同,但事实并非如此。如果你做isset($variableWithTypo),你会有一个bug,如果你做if ($variableWithTypo === null)(其中null是特殊的“标志值”),你也会有一个bug,但至少你会得到一个通知。 【参考方案4】:

据我所知,处理它们的唯一方法是在执行时观察输出。

并非如此:为防止弹出这些通知,您只需确保在第一次访问变量之前对其进行初始化。我们(遗憾的是 IMO)在 PHP 中没有变量声明,但在代码块的开头初始化它们也是一样的:

$my_var = value;

使用 phpDocumentor 语法,您还可以将它们声明为某种类型,至少以许多 IDE 能够进行代码查找的方式:

/** @desc optional description of what the variable does
    @var int */
$my_var = 0;

此外,您可以(有时需要)在尝试访问变量之前使用 isset() / empty() / array_key_exists() 条件。

I agree this sucks big time 有时,但这是必要的。在完成的生产代码中不应该有任何通知——即使关闭显示它们也会消耗性能,而且它们对于找出使用变量时可能出现的错别字非常有用。 (但你已经知道了。)

【讨论】:

【参考方案5】:

注意不要在第一次使用时进行需要变量值的操作,例如连接运算符.=

如果您是 C++ 程序员,您必须习惯于声明所有变量。如果你想使用它们,可以在 PHP 中通过将变量归零或创建空数组来做类似的事情。

注意用户输入,确保您已注册globals off 并通过isset() 检查来自$_GET 和$_POST 的输入。

您还可以尝试根据结构代码编写类,并在具有正确隐私政策的类声明的开头创建每个变量。

您还可以将应用程序逻辑与视图分离,通过准备所有必须首先输出的变量,当它显示时,您将知道您准备了哪些变量。

【讨论】:

【参考方案6】:

在开发阶段使用

error_reporting(E_ALL);

这将显示导致的每个错误、所有 NOTICE 错误等。

请留意您的error_log。这会显示错误。

使用错误报告系统,例如:

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

class ErrorReporter

    public function catch($errno, $errstr, $errfile, $errline)
    
        if($errno == E_USER_NOTICE && !defined('DEBUG'))
        
            // Catch all output buffer and clear states, redirect or include error page.
        
    


set_error_handler(array(new ErrorReporter,'catch'));

其他一些技巧是始终使用isset 来表示您可能/可能没有设置的变量,因为假设有if 语句。

始终使用if(isset($_POST['key'])),或者最好只使用if(!empty($_POST['key'])),因为这会检查键是否存在以及值是否为空。

确保您也了解比较运算符。像 C# 这样的语言使用 == 来检查布尔状态,而在 PHP 中检查数据类型你必须使用 === 并使用 == 来检查值状态,并使用单个 = 来分配值!

【讨论】:

【参考方案7】:

除非我遗漏了什么,否则为什么没有人建议您正确构建您的页面?我从来没有真正遇到过未定义变量错误的持续问题。

关于构建页面的想法

在顶部定义所有变量,必要时分配默认值,然后从那里使用这些变量。这就是我编写网页的方式,而且我从未遇到未定义变量的问题。

不要养成只在需要时才定义变量的习惯。这会快速创建意大利面条式代码,并且可能非常难以管理。

没有人喜欢意大利面条代码

如果您向我们展示您的一些代码,我们或许可以就如何更好地构建代码来解决此类错误提供建议。来自 C 背景的您可能会感到困惑;流程可能与网页不同。

【讨论】:

在哪里定义变量并不重要——代码后面的一个简单的错字会创建一个具有潜在意外值的新变量。解决这个问题的唯一方法是 (a) 运行每个代码路径,然后 (b) 观察日志文件。尽管可行,但 OP 正在寻找更好的方法。 C 和其他语言(甚至像 Python 和 javascript 这样的动态语言)具有编译器和 linter 之类的工具,可以检测未定义的变量,并且可以节省大量时间。最后,并非所有 PHP 开发人员都在编写“页面”或 Web 前端。【参考方案8】:

好的做法是在使用前定义所有变量,即设置一个默认值:

$变量 = 默认值;

这将解决大多数问题。如前所述,使用Xdebug 或编辑器中的内置调试工具,如NetBeans。

【讨论】:

【参考方案9】:

如果要隐藏未定义变量的错误,请使用@。示例:@$var

【讨论】:

【参考方案10】:

我相信可用于 PHP 的各种 Code Coverage 工具会突出这一点。

【讨论】:

【参考方案11】:

就个人而言,我尝试设置变量,即使它是空字符串、数组、布尔值等。然后我在使用它们之前使用诸如isset() 之类的函数。例如:

$page_found = false;

if ($page_found==false) 
    // Do page not found stuff here


if (isset($_POST['field'])) 
    $value = $_POST['field'];
    $sql = "UPDATE table SET field = '$value'";

等等。在一些聪明人说之前:我知道那个查询是不安全的。这只是使用isset() 的一个例子。

【讨论】:

【参考方案12】:

我真的没有在这里找到直接的答案。我发现这个问题的实际解决方案是使用PHP Code Sniffer 以及这个名为PHP Code Sniffer Variable Analysis 的很棒的扩展。

在 PHP Code Sniffer 中也可以使用常规的 PHP linter (php -l),所以我正在考虑为常规 PHP linting 自定义配置,检测未使用/未初始化的变量并验证我自己的代码样式,这一切都在一步完成.

我的最小 PHPCS 配置:

<?xml version="1.0"?>
<ruleset name="MyConfig">
    <description>Minimal PHP Syntax check</description>
    <rule ref="Generic.PHP.Syntax" />
    <rule ref="VariableAnalysis" />
</ruleset>

【讨论】:

以上是关于PHP 和未定义变量策略的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS HTTP 发布到 PHP 和未定义

试图从模型获取数据到视图,我得到这些错误: 试图获取非对象和未定义变量的属性

PHP Foreach 和未设置变量

idea thymeleaf 变量未定义

错误: - 未定义引用 `_imp__GetStockObject@4' 和未定义引用 `_imp__SetBkMode@8'

未定义的方法和未初始化的常量错误