PHP的怪癖和陷阱

PHP的怪癖和陷阱,php,Php,我意识到,尽管我的大部分经验都是编写PHP应用程序,但我发现自己时常犯“初学者错误”。这是因为PHP是一种有机发展的语言,因此有一些我不知道的特质、怪癖和陷阱 我想让这个问题成为一个维基,让所有想了解PHP的陷阱和例外的人都能从我们可能认为的规则中获益。但请不要写一般性的回答,比如: 有些函数作为参数接收参数 $needle,$haystack,而有些 $haystack,$needle 告诉函数名。你可以从我这里得到一些答案作为例子。哦,每个答案加上一个陷阱。这样我们就可以(通过投票)知道他们

我意识到,尽管我的大部分经验都是编写PHP应用程序,但我发现自己时常犯“初学者错误”。这是因为PHP是一种有机发展的语言,因此有一些我不知道的特质、怪癖和陷阱

我想让这个问题成为一个维基,让所有想了解PHP的陷阱和例外的人都能从我们可能认为的规则中获益。但请不要写一般性的回答,比如:

有些函数作为参数接收参数
$needle
$haystack
,而有些
$haystack
$needle

告诉函数名。你可以从我这里得到一些答案作为例子。哦,每个答案加上一个陷阱。这样我们就可以(通过投票)知道他们中哪一个是最被鄙视的

我不想挑起一场火焰战,就直说吧。如果您想写一些关于PHP的不好的东西,那么请将其作为对相应答案的注释

希望这个维基对我们所有人、初学者和专家都有帮助

更新:


在Andrew Moore的评论之后,我认为答案还应该包含陷阱的解决方案或解决方法。

尽管字符串可以使用
循环和索引进行迭代,但不能使用
foreach
循环进行迭代。例如:

$str = 'foo';
$max = strlen($str);

for ($i=0; $i<$max; $i++)  {
    echo $str[$i];
}
// outputs: foo

foreach ($str as $char) {
    echo $char;
}
// Warning: Invalid argument supplied for foreach() ...
$str='foo';
$max=strlen($str);

对于($i=0;$i),序列化处理XML结构的对象然后取消序列化不会恢复原始XML结构:

$dom = new DOMDocument;
$dom->loadXML('<test/>');

$dom = serialize($dom);
$dom = unserialize($dom);

var_dump($dom->saveXML());
// $ Warning: DOMDocument::saveXML(): Couldn't fetch DOMDocument in ...
// $ NULL
$dom=新的DOMDocument;
$dom->loadXML(“”);
$dom=序列化($dom);
$dom=unserialize($dom);
var_dump($dom->saveXML());
//$Warning:DOMDocument::saveXML():无法在中获取DOMDocument。。。
//$NULL

与SimpleXML对象类似。

非行业标准运算符优先级

$x = 1;
echo 'foo: ' . $x+1 . ' bar';

将输出:“
1 bar
”,而不是预期的“
foo:2 bar
”。解决方案:使用括号。

整数大小取决于平台。在没有任何外部模块的32位计算机上,通常不能使用64位整数。此外,不能声明无符号整数。

我最喜欢的:

<?php
$a = 0;
$b = 'x';
var_dump(FALSE == $a);
var_dump($a == $b);
var_dump($b == TRUE);

echo' <br />Conclusion: TRUE equals FALSE (at least in PHP)';

一些新引入的PHP功能没有效果,因为无法保证在托管环境中默认支持它们

我最大的不满是启用
标记语法的
short\u tags
设置。我认为PHP应该默认启用此功能,而不是选择加入


==编辑==


在PHP>=5.4中,速记echo语句不再考虑
short_tags
设置,因此它将在支持PHP 5.4及以上版本的所有托管环境中都可用。

我讨厌使用
?>
关闭标记关闭PHP文件时会出现问题(这似乎应该是另一种方式)。例如,在?>之后包含一个带有一些空格的文件,然后尝试更改标题(假设没有输出缓冲)。呃。我花了很长时间才学会从不使用
?>

=EDIT= 这对于PHP>=5.5不再适用!

我有时遇到的是
致命错误:无法使用函数
使用
empty()
构造。例如:

if (!empty(trim($_GET['s']))) {
    // ...
}
array_pop(  explode('\\', get_class($this))  )
empty()
需要一个变量,否则将导致错误

解决方案:

$s = trim($_GET['s']);
if (!empty($s)) {
    // ...
}

内置函数的命名约定不一致。例如,这组字符串处理函数:

 str_shuffle()
 str_split()
 str_word_count()
 strcasecmp()
 strchr()
 strcmp()
解决方案:始终打开手册

$x = "a string";

if ($x == 142)
{
    echo "lol, of course $x is a number";
}
else
{
    echo "$x is clearly not equal to 142";
}
运行时:

lol, of course a string is a number
a string is clearly not equal to 142
您必须使用===

$x = "a string";

if ($x === 142)
{
    echo "lol, of course $x is a number";
}
else
{
    echo "$x is clearly not equal to 142";
}
运行时:

lol, of course a string is a number
a string is clearly not equal to 142

进行真理测试的多种方法(,)+弱类型=

有了一些原则,您基本上可以避免参考下表:

  • 对于一般真值测试,可以使用布尔比较
    if($)
    {…}
    if(!$x){…}
    。它的行为方式与大多数语言中的布尔运算符相同

  • 如果您想测试表单输入的falsy值,请始终使用
    empty()
    (它将“0”视为false)

  • 如果要确定是否设置了变量,请始终使用
    isset()

  • 如果只需要检查null,请使用
    is_null()
    $x===null


我在把foreach和参考资料结合起来时遇到了各种麻烦

$testarray = array(1 => "one", 2 => "two");
$item = "three";
$testarray[3] =& $item;
foreach ($testarray as $key => $item) {
  // do nothing
}
echo $testarray[3]; // outputs "two"

在PHP4时代,这真的让我大吃一惊,尽管在PHP5中,如果不使用显式引用,行为会变得更理智,但我仍然时不时地被这一点所困扰。

让函数在不同的操作系统上表现不同,有些函数只在特定的操作系统上可用

例如,Windows上的
mail()
函数不能在
$to
参数中使用发件人名称。它只能包含电子邮件地址。在Linux上,所有功能都可以正常工作

另一个例子是,函数
strtime()
仅在Linux上可用,而在Windows上不可用(这使我进入了一个现有的项目,我想在我的Windows机器上运行)


当然,有些函数只在某些操作系统上有意义(如Win32API函数),但其他许多函数似乎在所有操作系统上都应该有相同的行为,而事实上它们没有。

有时您喜欢将函数结果直接传递给另一个只接受变量作为输入的函数

例如:

if (!empty(trim($_GET['s']))) {
    // ...
}
array_pop(  explode('\\', get_class($this))  )
…将导致E_STRICT-error“只有变量应通过引用传递”。要使其正常工作,只需在内部(返回)语句周围添加另一对括号:

array_pop( (explode('\\', get_class($this))) )
(如果您使用的是名称空间和扩展类,则此行仅返回调用对象的类名(不带名称空间),而不是扩展并可能包含此函数的父类。)