Здравствуй, дорогой читатель. В данной заметке я хотел бы рассказать вам об одной особенности, описанной в документации, но не особо используемой или не нашедшей своё место в современном PHP-мире.


Объявление функций внутри других функций


Допустим, вы решили определить одну функцию, которая будет регистрировать другие функции.
Такое поведение не совсем типично, но, возможно, имеет свои преимущества и способы применения.
function init() {
    function finish($resource) {
         // некоторая работа по завершению всех операций со связанным ресурсом
    }
    $resource = null;
    // некоторая работа, возможно создание ресурсов
    return $resource; // возвращение какого-то результата
}
$resource = init();
finish($resource);


Объявление функций внутри методов


Определяете вы класс, методы, тела методов, и в один прекрасный момент вы, прочитав stackoverflow или reddit, и попробовав запустить это на 3v4l.org, осознаёте, что возможность, которую PHP интерпретатор предоставляет, является с одной стороны очень спорной, а с другой дает возможность определять какую-либо функциональность только по запросу.

class test {
	function register() {
		if (!function_exists('test_init')) {
			function test_init() {
				echo 'Initializing library'.PHP_EOL;
			}
		}
	}
}

$test = new test;
$test->register();
test_init();


Этот код успешно выполняется на PHP 4.x (>=4.3.10), PHP 5 (>=5.0.5), PHP 7 и HHVM.
Видимо, такая функциональность кому-то была нужна, и ещё в 2005-м году был создан баг, описывающий данную функциональность.

P.S: В 2015-м году использовать глобальные функции уже совсем необязательно, а уж тем более определять вложенные, но эта возможность жива и скорее всего не скоро будет удалена из языка.

Комментарии (11)


  1. maximw
    10.06.2015 23:26

    Небольшая ошибка в первом куске кода.

    Fatal error: Cannot redeclare end() in .../test.php on line 3


    1. wapmorgan Автор
      10.06.2015 23:34

      Исправил на другое имя, т.к. не проверял код. Спасибо.


  1. Temirkhan
    10.06.2015 23:42
    +4

    Объявлять функции внутри других функций, насколько я помню, плохая практика.


    1. kaichou
      11.06.2015 09:51
      +3

      Расскажите это программистам JS.


      1. andrewnester
        11.06.2015 09:57
        +18

        В JS видимость таких функций ограничена «родительской» функцией, а в php такая функция становится глобальной. Это большая разница


      1. dmitrybarabash
        11.06.2015 13:10

        И паскалистам.


      1. Andre_487
        13.06.2015 01:40

        У JS и PHP активный обмен опытом: в PHP объявляют вложенные функции, в React пишут HTML среди кода


  1. andrewnester
    10.06.2015 23:52
    +2

    То, что вы описали, часто используется в компонентах Joomla например
    Компонент должен работать и в 1.5 и в 2.5, но в 2.5 удалили функцию, которая была в 1.5 или ее переименовали, и она используется в этом компоненте
    Для этого и пишутся там такие хаки, которые по требованию создают нужную функцию
    Ничего в этом хорошего нет, но это ведь legacy code :)


  1. tzlom
    11.06.2015 00:02
    +6

    Подобный код часто встречается в библиотеках мечтающих о mbstring, в автолоадере проверяется наличие нужных mb_* функций и если что-то отсутствует — объявляется свой костылёк


  1. Apfel
    11.06.2015 15:06

    Должен покаяться: применял такую практику для array_map, usort и т.д., когда еще не было анонимных функций.
    После их введения о таком забыл.

    P.S.
    Речь только о РНР.


  1. ilyaplot
    11.06.2015 21:33

    Как каз сегодня пришлось писать curl_reset внутри функции класса что бы заработал сторонний компонент. Что удобно, функция создается только в том случае, если не была определена. Так что функционал должен присутствовать.