91

In JavaScript nested functions are very useful: closures, private methods and what have you..

What are nested PHP functions for? Does anyone use them and what for?

Here's a small investigation I did

<?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()
Kijewski
  • 25,517
  • 12
  • 101
  • 143
meouw
  • 41,754
  • 10
  • 52
  • 69
  • 1
    I could've sworn I read that support for this was being dropped in PHP6 but I can't find it anywhere. – Greg Jan 06 '09 at 10:18
  • 2
    @greg I thought the whole plan for PHP6 was in up in the air anyway? – James Feb 10 '11 at 22:40
  • They're great for large functions -- sorta recursive organization – JVE999 May 27 '14 at 02:08
  • You've got closures in PHP too, no sweat. – Gralgrathor Mar 29 '16 at 14:06
  • I have uses for nested functions, but I'm unclear whether nested functions are actually implemented in PHP 7 and 8. When I try to use them, I can only get them defined when they are in bottom-up lexical order, not when in random order as expected and as implemented at the outermost (file) level. – David Spector Jan 15 '23 at 19:28

12 Answers12

97

If you are using PHP 5.3 you can get more JavaScript-like behaviour with an anonymous function:

<?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
?>

Output:

test
test
Gus Hogg-Blake
  • 2,393
  • 2
  • 21
  • 31
  • 12
    +1 for actually answering a (basically) functional topic with a functional answer, and not OOP – Peter Host Oct 22 '12 at 19:28
  • Great answer, but doesn't work recursively because `$inner` is undefined inside itself. That's important to me since I mostly use inner functions when I want to write a recursive helper but prefer to access the enclosed scope to avoid parameters and keep the recursive helper from being called directly. See [this answer](https://stackoverflow.com/a/75870226/6243352) if you plan to use recursion. – ggorlen Mar 28 '23 at 19:19
91

There is none basically. I've always treated this as a side effect of the parser.

Eran Galperin is mistaken in thinking that these functions are somehow private. They are simply undeclared until outer() is run. They are also not privately scoped; they do pollute the global scope, albeit delayed. And as a callback, the outer callback could still only be called once. I still don't see how it's helpful to apply it on an array, which very likely calls the alias more than once.

The only 'real world' example I could dig up is this, which can only run once, and could be rewritten cleaner, IMO.

The only use I can think of, is for modules to call a [name]_include method, which sets several nested methods in the global space, combined with

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

checks.

PHP's OOP would obviously be a better choice :)

SherylHohman
  • 16,580
  • 17
  • 88
  • 94
Martijn Laarman
  • 13,476
  • 44
  • 63
  • 9
    Yeah, really. That's brutally bad. – Tony Arkles Sep 29 '09 at 17:06
  • same as `def` declarations in Ruby – user102008 Aug 12 '11 at 07:01
  • 2
    Even though they are not exactly private functions, they still can NOT be called UNLESS the outer function is called, so this gives them a sort of dependency as a function to be ran "in conjunction" with the outer function... – techexpert Jun 13 '12 at 02:23
  • 2
    Without this behavior autoload wouldn't work. If declarations inside a function are somehow private, then the include/require performed by your autoload handler would end up doing nothing. – cleong Aug 14 '12 at 11:00
  • I am not fluent in PHP, but it occurs to me this might be a way to create 'private variables'? The outer function could declare some variables which the inner function(s) could then use/modify without polluting the global scope? Correct me if I'm wrong. – Stijn de Witt Feb 11 '14 at 14:38
  • Its also to realize that PHP does not 'hoisting'. The function is available only _after_ it is defined. – theking2 Feb 08 '23 at 15:52
10

[Rewritten according to the comment by @PierredeLESPINAY.]

It's not just a side-effect at all, but actually a very useful feature for dynamically modifying the logic of your program. It's from the procedural PHP days, but can come in handy with OO architectures too, if you want to provide alternative implementations for certain standalone functions in the most straightforward way possible. (While OO is the better choice most of the time, it's an option, not a mandate, and some simple tasks don't need the extra cruft.)

For example, if you dynamically/conditionally load plugins from your framework, and want to make the life of the plugin authors super easy, you can provide default implementations for some critical functions the plugin didn't override:

<?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... ;)
        }
    }
}
Sz.
  • 3,342
  • 1
  • 30
  • 43
  • 2
    However, according to the OP, the nested function scope seems not limited to the container function... – Pierre de LESPINAY Oct 26 '16 at 08:21
  • 1
    @PierredeLESPINAY: Oops, very true, thanks a lot for pointing out! :-o I updated (rewritten) the answer accordingly. (I.e. it's still a very handy feature, but for a completely different reason then.) – Sz. Nov 21 '16 at 18:41
8

Functions defined within functions I can't see much use for but conditionally defined functions I can. For example:

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

And then all your code needs to do is use the 'cmp' function in things like usort() calls so you don't litter language checks all over your code. Now I haven't done this but I can see arguments for doing it.

cletus
  • 616,129
  • 168
  • 910
  • 942
7

All the above being said, one might simply create a nested function to replace some localized, repetitive code within a function (that will only be used inside the parent function). An anonymous function is a perfect example of this.

Some might say just create private methods (or smaller code blocks) in a class, but that is muddying the waters when an ultra-specific task (which is exclusive to the parent) needs to be modularized, but not necessarily available to the rest of a class. The good news is if it turns out that you do need that function somewhere else, the fix is rather elementary (move the definition to a more central location).

Generally speaking, using JavaScript as the standard by which to evaluate other C based programming languages is a bad idea. JavaScript is definitely its own animal when compared to PHP, Python, Perl, C, C++, and Java. Of course, there are lots of general similarities, but the nitty, gritty details (reference JavaScript: The Definitive Guide, 6th Edition, Chapters 1-12), when paid attention to, make core JavaScript unique, beautiful, different, simple, and complex all at the same time. That's my two cents.

Just to be clear, I'm not saying nested functions are private. Just that nesting can help avoid clutter when something trivial needs to be modularized (and is only needed by the parent function).

Anthony Rutledge
  • 6,980
  • 2
  • 39
  • 44
2

All of my php is OO, but I do see a use for nested functions, particularly when your function is recursive and not necessarily an object. That is to say, it does not get called outside of the function it is nested in, but is recursive and subsequently needs to be a function.

There's little point in making a new method for the express use of a single other method. To me that's clumsy code and sort-of not the point of OO. If you're never going to call that function anywhere else, nest it.

Jesse James Richard
  • 473
  • 1
  • 6
  • 20
  • 1
    You're pretty much on the money, but I think a better example would be when declaring callback functions for array_filter(), array_map(), preg_replace_callback(), uasort(), and the like. I use these functions with a fair amount of frequency, and rarely do I need the callback I am declaring outside the OOP method I am calling it from, so it feels a lot cleaner to avoid polluting the global or even class namespace with the callback function. And I can finally do that with PHP 5.3 (as explained in user614643's answer)! – Derek Jul 20 '13 at 14:53
