语言构造和PHP中的“内置”函数有什么区别?


92

我知道includeissetrequireprintecho,和其他一些人都没有的功能,但语言结构。

这些语言构造中的一些需要括号,而其他则不需要。

require 'file.php';
isset($x);

有些具有返回值,而另一些则没有。

print 'foo'; //1
echo  'foo'; //no return value

那么语言构造和内置函数之间的内部区别是什么?

Answers:


131

(这比我打算的要长;请耐心等待。)

大多数语言是由一种称为“语法”的东西组成的:该语言由几个定义明确的关键字组成,并且可以使用该语法构建用该语言构造的完整表达范围。

例如,假设您有一个简单的四功能算术“语言”,仅将一位整数作为输入,而完全忽略运算顺序(我告诉过您这是一种简单的语言)。该语言可以通过以下语法定义:

// The | means "or" and the := represents definition
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /

根据这三个规则,您可以构建任意数量的单位输入算术表达式。然后,您可以编写一个解析器这句法,打破了任何有效的投入到它的组件类型($expression$number$operator)并处理结果。例如,3 + 4 * 5可以将表达式分解如下:

// Parentheses used for ease of explanation; they have no true syntactical meaning
$expression = 3 + 4 * 5
            = $expression $operator (4 * 5) // Expand into $exp $op $exp
            = $number $operator $expression // Rewrite: $exp -> $num
            = $number $operator $expression $operator $expression // Expand again
            = $number $operator $number $operator $number // Rewrite again

现在,我们可以使用定义的语言对原始表达式进行完全解析的语法。一旦有了这个,我们就可以编写一个解析器来查找的所有组合的结果$number $operator $number,并在只剩下一个时吐出一个结果$number

请注意,$expression原始表达式的最终解析版本中没有剩余的构造。那是因为$expression在我们的语言中,总可以简化为其他形式的组合。

PHP大致相同:语言结构被认为与我们的$numberor 等效$operator。它们不能简化为其他语言结构 ; 相反,它们是构建语言的基础单元。函数和语言构造之间的主要区别在于:解析器直接处理语言构造。它将功能简化为语言结构。

语言构造可能需要或可能不需要括号的原因,以及某些具有返回值而另一些则不完全取决于PHP解析器实现的特定技术细节的原因。我对解析器的工作方式并不精通,因此我无法具体解决这些问题,但是请再想一想以这种语言开头的语言:

$expression := ($expression) | ...

实际上,这种语言可以自由地使用找到的任何表达式并摆脱周围的括号。PHP(在这里我使用纯粹的猜测方法)对其语言结构可能采用类似的方式:print("Hello")可能会简化为print "Hello"解析之前的语言,反之亦然(语言定义可以添加括号,也可以删除括号)。

这就是为什么您无法重新定义诸如echo或的语言构造的根源print:它们已经有效地硬编码到了解析器中,而函数却映射到了一组语言构造,并且解析器允许您在编译时或运行时将该映射更改为替换您自己的一组语言构造或表达式。

归根结底,构造和表达式之间的内部区别是:解析器可以理解和处理语言构造。虽然内置功能由语言提供,但在解析之前会被映射并简化为一组语言结构。

更多信息:

编辑:通读一些其他答案,人们会指出自己的观点。其中:

  • 内置的语言比函数调用起来更快。这是正确的,即使只是微不足道的,因为PHP解释器在解析之前不需要将该函数映射到其语言内置的等效项。但是,在现代机器上,差异几乎可以忽略不计。
  • 内置语言绕过错误检查。这可能是正确的,也可能不是正确的,具体取决于每个内置的PHP内部实现。的确,很多时候函数会具有更高级的错误检查以及内建函数没有的其他功能,这是事实。
  • 语言构造不能用作函数回调。的确如此,因为构造不是函数。他们是独立的实体。当您对内置函数进行编码时,您并不是在对带有参数的函数进行编码-内置函数的语法直接由解析器处理,并且被识别为内置函数,而不是函数。(如果您考虑使用具有一流功能的语言,这可能会更容易理解:有效地,您可以将功能作为对象传递。您不能使用内置函数做到这一点。)

2
答案很开放,足以适用于多种语言,而不仅仅是PHP。谢谢!
Levi Botelho 2013年

15

语言构造由语言本身提供(例如“ if”,“ while”,...之类的指令);因此,他们的名字。

其结果之一是,它们的调用速度比预定义或用户定义的函数快(或者,我已经听/读过几次)

我不知道它是如何完成的,但是他们可以做的一件事(因为直接集成到语言中)是“绕过”某种错误处理机制。例如,isset()可以与不存在的变量一起使用,而不会引起任何通知,警告或错误。

function test($param) {}
if (test($a)) {
    // Notice: Undefined variable: a
}

if (isset($b)) {
    // No notice
}

*请注意,并非所有语言的构造都如此。

函数和语言结构之间的另一个区别是,其中一些函数可以不带括号地调用,例如关键字。

例如 :

echo 'test'; // language construct => OK

function my_function($param) {}
my_function 'test'; // function => Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING

在这里,并非所有语言结构都如此。

我想绝对没有办法“禁用”语言构造,因为它是语言本身的一部分。另一方面,许多“内置” PHP函数并不是真正的内置函数,因为它们是由扩展提供的,因此它们始终处于活动状态(但不是全部)。

另一个区别是语言构造不能用作“函数指针”(例如,我的意思是回调):

$a = array(10, 20);

function test($param) {echo $param . '<br />';}
array_map('test', $a);  // OK (function)

array_map('echo', $a);  // Warning: array_map() expects parameter 1 to be a valid callback, function 'echo' not found or invalid function name

我现在还没有其他想法...而且我对PHP的内部知识也不了解...所以就这样吧^^

如果您在这里没有太多答案,也许您可​​以向邮件列表内部人员询问(请参阅http://www.php.net/mailing-lists.php),那里有许多PHP核心开发人员;他们是可能会知道这些东西的人^^

(我对其他答案非常感兴趣,btw ^^)

作为参考:PHP中的关键字和语言结构列表


您可以有一个函数,该函数接受未设置的变量,而无需通过引用引用该变量来生成通知。这不限于isset()之类的语言构造。
汤姆·海格

哦,没有考虑这个问题:-(谢谢!
Pascal MARTIN

4

仔细阅读代码后,我发现php解析了yacc文件中的某些语句。所以它们是特例。

(请参见Zend / zend_language_parser.y)

除此之外,我认为没有其他差异。


1

您可以覆盖内置函数。关键字是永远的。


这不是内置函数。在APD(高级PHP调试器)扩展中定义。
Ionuț G. Stan,

关于覆盖函数,您可能会在runkit扩展中有一个战利品(它也不是核心,它是一个扩展,因此不对OP做出回应,而仅对这个回答进行回答);它确实功能强大,并且比APD更新(我相信不久前我听说有人仍在研究它,即使pecl.php.net上没有显示它)
Pascal MARTIN
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.