PHPの言語構成と「組み込み」関数の違いは何ですか?


92

私はそれを知っているincludeissetrequireprintecho、およびいくつかの他の機能が、言語構造ではありません。

これらの言語構成要素には、括弧が必要なものと不要なものがあります。

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

戻り値を持つものもあれば、持たないものもあります。

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

では、言語構造と組み込み関数の内部的な違いは何ですか?

回答:


131

(これは私が意図したよりも長いです。ご容赦ください。)

ほとんどの言語は「構文」と呼ばれるもので構成されています。言語はいくつかの明確に定義されたキーワードで構成されており、その言語で構築できる式の完全な範囲はその構文から構築されます。

たとえば、1桁の整数のみを入力として受け取り、演算の順序を完全に無視する単純な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 := + | - | * | /

これらの3つのルールから、任意の数の1桁入力の算術式を作成できます。任意の有効な入力故障していることあなたは、その構成要素のタイプ(にこの構文のためのパーサを書くことができ$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が1つだけの場合に結果を吐き出すことができます$number

$expression元の式の最終的に解析されたバージョンには構成要素が残っていないことに注意してください。それ$expressionは、私たちの言語では常に他のものの組み合わせに還元できるからです。

PHPはほとんど同じです:言語構造は、当社の同等のものとして認識されています$number$operator。それらを他の言語構造に還元することできません。代わりに、それらは言語が構築される基本単位です。関数と言語構成要素の主な違いはこれです。パーサーは言語構成要素を直接扱います。関数を言語構造に単純化します。

言語構造が括弧を必要とする場合と必要としない場合の理由、および戻り値があるものとないものの理由は、PHPパーサー実装の特定の技術的な詳細に完全に依存しています。私はパーサーがどのように機能するかについてあまり詳しくないので、これらの質問に具体的に取り組むことはできませんが、これで始まる言語を少し想像してみてください。

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

事実上、この言語は、見つけた表現を自由に取り、周囲の括弧を取り除くことができます。PHP(ここでは純粋な当て推量を採用しています)は、その言語構造に類似したものを採用しているprint("Hello")可能性があります:print "Hello"解析される前に削減されるか、またはその逆(言語定義で括弧を追加することも、括弧を取り除くこともできます)。

これがechoorのような言語構成体を再定義できない理由の根本printです:それらは効果的にパーサーにハードコードされていますが、関数は言語構成体のセットにマップされており、パーサーではコンパイル時または実行時にそのマッピングを変更できます独自の言語構成または式のセットで置き換えます。

結局のところ、構文と式の内部的な違いは次のとおりです。言語構文はパーサーによって理解され、処理されます。組み込み関数は、言語によって提供されますが、構文解析の前に、一連の言語構成にマップされ、単純化されます。

より詳しい情報:

  • Backus-Naur form、形式言語の定義に使用される構文(yaccはこの形式を使用します)

編集:他のいくつかの答えを読んで、人々は良い点を作ります。その中で:

  • 組み込み言語は、関数よりも呼び出しが高速です。PHPインタープリターは、構文解析の前にその関数を言語組み込みのものにマップする必要がないため、これはほんの少しでも当てはまります。しかし、最近のマシンでは、その違いはごくわずかです。
  • 組み込み言語はエラーチェックをバイパスします。これは、各組み込みのPHP内部実装に応じて、正しい場合とそうでない場合があります。多くの場合、組み込み関数にはない高度なエラーチェックやその他の機能が関数にあることは確かです。
  • 言語構造は関数コールバックとして使用できません。構成は関数でないため、これは当てはまります。それらは別のエンティティです。組み込み関数をコーディングするときは、引数を取る関数をコーディングするのではなく、組み込み関数の構文はパーサーによって直接処理され、関数ではなく組み込み関数として認識されます。(これは、ファーストクラスの関数を備えた言語を考えると理解しやすいかもしれません。事実上、関数をオブジェクトとして渡すことができます。組み込み関数ではできません。)

2
PHPだけでなく、多くの言語に適用できる十分な制限のないすばらしい答えです。ありがとうございました!
Levi Botelho 2013年

15

言語構成体は、言語自体によって提供されます( "if"、 "while"、...などの指示のように)。したがって、その名前。

その1つの結果は、事前定義またはユーザー定義の関数よりも呼び出すのが速いことです(または、私は何度か聞いた/読んだことがあります)。

私はそれがどのように行われるかはわかりませんが、(言語に直接統合されているため)できることの1つは、なんらかのエラー処理メカニズムを「バイパス」することです。たとえば、isset()は、通知、警告、エラーを発生させることなく、存在しない変数で使用できます。

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

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

*すべての言語の構成要素に当てはまるわけではないことに注意してください。

関数と言語構造のもう1つの違いは、キーワードのように、括弧なしで呼び出すことができるものがあることです。

例えば ​​:

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

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

ここでも、すべての言語構造に当てはまるわけではありません。

言語自体は言語の一部であるため、言語構造を「無効にする」方法はまったくないと思います。一方、多くの「組み込み」PHP関数は、常にアクティブになるように拡張機能によって提供されるため、実際には組み込まれていません(すべてではありません)。

もう1つの違いは、言語の構成要素を「関数ポインター」として使用できないことです(たとえば、コールバックなど)。

$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の内部についてはよくわかりません...だから今はそれでおしまいです^^

ここであまり答えが得られない場合は、PHPコア開発者が多数いるメーリングリストの内部http://www.php.net/mailing-lists.phpを参照)に質問することができます。彼らはおそらくそのことについて知っているだろう^^

(そして私は他の答えに本当に興味があります、ところで^^)

リファレンスとして:PHPのキーワードと言語構成のリスト


変数を参照で取得することにより、通知を生成せずに設定されていない変数を受け入れる関数を持つことができます。これは、isset()などの言語構成に限定されません。
トムヘイ

ああ、それについては考えていませんでした:-(ありがとう!
Pascal MARTIN

4

コードを調べてみると、phpがyaccファイル内のステートメントの一部を解析していることがわかりました。したがって、それらは特別なケースです。

(Zend / zend_language_parser.yを参照)

それ以外は、他に違いがあるとは思いません。


1

組み込み関数をオーバーライドできます。キーワードは永遠です。


これは組み込み関数ではありません。APD(Advanced PHP Debugger)拡張で定義されています。
Ionuț G. Stan

関数のオーバーライドについては、runkit拡張機能に略奪品がある可能性があります(これはコアではなく、拡張機能であるため、OPには応答せず、この回答にのみ応答します)。それは本当に強力で、APDよりも最近です(pecl.php.netに表示されていない場合でも、一部の人々がまだ取り組んでいると聞いたと思います)
Pascal MARTIN
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.