PHP的隐藏功能是什么?

174

我知道这个问题听起来像是在刻意追求积分,但是让我解释一下我的意图。

我大学毕业后在一个PHP团队找到了工作。我在那里工作了一年半,认为自己已经学会了所有关于编程的东西。

后来,我在一个规模相当大的公司担任一人内部开发岗位,所有的工作都是用C#完成的。为了胜任这份工作,我开始阅读大量的博客和书籍,很快就意识到我曾经认为自己已经掌握了所有技能是多么的错误。我学习了单元测试、依赖注入、装饰器模式、松耦合设计原则、组合优于继承的争论等等——至今仍在不断吸收学习。毫无疑问,过去一年中我的编程风格完全改变了。

现在,我发现自己正在为朋友的初创公司开发一个PHP项目,并感到完全受限制,与使用C#编程形成了鲜明对比。对我来说,所有类范围内的变量必须通过'$this->'来引用真的很烦人。我很烦恼试过的所有IDE都没有很好的智能提示,而我的SimpleTest单元测试方法必须以单词“test”开头。动态类型让我不能隐式地指定方法期望的参数类型,并且您必须编写switch语句来进行方法重载。我无法忍受你不能有嵌套的命名空间并且必须使用::运算符来调用基类的构造函数。

现在,我并不想发起PHP vs C#的辩论,我只是想说,我相信在PHP中有一些功能我还不知道或者还没有正确使用过。我已经习惯了C#的编程环境,很难看到外面的新鲜事物。

因此,我想问问您,您最喜欢的PHP功能是什么?在PHP中有哪些事情是您可以做但.NET语言更难实现的呢?


面向对象编程的缺陷?对我来说,这是你发现的最糟糕的“隐藏”功能。 - knoopx
这些线程有点有趣...因为对于我所在的团队,“隐藏功能”是一个代表“漏洞”的代码短语。你知道吗...有时候发现一个隐藏的功能并不一定是好事... - Ganesh Shankar
@Ganesh 一个人的错误可能是另一个人的隐藏功能... - Xeoncross
78个回答

34

HEREDOC语法是我最喜欢的隐藏功能。它总是很难找到,因为你无法通过谷歌搜索<<<,但它可以帮助你避免转义大块的HTML并仍然允许你将变量插入到流中。

echo <<<EOM
  <div id="someblock">
    <img src="{$file}" />
  </div>
EOM;

2
HEREDOC 是我最喜欢构建和使用 SQL 语句的方式。 - bdl
11
注意:结束符“EOM;”不能缩进。 - micahwittman
7
亲爱的上帝,这是我见过的最丑陋的代码。如果你正在使用HEREDOC,则没有将展示与逻辑分开。 - Lotus Notes
@Byron:你不必将它用于演示,它可以用于任何字符串。请参阅bdl的评论。 - Tom Pažourek
最好使用MVC模式将HTML、Javascript与PHP分离。 - daGrevis
显示剩余2条评论

34

可能很少有人知道可以将常量“变量”指定为函数参数的默认值:

function myFunc($param1, $param2 = MY_CONST)
{
//code...
}

字符串可以像数组一样使用:

$str = 'hell o World';
echo $str; //outputs: "hell o World"

$str[0] = 'H';
echo $str; //outputs: "Hell o World"

$str[4] = null;
echo $str; //outputs: "Hello World"

3
这最后一个很棒。虽然我不知道它何时比其他方法更好地删除字符,但还是要点个赞。 - George Mauer
3
用函数来完成这个任务可能比直接调用更有效率。字符串通常是按照连续的方式存储在内存中的,因此访问 $str[4] 是很简单的。将字符串存储为字符数组是大多数源自C语言的编程语言所共有的做法。 - sjobe
1
您不必使用已定义的常量作为默认值。以下也是完全有效的:function foot($param1, $default = array('key'=>'value'), $default_s = 'String', $default_i = 10, $default_b = false)。但是,您正确地指出不能将变量用作默认参数。 - dcousineau
@dcousineau 你的例子是完全有效的,因为array()不是一个函数而是一种语言结构。函数调用不能作为参数的默认值。 - Jamol
你可以像这样访问字符串中的字符: $a_char=$a_string{2} - Felipe
4
如果你有多字节字符串(例如外语等),请小心将字符串视为数组。 - philfreo

33
PHP代码最有用的一点是,如果我对一个函数不太理解,我可以通过浏览器搜索并输入以下内容来查找它:

http://php.net/function

上个月我在一些代码中看到了"range"函数。这是我从未使用过的数百个函数之一,但事实证明它非常有用。

http://php.net/range

那个URL是https://www.php.net/manual/en/function.range.php的别名。将函数和关键字映射到URL的这个简单想法真是太棒了。
我希望其他语言、框架、数据库和操作系统都有一个像这样简单的机制来查找文档。

5
range() 函数可用于 foreach( range(1, 10) as $i) { }; - alex
如果您使用的是 FireFox 浏览器;只需在地址栏键入 "PHP 函数",它会进行一个谷歌“幸运之星”搜索,您几乎总能找到正确的 PHP 文档页面。 - Kolky

