В чем разница между языковой конструкцией и "встроенной" функцией в PHP?
Я знаю это include
, isset
, require
, print
, echo
и некоторые другие не функции, а языковые конструкции.
Некоторые из этих языковых конструкций нуждаются в скобках, другие нет.
require 'file.php';
isset($x);
Некоторые имеют возвращаемое значение, другие нет.
print 'foo'; //1
echo 'foo'; //no return value
Так в чем же заключается внутренняя разница между языковой конструкцией и встроенной функцией?
4 ответа
(Это дольше, чем я хотел; пожалуйста, потерпите меня.)
Большинство языков состоят из так называемого "синтаксиса": язык состоит из нескольких четко определенных ключевых слов, и весь спектр выражений, которые вы можете построить на этом языке, построен на основе этого синтаксиса.
Например, допустим, у вас есть простой четырехфункциональный арифметический "язык", который принимает в качестве входных данных только однозначные целые числа и полностью игнорирует порядок операций (я говорил вам, что это был простой язык). Этот язык может быть определен по синтаксису:
// 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 для каждой встроенной. Конечно, верно, что чаще всего функции будут иметь более продвинутую проверку ошибок и другие функциональные возможности, которых нет у встроенных функций.
- Языковые конструкции не могут использоваться в качестве обратных вызовов функций. Это правда, потому что конструкция не является функцией. Это отдельные сущности. Когда вы кодируете встроенную функцию, вы не кодируете функцию, которая принимает аргументы - синтаксис встроенной функции обрабатывается непосредственно анализатором и распознается как встроенная функция, а не функция. (Это может быть легче понять, если вы рассматриваете языки с первоклассными функциями: фактически вы можете передавать функции как объекты. Вы не можете делать это с помощью встроенных функций.)
Языковые конструкции предоставляются самим языком (например, такие инструкции, как "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
После просмотра кода я обнаружил, что php анализирует некоторые операторы в файле yacc. Так что это особые случаи.
(см. Zend/zend_language_parser.y)
Кроме того, я не думаю, что есть другие различия.
Вы можете переопределить встроенные функции. Ключевые слова навсегда.