В чем разница между языковой конструкцией и «встроенной» функцией в PHP?


92

Я знаю , что include, isset, require, print, echo, и некоторые другие не являются функциями , но языковые конструкции.

Некоторые из этих языковых конструкций нуждаются в скобках, другие - нет.

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

У некоторых есть возвращаемое значение, у других - нет.

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

Так в чем же внутренняя разница между языковой конструкцией и встроенной функцией?

Ответы:


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 во многом аналогичен: языковые конструкции распознаются как эквивалент наших $numberили $operator. Их нельзя свести к другим языковым конструкциям ; вместо этого они являются базовыми единицами, из которых строится язык. Ключевое различие между функциями и языковыми конструкциями заключается в следующем: синтаксический анализатор имеет дело непосредственно с языковыми конструкциями. Он упрощает функции в языковых конструкциях.

Причина, по которой языковые конструкции могут требовать или не требовать скобок, и причина, по которой одни имеют возвращаемые значения, а другие нет, полностью зависит от конкретных технических деталей реализации парсера PHP. Я не так хорошо разбираюсь в том, как работает парсер, поэтому не могу конкретно ответить на эти вопросы, но представьте на секунду язык, который начинается с этого:

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

Фактически, этот язык может свободно принимать любые найденные выражения и избавляться от окружающих скобок. PHP (и здесь я использую чистые догадки) может использовать что-то подобное для своих языковых конструкций: print("Hello")может быть сокращено до того, print "Hello"как он будет проанализирован, или наоборот (определения языка могут добавлять круглые скобки, а также избавляться от них).

Это корень того, почему вы не можете переопределить языковые конструкции, такие как echoили print: они фактически жестко запрограммированы в синтаксическом анализаторе, тогда как функции отображаются на набор языковых конструкций, а синтаксический анализатор позволяет вам изменить это сопоставление во время компиляции или выполнения на замените свой собственный набор языковых конструкций или выражений.

В конце концов, внутреннее различие между конструкциями и выражениями состоит в следующем: языковые конструкции понимаются и обрабатываются синтаксическим анализатором. Встроенные функции, хотя и предоставляются языком, перед синтаксическим анализом сопоставляются и упрощаются с набором языковых конструкций.

Больше информации:

  • Форма Бэкуса-Наура , синтаксис, используемый для определения формальных языков (yacc использует эту форму)

Изменить: читая некоторые другие ответы, люди делают хорошие выводы. Среди них:

  • Встроенный язык вызывается быстрее, чем функция. Это правда, хотя бы в некоторой степени, потому что интерпретатору PHP не нужно отображать эту функцию на ее встроенные в язык эквиваленты перед синтаксическим анализом. Однако на современной машине разница весьма незначительна.
  • Встроенный язык обходит проверку ошибок. Это может быть, а может и не быть правдой, в зависимости от внутренней реализации PHP для каждой встроенной команды. Конечно, верно, что чаще всего функции будут иметь более продвинутую проверку ошибок и другие функции, которых нет у встроенных функций.
  • Языковые конструкции нельзя использовать в качестве обратных вызовов функций. Это правда, потому что конструкция не является функцией . Это отдельные сущности. Когда вы кодируете встроенную функцию, вы не кодируете функцию, которая принимает аргументы - синтаксис встроенной функции обрабатывается непосредственно анализатором и распознается как встроенная функция, а не функция. (Это может быть легче понять, если вы рассматриваете языки с функциями первого класса: по сути, вы можете передавать функции как объекты. Вы не можете сделать это с помощью встроенных функций.)

2
Отличный ответ, достаточно открытый, чтобы его можно было применить ко многим языкам, а не только к PHP. Спасибо!
— Леви Ботельо

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; они те, кто, вероятно, знают об этом ^^

(И меня действительно интересуют другие ответы, кстати ^^)

Для справки: список ключевых слов и языковых конструкций в PHP.


У вас может быть функция, которая принимает неустановленную переменную без генерации уведомления, принимая переменную по ссылке. Это не ограничивается языковыми конструкциями, такими как isset ().
— Tom Haigh

О, не думал об этом :-( Спасибо!
— Паскаль МАРТИН

4

Пройдя по коду, я обнаружил, что php анализирует некоторые инструкции в файле yacc. Так что это особые случаи.

(см. Zend / zend_language_parser.y)

Кроме того, я не думаю, что есть другие отличия.


1

Вы можете переопределить встроенные функции . Ключевые слова навсегда.


Это не встроенная функция. Определяется в расширении APD (Advanced PHP Debugger).
— Ionuț G. Stan

насчет переопределения функций, у вас может быть добыча в расширении runkit (это тоже не ядро, это расширение, поэтому не отвечает на OP, а только на этот ответ); он действительно мощный и новее, чем APD (и я думаю, что некоторое время назад слышал, что некоторые люди все еще работают над ним, даже если он не показан на pecl.php.net)
— Паскаль МАРТИН
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.