web-dev-qa-db-ja.com

PHPネストされた関数とは何ですか?

JavaScriptでは、ネストされた関数は非常に便利です。クロージャ、プライベートメソッド、そして何がありますか。

ネストされたPHP関数は何ですか?誰がそれらを使用しますか?

これは私がやった小さな調査です

<?php
function outer( $msg ) {
    function inner( $msg ) {
        echo 'inner: '.$msg.' ';
    }
    echo 'outer: '.$msg.' ';
    inner( $msg );
}

inner( 'test1' );  // Fatal error:  Call to undefined function inner()
outer( 'test2' );  // outer: test2 inner: test2
inner( 'test3' );  // inner: test3
outer( 'test4' );  // Fatal error:  Cannot redeclare inner()
75
meouw

基本的には何もありません。私はこれを常にパーサーの副作用として扱ってきました。

Eran Galperinは、これらの関数が何らかの形でプライベートであると誤解されており、outer()が実行されるまで単純に宣言されていません。また、プライベートスコープではなく、遅延はしますがグローバルスコープを汚染します。そして、コールバックとして、外側のコールバックはまだ一度しか呼び出せません。エイリアスを複数回呼び出す可能性が非常に高い配列にそれを適用することがどのように役立つかはまだわかりません。

掘り起こすことができる唯一の「現実世界」の例は this で、これは一度だけ実行でき、クリーナーIMOを書き換えることができます。

私が考えることができる唯一の用途は、モジュールが[name] _includeメソッドを呼び出すことです。

if (!function_exists ('somefunc')) {
  function somefunc() { }
}

チェック。

PHPのOOPは明らかに良い選択です:)

85
Martijn Laarman

PHP 5.3を使用している場合、匿名関数を使用すると、Javascriptに似た動作を得ることができます。

<?php
function outer() {
    $inner=function() {
        echo "test\n";
    };

    $inner();
}

outer();
outer();

inner(); //PHP Fatal error:  Call to undefined function inner()
$inner(); //PHP Fatal error:  Function name must be a string
?>

出力:

test
test
83
user641463

[@ PierredeLESPINAYのコメントに従って書き直されました。]

これは単なる副作用ではありませんが、実際には動的に変更するプログラムのロジックにとって非常に便利な機能です。手続き型のPHP日からですが、最も簡単に特定のスタンドアロン関数の代替実装を提供したい場合は、OOアーキテクチャも便利です。 (可能であれば、ほとんどの場合、OOがより良い選択であり、それはオプションであり、委任ではなく、いくつかの単純なタスクは余計な作業を必要としません。)

たとえば、フレームワークからプラグインを動的/条件付きでロードし、プラグイン作成者の生活を非常に簡単にしたい場合、プラグインがオーバーライドしなかったいくつかの重要な機能のデフォルトの実装を提供できます。

<?php // Some framework module

function provide_defaults()
{
    // Make sure a critical function exists:
    if (!function_exists("tedious_plugin_callback"))
    {
        function tedious_plugin_callback()
        {
        // Complex code no plugin author ever bothers to customize... ;)
        }
    }
}
9
Sz.

関数内で定義された関数あまり使用できませんが、条件付きで定義された関数は使用できます。例えば:

if ($language == 'en') {
  function cmp($a, $b) { /* sort by English Word order */ }
} else if ($language == 'de') {
  function cmp($a, $b) { /* sort by German Word order; yes it's different */ }
} // etc

そして、コードで行う必要があるのは、usort()呼び出しなどで 'cmp'関数を使用することだけです。これにより、コード全体で言語チェックを無駄にすることはありません。今、私はこれをしていませんが、それを行うための議論を見ることができます。

7
cletus

上記のことはすべて、単純にネストされた関数を作成して、関数内のローカライズされた繰り返しコード(親関数内でのみ使用される)を置き換えることができます。無名関数はこれの完璧な例です。

クラスにプライベートメソッド(またはより小さいコードブロック)を作成するだけの人もいるかもしれませんが、それは非常に特殊なタスク(親に排他的)をモジュール化する必要があるが、必ずしも他の人が利用できるとは限りませんクラス。良いニュースは、その関数が他のどこかに必要であることが判明した場合、修正はかなり基本的なことです(定義をより中央の場所に移動します)。

一般的に、他のCベースのプログラミング言語を評価するための標準としてJavaScriptを使用するのは悪い考えです。 JavaScriptは、PHP、Python、Perl、C、C++、およびJavaと比較すると、間違いなく独自の動物です。もちろん、一般的な類似点はたくさんありますが、非常にきめ細かな詳細(リファレンスJavaScript:The Definitive Guide、6th Edition、Chapter 1-12)に注意を払うと、コアJavaScriptがユニークになり、美しく、異なって、シンプルで、複雑なものです。それは私の2セントです。

明確にするために、ネストされた関数がプライベートであると言っているわけではありません。そのネストは、些細なことをモジュール化する必要がある場合(そして親関数でのみ必要な場合)に混乱を避けるのに役立ちます。

3

私のphpはすべてオブジェクト指向ですが、特に関数が再帰的であり、必ずしもオブジェクトではない場合、ネストされた関数の使用を確認しています。つまり、ネストされている関数の外部では呼び出されませんが、再帰的であり、関数である必要があります。

他の1つのメソッドを明示的に使用するための新しいメソッドを作成しても、ほとんど意味がありません。私にとってそれは不器用なコードであり、オブジェクト指向ではありません。他の場所でその関数を呼び出さない場合は、入れ子にします。

