Для чего нужны вложенные функции PHP? - PullRequest
73 голосов
/ 06 января 2009

В 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()

Ответы [ 12 ]

82 голосов
/ 06 января 2009

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

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

Единственный пример из "реального мира", который я могу выкопать, это это , который может быть запущен только один раз и может быть переписан для более чистого ИМО.

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

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

проверка.

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

81 голосов
/ 20 мая 2011

Если вы используете 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
9 голосов
/ 15 декабря 2013

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

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

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

<?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... ;)
        }
    }
}
7 голосов
/ 06 января 2009

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

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 (), чтобы вы не засоряли проверки языка во всем коде. Сейчас я этого не сделал, но вижу аргументы для этого.

2 голосов
/ 27 сентября 2015

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

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

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

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

2 голосов
/ 01 мая 2012

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

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

1 голос
/ 09 января 2014

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

0 голосов
/ 07 февраля 2019

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

0 голосов
/ 27 августа 2018

если вы находитесь в 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 (), чтобы избежать повторного объявления ошибки

0 голосов
/ 27 апреля 2018

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

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, где вызов функции может быть где угодно внутри родительской функции

...