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) /* ... */
当然,您可以使用isset
1 在给定点检查变量是否存在。但是,如果您的代码依赖于此,那么它的结构可能很差。我的观点是,与例如相反。在 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 和未定义变量策略的主要内容,如果未能解决你的问题,请参考以下文章
试图从模型获取数据到视图,我得到这些错误: 试图获取非对象和未定义变量的属性
错误: - 未定义引用 `_imp__GetStockObject@4' 和未定义引用 `_imp__SetBkMode@8'