Для чего нужны вложенные функции 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()

11 ответов


в основном нет, я всегда рассматривал это как побочный эффект парсера.

Эран Гальперин ошибается, что эти функции как-то частные, они просто необъявленные, пока outer() запускается. Они также не являются частными, они загрязняют глобальный охват, хотя и с задержкой. И в качестве обратного вызова внешний обратный вызов все еще может быть вызван только один раз. Я все еще не вижу, как это полезно, применяя его к массиву, который, скорее всего, вызывает псевдоним больше, чем однажды.

единственный пример "реального мира", который я мог выкопать, это этой который может работать только один раз и может быть переписан cleaner IMO.

единственное использование, которое я могу придумать, - это для модулей вызвать метод [name]_include, который устанавливает несколько вложенных методов в глобальном пространстве в сочетании с

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

проверка.

ООП PHP, очевидно, будет лучшим выбором :)


Если вы используете PHP 5.3, вы можете получить больше Javacript-подобного поведения с анонимной функцией:

<?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

[переписано в соответствии с комментарием @PierredeLESPINAY.]

Это не просто побочный эффект на всех, но на самом деле очень полезная функция для динамическое изменение логика вашей программы. Это из процедурных дней PHP, но может пригодиться и с архитектурами OO, если вы хотите предоставить альтернативные реализации для определенных автономных функций самым простым способом. (В то время как OO является лучшим выбором время, это вариант, а не мандат, и некоторые простые задачи не нуждаются в дополнительном cruft.)

например, если вы динамически/условно загружаете плагины из своей структуры и хотите сделать жизнь авторов плагинов очень простой, вы можете предоставить реализации по умолчанию для некоторых важных функций, которые плагин не переопределил:

<?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... ;)
        }
    }
}

функции, определенные в функциях, которые я не вижу большой пользы, но условно определенные функции я могу. Например:

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

и тогда все, что нужно сделать вашему коду, это использовать функцию " cmp " в таких вещах, как вызовы usort (), чтобы вы не засоряли языковые проверки по всему вашему коду. Теперь я этого не сделал, но я вижу аргументы для этого.


весь мой php-OO, но я вижу использование вложенных функций, особенно когда ваша функция рекурсивна и не обязательно является объектом. То есть он не вызывается вне функции, в которую он вложен, но является рекурсивным и впоследствии должен быть функцией.

мало смысла в создании нового метода для экспресс-использования одного другого метода. Для меня это неуклюжий код и вид-не смысл ОО. Если вы не собираетесь вызывать эту функцию нигде, гнездо это.


все вышесказанное, можно просто создать вложенную функцию для замены некоторого локализованного повторяющегося кода внутри функции (который будет использоваться только внутри родительской функции). Идеальным примером этого является анонимная функция.

некоторые могут сказать, что просто создают частные методы (или меньшие блоки кода) в классе, но это мутит воду, когда ультраспецифическая задача (которая является исключительной для родителя) должна быть модульной, но не обязательно доступной для остальная часть класса. Хорошей новостью является то, что если окажется, что вам нужна эта функция где-то еще, исправление довольно элементарно (переместите определение в более центральное место).

вообще говоря, использование JavaScript в качестве стандарта для оценки других языков программирования на основе C-плохая идея. JavaScript определенно является собственным животным по сравнению с PHP, Python, Perl, C, C++ и Java. Конечно, есть много общего сходства, но мелкие, мелкие детали (ссылка JavaScript: окончательное руководство, 6-е издание, главы 1-12), если обратить внимание, сделать основной JavaScript уникальным, красивым, разным, простым и сложным одновременно. Это мои два цента.

чтобы быть ясным, я не говорю, что вложенные функции являются частными. Просто это вложенность может помочь избежать беспорядка, когда что-то тривиальное должно быть модулировано (и требуется только родительской функцией).


в вызове webservice мы обнаружили гораздо более низкие накладные расходы (память и скорость) динамически, включая вложенные функции над библиотеками, полными 1000 функций. Типичный стек вызовов может быть между 5-10 глубокими вызовами, требующими динамического связывания дюжины файлов 1-2kb, был лучше, чем включение мегабайт. Это было сделано просто путем создания небольшой функции util, которую требует обертывание. Включенные функции становятся вложенными в функции над стеком вызовов. Рассмотрим это в отличие от классов, полных 100 функций, которые не требовались при каждом вызове webservice, но также могли использовать встроенные функции ленивой загрузки php.


вложенные функции полезны в Memoization (результаты функции кэширования для повышения производительности).

<?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);
}
?>

вложенные функции полезны, если вы хотите, чтобы вложенная функция использовала переменную, объявленную в родительской функции.

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

Я знаю, что это старый пост, но fwiw я использую вложенные функции, чтобы дать аккуратный и аккуратный подход к рекурсивному вызову, когда мне нужна только локальная функциональность-например, для построения иерархических объектов и т. д. (Очевидно, вам нужно быть осторожным, родительская функция вызывается только один раз):

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
}

замечание в php по сравнению с JS, например, заключается в том, что вызов вложенной функции должен быть сделан после, т. е. ниже, объявления функции (по сравнению с JS, где вызов функции может быть в любом месте родительской функции


если вы находитесь в php 7, то смотрите это: Эта реализация даст вам четкое представление о вложенной функции. Предположим, что у нас есть три функции(too (), boo() и zoo ()), вложенные в функцию foo(). 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 (), чтобы избежать ошибки повторного объявления