2

Webサービスの呼び出しでは、ネストされた方法で、数千の関数でいっぱいのライブラリ上の個々の関数を含め、動的にはるかに低いオーバーヘッド(メモリと速度)が見つかりました。典型的なコールスタックは、5〜10コールの深さで、1ダースの1〜2 kbファイルを動的にリンクするだけで、メガバイトを含めるよりも優れている場合があります。これは、ラッピングが必要な小さなutil関数を作成するだけで行われました。含まれる関数は、呼び出しスタックの上の関数内にネストされます。すべてのWebサービスの呼び出しに必要ではないが、PHPの組み込みの遅延読み込み機能を使用することもできた数百の関数で満たされたクラスとは対照的に考えてください。

1
ZhuLien

PHP 7を使用している場合は、以下を参照してください:この実装により、ネストされた関数について明確なアイデアが得られます。関数foo()にネストされた3つの関数(too()、boo()およびZoo())があるとします。 boo()とZoo()には同じ名前のネストされた関数xoo()があります。このコードでは、ネストされた関数のルールを明確にコメントアウトしています。

   function foo(){
        echo 'foo() is called'.'<br>';
        function too(){
            echo 'foo()->too() is called'.'<br>';
        }
        function boo(){
            echo 'foo()->boo() is called'.'<br>';
            function xoo(){
                echo 'foo()->boo()->xoo() is called'.'<br>';
            }
            function moo(){
                echo 'foo()->boo()->moo() is called'.'<br>';
            }
        }
        function Zoo(){
            echo 'foo()->Zoo() is called'.'<br>';
            function xoo(){     //same name as used in boo()->xoo();
                echo 'Zoo()->xoo() is called'.'<br>';
            }
        #we can use same name for nested function more than once 
        #but we can not call more than one of the parent function
        }
    }

/****************************************************************
 * TO CALL A INNER FUNCTION YOU MUST CALL OUTER FUNCTIONS FIRST *
 ****************************************************************/
    #xoo();//error: as we have to declare foo() first as xoo() is nested in foo()

    function test1(){
        echo '<b>test1:</b><br>';
        foo(); //call foo()
        too();
        boo();
        too(); // we can can a function twice
        moo(); // moo() can be called as we have already called boo() and foo()
        xoo(); // xoo() can be called as we have already called boo() and foo()
        #Zoo(); re-declaration error
        //we cannont call Zoo() because we have already called boo() and both of them have same named nested function xoo()
    }

    function test2(){
        echo '<b>test2:</b><br>';
        foo(); //call foo()
        too();
        #moo(); 
        //we can not call moo() as the parent function boo() is not yet called
        Zoo(); 
        xoo();
        #boo(); re-declaration error
        //we cannont call boo() because we have already called Zoo() and both of them have same named nested function xoo()

    }

Test1()を呼び出すと、出力は次のようになります。

test1:
foo() is called
foo()->too() is called
foo()->boo() is called
foo()->too() is called
foo()->boo()->moo() is called
foo()->boo()->xoo() is called

test2()を呼び出すと、出力は次のようになります。

test2:
foo() is called
foo()->too() is called
foo()->Zoo() is called
Zoo()->xoo() is called

ただし、再宣言エラーを回避するために、text1()とtest2()を同時に呼び出すことはできません。

0
princebillyGK

入れ子関数は、親関数内で宣言された変数を使用する場合に便利です。

<?php
ParentFunc();
function ParentFunc()
{
  $var = 5;
  function NestedFunc()
  {
    global $var;
    $var = $var + 5;
    return $var;
  };
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
  echo NestedFunc()."<br>";
}
?>
0
Matthew

私はこれが古い投稿であることを知っていますが、私はネストされた関数を使用して、ローカルでのみ機能が必要なときに再帰呼び出しにきちんと整頓されたアプローチを与えます-例えば。階層オブジェクトなどを構築する場合(明らかに、親関数は1回しか呼び出されないことに注意する必要があります):

function main() {
    // Some code

    function addChildren ($parentVar) {
        // Do something
        if ($needsGrandChildren) addChildren ($childVar);
    }
    addChildren ($mainVar); // This call must be below nested func

    // Some more code
}

例えば、JSと比較したphpの注意点は、ネストされた関数の呼び出しは、関数宣言の後に、つまり、関数宣言の後に行う必要があることです(関数呼び出しが親関数内のどこにでもある場合のJSと比較して)

0
user8333817

ネストされた関数はメモ化に役立ちます(関数の結果をキャッシュしてパフォーマンスを向上させます)。

<?php
function foo($arg1, $arg2) {
    $cacheKey = "foo($arg1, $arg2)";
    if (! getCachedValue($cacheKey)) {
        function _foo($arg1, $arg2) {
            // whatever
            return $result;
        }
        $result = _foo($arg1, $arg2);
        setCachedValue($cacheKey, $result);
    }
    return getCachedValue($cacheKey);
}
?>
0
Paul

私は、この特性を実際に使用したのは、プライマリのよりカテゴリ的な関数内で小さな再帰関数を実行するのが有用な場合だけでしたが、プライマリプロセスの動作の基本であったため、別のファイルに移動したくありませんでした。私はこれを行う他の「ベストプラクティス」方法があることを認識していますが、開発者がパーサーを見るたびにその機能を確認することを確認したいと思います。

0
MJHd