30

快速块注释

/*
    die('You shall not pass!');
//*/


//*
    die('You shall not pass!');
//*/

这些注释允许您切换代码块是否使用一个字符进行注释。


14
这并不是只适用于PHP语言。任何支持// ...单行注释和/* ... */块注释的语言都可以使用这种方式。 - Jordan Ryan Moore
任何代码清理工具最终都会因为你使用这个而讨厌你... ;) - Talvi Watia
3
我之前也用过 /** //**/。你可以通过删除或添加第一个注释中的空格来切换该块注释。这种方法还有一个优点,就是适用于 CSS(以及其他不支持 // ... 注释的语言)。 - kingjeffrey
顺便提一下,这是原文链接:http://aleembawany.com/2009/01/27/lazy-block-comment-trick - aleemb
@aleemb,请不要再对这个问题进行任何修改。 - Sam Becker

29

我的清单...大部分更多地属于“隐藏功能”而非“最喜欢的功能”(我希望如此!),并且并非所有功能都有用,但是...是的。

// swap values. any number of vars works, obviously  
list($a, $b) = array($b, $a);

// nested list() calls "fill" variables from multidim arrays:  
$arr = array(  
  array('aaaa', 'bbb'),  
  array('cc', 'd')  
);  
list(list($a, $b), list($c, $d)) = $arr;  
echo "$a $b $c $d"; // -> aaaa bbb cc d  

// list() values to arrays  
while (list($arr1[], $arr2[], $arr3[]) = mysql_fetch_row($res)) { .. }  
// or get columns from a matrix  
foreach($data as $row) list($col_1[], $col_2[], $col_3[]) = $row;

// abusing the ternary operator to set other variables as a side effect:  
$foo = $condition ? 'Yes' . (($bar = 'right') && false) : 'No' . (($bar = 'left') && false);  
// boolean False cast to string for concatenation becomes an empty string ''.  
// you can also use list() but that's so boring ;-)  
list($foo, $bar) = $condition ? array('Yes', 'right') : array('No', 'left');

你也可以嵌套三元运算符,在某些情况下非常方便。

// the strings' "Complex syntax" allows for *weird* stuff.  
// given $i = 3, if $custom is true, set $foo to $P['size3'], else to $C['size3']:  
$foo = ${$custom?'P':'C'}['size'.$i];  
$foo = $custom?$P['size'.$i]:$C['size'.$i]; // does the same, but it's too long ;-)  
// similarly, splitting an array $all_rows into two arrays $data0 and $data1 based  
// on some field 'active' in the sub-arrays:  
foreach ($all_rows as $row) ${'data'.($row['active']?1:0)}[] = $row;

// slight adaption from another answer here, I had to try out what else you could  
// abuse as variable names.. turns out, way too much...  
$string = 'f.> <!-? o+';  
${$string} = 'asdfasf';  
echo ${$string}; // -> 'asdfasf'  
echo $GLOBALS['f.> <!-? o+']; // -> 'asdfasf'  
// (don't do this. srsly.)

${''} = 456;  
echo ${''}; // -> 456  
echo $GLOBALS['']; // -> 456  
// I have no idea.  

好的,我现在停止翻译 :-)


嗯,已经有一段时间了..

// just discovered you can comment the hell out of php:
$q/* snarf */=/* quux */$_GET/* foo */[/* bar */'q'/* bazz */]/* yadda */;

所以,刚刚发现如果你用花括号将任意字符串包起来,就可以将其作为方法名传递。但是你不能定义任何字符串作为方法名,不过你可以通过__call()捕获它们,并根据需要进一步处理。嗯...

class foo {
  function __call($func, $args) {
    eval ($func);
  }
}

$x = new foo;
$x->{'foreach(range(1, 10) as $i) {echo $i."\n";}'}();

我在 Reddit 的评论中发现了这个小宝石:

$foo = 'abcde';
$strlen = 'strlen';
echo "$foo is {$strlen($foo)} characters long."; // "abcde is 5 characters long."

您不能直接在{}内部调用函数,但是您可以使用保存函数名称的变量并调用它们! (*同时*您也可以在其上使用变量变量)


2
请不要过度使用三元比较运算符,这会导致代码混乱。 - staticsan
嗯,交换技巧很棒且实用,谢谢。 - OZ_
1
${''} = 456; 哈哈哈... 真是滥用。 - Skurmedel
+1 对于 ${''} = 456;。最后得到一个完全匿名的变量。 - Halil Özgür
在你的帖子中,“Abuse” 在每个方面上都被正确地使用了。 - Tim
显示剩余2条评论

26

我有点像你,已经编写PHP超过8年了。大约一年前,我必须参加.NET/C#课程,我真的很喜欢C#语言(讨厌ASP.NET),但这使我成为了一个更好的PHP开发人员。

作为一种语言,PHP相当差劲,但是,我非常熟练使用它,LAMP堆栈也很棒。最终产品远远超过部分总和。

话虽如此,针对您的问题:

http://uk.php.net/SPL

我喜欢SPL,C#中的集合类是我开始使用时就喜欢的东西。现在我可以同时实现两者。

