Переменные функции в PHP

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

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

<?php
function plagin_a()
{
 return 'Вызван плагин А';
}
 
function plagin_b()
{
 return 'Вызван плагин B';
}
 
$var = "plagin_b";
 
// данная строка вызовет функцию plagin_b()
$text = $var();
 
// легко догадаться, какое значение будет у переменной $text
print($text);
?>

 

Как видите, мы нигде не описывали никакой функции, имя которой бы было $var(). Однако, интерпретатор PHP всё равно «найдёт» такую функцию и исполнит её. Ведь значением переменной $var у нас является строка символов: «plagin_b». Главное, чтобы где-то в сценарии интерпретатор нашёл функцию хотя бы с таким названием.

Примечание. Могу сказать, что мой пример высосан из пальца и, возможно, чрезмерно краток. Но данный пример приведён для наглядности и потому столь примитивен. Теперь можно приступать к более сложным примерам. По приведённой выше ссылке, в общем-то, предоставлена вся справочная информация, поэтому сложностей у меня не возникало. И ни у кого не возникнет, думаю.

Как показать использование на практике переменных функций? Для реализации обратных вызовов, таблиц функций. По крайней мере такой список приводит документация. Мой пример является своеобразной разновидностью таблиц функций. Я столкнулся с необходимостью подключать к основному скрипту произвольные файлы, внутри которых заранее неизвестный код и функции с заранее неизвестными именами. Как тогда эти функции вызывать?

Примечание. Напомню, кратко, что делает Кафедра. Она позволяет создать по шаблону неограниченное количество упражнений (или целых контрольных работ), где числа в формулировках не будут повторяться, а значит варианты будут разными и невозможно будет списать ответ. Изначально в проекте требовалось, чтобы любой желающий мог добавить в проект своё упражнение без какой-либо переделки исходного кода программы. Чтобы написание не требовало изучение остальных частей программы. Ну а если кто-то просто получил новое упражнение по почте? Нужно иметь возможность не очень сложно добавить её в программу. По этой причине каждое упражнение со своим кодом и шаблоном стали делать в форме расширений или плагинов и размещать в отдельном файле. Скопировал новый файл в каталог с программой — в программе появилась новая задача.

Как это реализовано в коде? В каталоге с программой есть подкаталог с этими самыми упражнениями. Одно упражнение — один php-файл (в общем случае). Каждый php-файл с упражнением — это маленький «чёрный ящик», который работает независимо от других и от самой программы. Внутри него, как правило, одна-единственная функция, которую можно бесконечное число раз вызывать и она как раз будет возвращать очередной экземпляр упражнения. Вот только как эта функция называется? Заранее это никогда неизвестно.

Есть минимум два подхода к решению этой проблемы:

  • помещать каждый плагин/тему/упражнение в отдельный подкаталог с уникальным именем, а внутри него расположить php-файл с кодом, ну и какой-нибудь текстовый файл со стандартным именем типа about.conf. В тексте этого файла перечислить с каждой новой строчки все имена функций, которые имеются в данном расширении.
  • принять соглашение, что имя неизвестной функции совпадает с именем файла, внутри которого она описана. Ну а прочитать имя файла в каталоге — это школьная тема.

Мне больше понравился второй вариант — это позволяет совместить конфигурационный и исполняемый файлы в одном. Так и получилось, что имя функции (или главной функции, если их несколько) совпадает с именем файла, в котором она описана. Поэтому нам всего-то нужно завести переменную, хранящую имя файла, потом добавить к ней скобки и… готово. Для понимания кода, написанного ниже, поясню, что я называю все задачи и упражнения одним словом — «этюд». Итак:

// листинг (*)
$find_files = glob("path/root/dir/*");
 
foreach ($find_files as $key =&gt; $value)
{
  $etude = basename($value, '.php');
  $text = $etude();
  print($text);
}

В свою очередь в сканируемом каталоге расположены файлы file_name_A.php и file_name_B.php такого содержания:

// листинг (**)
function file_name_A()
{
  /* данная функция предназначена для выполнения действия N и условно называется A.
  Обращу внимание, что имя функции file_name_A() полностью (кроме расширения) совпадает с именем самого файла - file_name_A.php */
 return 'Функция выполняет N и называется A';
}

Или такого:

// листинг (***)
function file_name_B()
{
  /* данная функция предназначена для выполнения действия N_2 и условно называется B
  Снова обращу внимание, что имя функции совпадает с именем файла!*/
 include('test.php');
 return 'Функция выполняет N_2 и называется B';
}

Тогда код из листинга (*) выведет на экран результат выполнения кода из листинга (**) и (***). Удобно? Безусловно. Ничто не мешает создавать структуры с более сложной иерархией, когда одно из расширений зависит от другого, но это уже вопрос к архитекторам приложения.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*