PHP Inner Functions And Closure
Written by Mike James   
Friday, 07 January 2022
Article Index
PHP Inner Functions And Closure
Lifetimes
Object-oriented PHP
Anonymous functions can be local

 

Banner

Objects and inner functions

There is an interaction between object oriented programming and inner functions that we need to be aware of.

It is the case that things tend to go a little wrong or strange where the non-object oriented facilities in a language meet the grafted on object oriented facilities. In the case of PHP and inner functions the logic all still works but it results in behaviour you might not expect.

Consider an inner function defined within the method of a class.

class MyClass
{
 public $MyProperty;
 public function MyMethod()
 {
  if(!function_exists("MyInnerFunction")){
   function MyInnerFunction()
   {
    echo('MyInnerFunction ');
   }
  }
 }
}

 

In this case the inner function is once again a global function. It certainly isn't a method of MyClass and you can't call it using object notation:

$MyObject->MyInnerMethod();

To create the global function you have to call the method and then the function it created:

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

The inner function knows nothing of its origin within a class or the instance that created it - and this can be a problem.

For example, the inner function does not have access to $this - it simply isn't part of the class. If you try:

class MyClass
{
 public $MyProperty="Default Value";
 public function MyMethod()
 {
  echo($this->MyProperty);
  if(!function_exists("MyInnerFunction")){
   function MyInnerFunction()
   {
    echo($this->MyProperty);
    echo('MyInnerFunction ');
   }
  }
 }
}

You will discover that an error is generated by the inner function

Using $this when not in object context

The standard solution for this sort of problem is to create a new variable - usually called $that - and explicitly transfer in the $this reference:

function MyInnerFunction($that)
{
 echo($that->MyProperty);
 echo('MyInnerFunction ');
}

and arrange to call MyInnerFunction as:

MyInnerFunction($this);

when ever the call is in an object context. This still doesn't quite work because the inner function isn't part of the class - its global - and hence the $that reference only gives you access to public methods and properties. That is $that can't give you access to private or protected members of the class - but in many cases access to public methods is sufficient.

Using inner functions

At this point you understand inner functions but might well be wondering what use they could possibly have.

The answer is that many development frameworks - Joomla for example - divide up the task of creating extensions or simply using the framework into writing methods.

So the documentation often says - create some code in a file called template.php and place it in a particular directory. What the framework then does is to include the file within a class definition. For example:

class HTMLview{
 public function htmlview(){
  include "template.php"
 }
}

In other words by specifying that the code has to be in a particular file the framework has simplified the task to writing template code not the more complex idea of building a class.

A sure sign that this is happening is the instruction to use $this within the template code.

 

Banner

 

This is a good approach as long as the amount of code is small. If you have a lot of code to add to a framework in this way then there is a problem in that you want to divide it up into separate functions. You can't because you are already defining the code within a function and so declaring another function isn't allowed - expect of course it is. You can create an inner function.

In fact any function you define in the include file is necessarily an inner function. 

From the point of view of the programmer working on the module to be included in the framework there might well be no understanding that they are working within an enclosing function and this can result in some strange and inexplicable behaviour.

Of course if you dont' spot this then all sorts of things go wrong and many programmers conclude that they can't define functions within such include files. A fact that you can confirm if you scan though comments included in the comments of many frameworks which say something like

"I have to repeat this code because I can't define a function"

Well you can define a function to avoid repeating code as long as you follow some simple rules. The rules are:

  1. define the function conditionally as shown above to make sure that it is only created once.
  2. define any functions that you want to use at the start of the file so that they can be called from the rest of the code.
  3. you can't use $this but you can define a $that variable and pass it to your function
  4. you still can't access protected or private members using $that

In some cases the restriction to only using public members is a problem but usually it isn't. Equally if you also know that the method will only ever be executed once then you can also drop the conditional declaration and simply define the function in the usual way.

Also if you do this then the functions will be defined as the include file is read in and you can define them anywhere - i.e. they don't have to be at the start of the file.

In short - you can use inner functions to divide up a method that you are writing as part of an include file even if the file is includes within another function or within a method.



Last Updated ( Friday, 07 January 2022 )