安德鲁


26

数组操作。
有很多工具可以用来操作和处理数组。这并不是 PHP 独有的,但我从未使用过一种语言使得操作如此简单易行。


像什么样的例子?在我看来,所有函数的命名和位置都很尴尬,都在全局命名空间中。此外,除了使用 $arr[] = $newvalue 添加值之外,我想不到任何其他语言不同的东西,这很酷。 - George Mauer
8
PHP数组是一种数据结构,可以方便地用作栈、队列、双端队列、列表、哈希表等。对于大多数常见需求,仅使用array_*函数就足够灵活了。 - Camilo Díaz Repka
6
Python在数组方面(作为列表和元组)比PHP表现更出色。 - too much php

24

我有一点惊讶,竟然没有人提到这个技巧,但我的一个最喜欢的数组技巧是使用加号运算符。它有点像array_merge()但更简单。我发现这通常是我想要的。实际上,它将RHS中的所有条目复制到LHS的副本中,并根据需要覆盖它们(即它是非交换的)。非常适用于使用“默认”数组开始并一次性添加一些真实值,同时为未提供的值保留默认值。

请求代码示例:

// Set the normal defaults.
$control_defaults = array( 'type' => 'text', 'size' => 30 );

// ... many lines later ...

$control_5 = $control_defaults + array( 'name' => 'surname', 'size' => 40 );
// This is the same as:
// $control_5 = array( 'type' => 'text', 'name' => 'surname', 'size' => 40 );

3
当你需要维护大量的代码时,我认为它不如array_merge函数清晰明了。至少当你使用array_merge函数时,很明显你正在处理数组。 - Sylvain
这是一个很棒的功能,需要注意的是,在“+”号右侧的数组不会覆盖“+”号左侧数组中已有的键。 - Wil Moore III
实际上,@wilmmore,它确实会。这正是使其如此有用的特性。 - staticsan
我认为左侧数组中找到的元素是那些保留下来的,即没有被右侧数组上的元素覆盖。因此,我认为 size 仍然是30。 - BDuelz
如果您将此用作 array_merge 的话,那么您是错误的: “+ 运算符返回左手边数组追加右手边数组;对于两个数组中都有的键值,将使用左手边数组中的元素,而来自右手边数组的匹配元素将被忽略。” 参见 http://php.net/manual/en/language.operators.array.php 和 http://php.net/manual/en/function.array-merge.php - Halil Özgür
显示剩余3条评论

21

快速粗略是默认设置。
这种语言充满了有用的简短方式,使PHP成为具有短时间上市的(小型)项目的理想选择。 并不是说清晰的PHP代码不可能,只是需要更多的努力和经验。

但我喜欢PHP是因为它让我表达自己的想法而不必打一篇文章。

PHP:

if (preg_match("/cat/","one cat")) {
   // do something
}

JAVA:

import java.util.regex.*;
Pattern p = Pattern.compile("cat");
Matcher m = p.matcher("one cat")
if (m.find()) {
  // do something
}

是的,这也包括不要打Int


4
你应该使用 strpos 函数:if (false !== strpos("one cat", "cat")) {。该函数可用于查找字符串中指定子串的位置,如果找到则返回该子串第一次出现的索引值,否则返回 false。以上代码的意思是:如果在字符串 "one cat" 中找到了子串 "cat",则执行后面的代码块。 - OIS
17
他的例子旨在说明和比较快速正则表达式匹配的运行方式,而不是如何在 "one cat" 中查找字符串 "cat"。 - dcousineau
很好的点子 @eyelidlessness。我使用ereg()已经有好几年了,自从php 5.3以来就改用preg了。我不喜欢过时的消息;-) - Bob Fanger
19
如果(Pattern.matches("cat", "one cat")){ // 执行某些操作 }如果你不了解Java,请不要抱怨它。 - whiskeysierra
3
+1 Willi,你好!请问如何在Java中实现 preg_replace('/([<[]!--\s*)(\S*?)(\s*--[>]]?)/se', "$this->Choose('\1','\2','\3',$data)", $text)?该函数用于查找输入文本中的注释,并使用匹配元素调用一个函数。在这种情况下,$this->choose(...) 函数决定替换匹配项并返回结果。 - DeveloperChris
1
正则表达式在PHP中相当糟糕...我更喜欢Perl或JavaScript风格的正则表达式,其中//是语言内置的。 - cdmckay

21

这里有一个例子,我喜欢如何在未提供函数参数的情况下设置默认值,它更加简单:

function MyMethod($VarICareAbout, $VarIDontCareAbout = 'yippie') { }

4
挺有趣的,上周我在 Google Reader 中看到了这个“隐藏功能”。我不明白它到底哪里隐藏了——这只是基本的语法。例如,尝试使用 if($var = true)。 - Ross
8
比什么更容易?大多数语言都具有这个特点。 - Christian Davén
10
比C#(我认为包括C++和Java在内)更容易。 - George Mauer
8
C++ 支持默认参数值。 - sjobe
2
C#完全不支持默认值。你必须编写一个重载函数并始终声明该值,这非常繁琐。 - DeveloperChris
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接