В чем разница между языковой конструкцией и «встроенной» функцией в 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)
Паскаль МАРТИН