(Это дольше, чем я предполагал; пожалуйста, потерпите меня.)
Большинство языков состоит из того, что называется «синтаксисом»: язык состоит из нескольких четко определенных ключевых слов, и полный диапазон выражений, которые вы можете построить на этом языке, строится из этого синтаксиса.
Например, предположим, что у вас есть простой арифметический «язык» с четырьмя функциями, который принимает на вход только однозначные целые числа и полностью игнорирует порядок операций (я сказал вам, что это был простой язык). Этот язык можно определить с помощью синтаксиса:
$expression := $number | $expression $operator $expression
$number := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
$operator := + | - | * | /
Из этих трех правил вы можете построить любое количество арифметических выражений с однозначным вводом. Вы можете написать парсер для этого синтаксиса , который сломается любой действительный вклад в его составных типов ( $expression
, $number
или $operator
) и сделок с результатом. Например, выражение 3 + 4 * 5
можно разбить следующим образом:
$expression = 3 + 4 * 5
= $expression $operator (4 * 5)
= $number $operator $expression
= $number $operator $expression $operator $expression
= $number $operator $number $operator $number
Теперь у нас есть полностью проанализированный синтаксис на нашем определенном языке для исходного выражения. Когда у нас есть это, мы можем пройти и написать синтаксический анализатор, чтобы найти результаты всех комбинаций $number $operator $number
, и выдать результат, когда у нас останется только один $number
.
Обратите внимание, что $expression
в окончательной проанализированной версии нашего исходного выражения не осталось никаких конструкций. Это потому, что $expression
всегда можно свести к комбинации других вещей на нашем языке.
PHP во многом аналогичен: языковые конструкции распознаются как эквивалент наших $number
или $operator
. Их нельзя свести к другим языковым конструкциям ; вместо этого они являются базовыми единицами, из которых строится язык. Ключевое различие между функциями и языковыми конструкциями заключается в следующем: синтаксический анализатор имеет дело непосредственно с языковыми конструкциями. Он упрощает функции в языковых конструкциях.
Причина, по которой языковые конструкции могут требовать или не требовать скобок, и причина, по которой одни имеют возвращаемые значения, а другие нет, полностью зависит от конкретных технических деталей реализации парсера PHP. Я не так хорошо разбираюсь в том, как работает парсер, поэтому не могу конкретно ответить на эти вопросы, но представьте на секунду язык, который начинается с этого:
$expression := ($expression) | ...
Фактически, этот язык может свободно принимать любые найденные выражения и избавляться от окружающих скобок. PHP (и здесь я использую чистые догадки) может использовать что-то подобное для своих языковых конструкций: print("Hello")
может быть сокращено до того, print "Hello"
как он будет проанализирован, или наоборот (определения языка могут добавлять круглые скобки, а также избавляться от них).
Это корень того, почему вы не можете переопределить языковые конструкции, такие как echo
или print
: они фактически жестко запрограммированы в синтаксическом анализаторе, тогда как функции отображаются на набор языковых конструкций, а синтаксический анализатор позволяет вам изменить это сопоставление во время компиляции или выполнения на замените свой собственный набор языковых конструкций или выражений.
В конце концов, внутреннее различие между конструкциями и выражениями состоит в следующем: языковые конструкции понимаются и обрабатываются синтаксическим анализатором. Встроенные функции, хотя и предоставляются языком, перед синтаксическим анализом сопоставляются и упрощаются с набором языковых конструкций.
Больше информации:
- Форма Бэкуса-Наура , синтаксис, используемый для определения формальных языков (yacc использует эту форму)
Изменить: читая некоторые другие ответы, люди делают хорошие выводы. Среди них:
- Встроенный язык вызывается быстрее, чем функция. Это правда, хотя бы в некоторой степени, потому что интерпретатору PHP не нужно отображать эту функцию на ее встроенные в язык эквиваленты перед синтаксическим анализом. Однако на современной машине разница весьма незначительна.
- Встроенный язык обходит проверку ошибок. Это может быть, а может и не быть правдой, в зависимости от внутренней реализации PHP для каждой встроенной команды. Конечно, верно, что чаще всего функции будут иметь более продвинутую проверку ошибок и другие функции, которых нет у встроенных функций.
- Языковые конструкции нельзя использовать в качестве обратных вызовов функций. Это правда, потому что конструкция не является функцией . Это отдельные сущности. Когда вы кодируете встроенную функцию, вы не кодируете функцию, которая принимает аргументы - синтаксис встроенной функции обрабатывается непосредственно анализатором и распознается как встроенная функция, а не функция. (Это может быть легче понять, если вы рассматриваете языки с функциями первого класса: по сути, вы можете передавать функции как объекты. Вы не можете сделать это с помощью встроенных функций.)