Внутренние функции и замыкания Closure в PHP ч.1

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

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

На самом деле, если вы понимаете, как они работают, они могут быть очень полезными, и без них ваш код может быть не таким изящным.

Когда вы соединяете их с анонимными функциями, добавленными в PHP 5.3.0, вы получаете что-то, что почти, но не совсем, то, что предлагают другие языки.

Но сначала давайте посмотрим на основы функций PHP.

Глобальные функции
Функция PHP проста для определения:

function MyFunction()
{
 instructions that make up the function
}

Вы можете включить параметры и инструкцию return.

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

Например, если мы определим функцию:

function MyFunction()
{
 echo("My Function");
}

Тогда ее можно использовать из класса:

class MyClass
{
 public function MyMethod()
 {
  MyFunction();
 }
}

и когда метод вызывается

$MyObject = new MyClass();
$MyObject->MyMethod();

MyFunction вызывается.

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

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

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

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

Внутренние функции

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

В PHP внутренние функции являются глобальными и, следовательно, ведут себя так же, как если бы они были объявлены вне любой содержащей функции.

Это означает, что внутренние функции бесполезны.

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

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

Таким образом, функции PHP исключают две особенности, которые делают возможным замыкание, и обе из-за того, что в PHP глобальные функции.

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

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

Но это не значит, что каждая функция имеет то же время жизни, что и программа, в которой они содержатся.

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

Например, вы можете условно объявить функцию:

if($test) {
 function MyFunction()
 {
  echo("My Function");
 }
}

Это приводит к тому, что MyFunction не существует в программе до тех пор, пока не будет выполнен оператор if и условие не станет истинным. Как только это произошло, функция существует и продолжает существовать до конца программы.

Вы можете думать об этом как, что «функция существует с того момента, как PHP начинает это интерпритировать».

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

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

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

Например, если вы определяете две функции, одна внутри другой:

function MyFunction()
{
 echo ('My Function');
 function MyInnerFunction()
 {
   echo('MyInnerFunction');
 }
}

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

MyFunction();
MyInnerFunction();

работает, но после вызова единожды MyFunction, вы не можете ее вызвать повторно, не генерируя ошибку: Fatal error: Cannot redeclare MyInnerFunction().

Решение проблемы попытки создания функции более одного раза заключается в использовании идеи условного объявления. Например:

function MyFunction()
{
 echo('My Function');
 if(!function_exists("MyInnerFunction")){
  function MyInnerFunction()
  {
   echo('MyInnerFunction');
  }
 }
}

То, как это работает, теперь должно быть очевидным. Оператор if просто проверяет, существует ли имя функции в глобальной области видимости. Если это так, то функция не объявляется во второй раз, и ошибки не возникает.

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

Теперь мы подошли к тонкому вопросу, который, оказывается, имеет практическое значение.

Можете ли вы вызвать внутреннюю функцию из функции, которая ее содержит?

Ответ — да, но только после того, как она была определена. Например; вы не можете использовать:

function MyFunction()
{
 echo("My Function");
 MyInnerFunction();
 if(!function_exists("MyInnerFunction")){
   function MyInnerFunction()
   {
    echo('MyInnerFunction');
   }
 }
}

Проблема в том, что когда вы пытаетесь вызвать MyInnerFunction, она еще не была объявлена. Однако, если вы переместите вызов после if, тогда все будет хорошо:

function MyFunction()
{
 echo("My Function");
 if(!function_exists("MyInnerFunction")){
   function MyInnerFunction()
   {
    echo('MyInnerFunction');
   }
 }
 MyInnerFunction();
}

Это странно и требует некоторого времени, чтобы привыкнуть, но это совершенно логично.

Конечно, во второй раз, когда MyFunction вызывается, внутренняя функция уже определена, и вы, в принципе, можете вызывать внутреннюю функцию где угодно, поскольку внутренняя функция действительно глобальная, где угодно!

Внутренние функции и замыкания Closure в PHP ч.2

Переведено с ресурса:  i-programmer.info/programming/php/1130-php-inner-functions-and-closure.html

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