1

In webservice calling we found it a much lower overhead (memory and speed) dynamically including in a nested fashion, individual functions over libraries full of 1000s of functions. The typical call stack might be between 5-10 calls deep only requiring linking a dozen 1-2kb files dynamically was better than including megabytes. This was done just by creating a small util function wrapping requires. The included functions become nested within the functions above the call stack. Consider it in contrast to classes full of 100s of functions that weren't required upon every webservice call but could also have used the inbuilt lazy loading features of php.

ZhuLien
  • 11
  • 1
1

if you are in php 7 then see this: This implementation will give you a clear idea about nested function. Suppose we have three functions(too(), boo() and zoo()) nested in function foo(). boo() and zoo() have same named nested function xoo(). Now in this code I have commented out the rules of nested functions clearly.

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

    }

Now if we call test1() the output will be this:

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

if we call test2() the output will be this:

test2:
foo() is called
foo()->too() is called
foo()->zoo() is called
zoo()->xoo() is called

But we cannot call both text1() and test2() at same time to avoid re-declaration error

princebillyGK
  • 2,917
  • 1
  • 26
  • 20
  • 2
    This would be easier to read and digest if the function names reflected some unique characteristic of each function, rather than arbitrary, rhyming, similar looking names. It is confusing and difficult to keep track of. Choosing names that help us keep track of where each is located would make this user friendly and reduce the cognitive load required to read and understand. I do not have the time, or will of punishment, to make it through this post, though you probably have a great point hidden in there, I suspect few will stick around to excavate it. Reading SO is not a research project. KISS – SherylHohman Jul 08 '20 at 17:18
  • Note that this is not function _nesting_, but declaring global functions inside another global function. You could also declare all functions in the global namespace and wrap them inside conditions. – Philipp Aug 23 '22 at 13:56
1

For those that suggest that there is no practical use of nested functions. Yes they have use and this is an example.

Imagine that I have a file called my_file.php which is used to get an ajax result out of. But what if there are times that you don't want to get the result through ajax but you want to include it twice in the same page without conflicts?

Lets say ajax file my_file.php :

<?php
// my_file.php used for ajax

$ajax_json_in = 10; 

function calculations($a, $b)
{   $result = $a + $b;
    return $result;
}

$new_result = $ajax_json_in * calculations(1, 2);

$ajax_json_out =  $new_result; 
   
?>

Below example includes the above file twice without conflict. You may not want to ajax call it, because there are cases that you need to include it straight in the HTML.

<?php
// include the above file my_file.php instead of ajaxing it

function result1 
{
    $ajax_json_in = 20; 
    include("my_file.php");
    return $ajax_json_out; 
}


function result2 
{
    $ajax_json_in = 20; 
    include("my_file.php");
    return $ajax_json_out;
}

?>

Including the file makes the file's functions nested. The file is used both for ajax calls and inline includes !!!

So there is use in real life of nested functions.

Have a nice day.

Thanasis
  • 329
  • 4
  • 8
0

I know this is an old post but fwiw I use nested functions to give a neat and tidy approach to a recursive call when I only need the functionality locally - e.g. for building hierarchical objects etc (obviously you need to be careful the parent function is only called once):

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
}

A point of note in php compared with JS for instance is that the call to the nested function needs to be made after, i.e. below, the function declaration (compared with JS where the function call can be anywhere within the parent function

0

I have only really used this characteristic when it was useful to execute a small recursive function inside a primary, more categorical function, but didn't want to move it to a different file because it was fundamental to the behavior of a primary process. I realize there are other "best practice" ways of doing this, but I want to make sure my devs see that function every time they look at my parser, it's likely what they should modify anyway...

MJHd
  • 166
  • 1
  • 10
0

Building on this answer, you can make the inner function recursive with a use clause:

<?php
function outer() {
    $inner = function ($i = 0) use (&$inner) {
        if ($i < 10) {
            echo "test $i\n";
            $inner($i + 1);
        }
    };

    $inner();
}

outer();
outer();

?>

If you have other variables to bring into the closure, add them as well:

<?php
function outer($count) {
    $inner = function ($i = 0) use (&$inner, $count) {
        if ($i < $count) {
            echo "test $i\n";
            $inner($i + 1);
        }
    };

    $inner();
}

outer(3);
outer(5);

?>

As with the parent answer, this pattern ensures the inner function doesn't leak out to the global scope.

ggorlen
  • 44,755
  • 7
  • 76
  • 106