88

This code produces the result as 56.

function x ($y) {
    function y ($z) {
        return ($z*2);
    }

    return($y+3);
}

$y = 4;
$y = x($y)*y($y);
echo $y;

Any idea what is going inside? I am confused.

Charles
  • 50,943
  • 13
  • 104
  • 142
Posto
  • 7,362
  • 7
  • 44
  • 61

9 Answers9

113

X returns (value +3), while Y returns (value*2)

Given a value of 4, this means (4+3) * (4*2) = 7 * 8 = 56.

Although functions are not limited in scope (which means that you can safely 'nest' function definitions), this particular example is prone to errors:

1) You can't call y() before calling x(), because function y() won't actually be defined until x() has executed once.

2) Calling x() twice will cause PHP to redeclare function y(), leading to a fatal error:

Fatal error: Cannot redeclare y()

The solution to both would be to split the code, so that both functions are declared independent of each other:

function x ($y) 
{
  return($y+3);
}

function y ($z)
{
  return ($z*2);
}

This is also a lot more readable.

Duroth
  • 6,315
  • 2
  • 19
  • 23
  • 63
    Another difference is that if you have function y inside function x, and you call function x twice, you will get an error for re-declaring function y. (If you really need to this, you need to surround function y with a check for if (function_exists('y')) { ... ) – Eugene M Oct 27 '09 at 15:27
  • 3
    @Eugene M: I never even knew that.. Thanks for pointing it out! (Just tested it myself, after reding your comment.) – Duroth Oct 28 '09 at 07:33
  • 4
    "which means that you can safely 'nest' function definitions, while still being able to call them anywhere in the file" -- that's a horrible language property. – Michael Apr 13 '15 at 11:24
  • 2
    Michael, you can use anonymous function, assign them to a variable and enjoy, it will be limited in scope. @Duroth, you can also avoid re-declaring errors when using anonymous functions. – Yevgeniy Afanasyev Jun 22 '15 at 00:27
  • So the scope of the function within the function is still global (within the namespace of the file it is defined in)? – Jason Jun 29 '17 at 11:48
34
(4+3)*(4*2) == 56

Note that PHP doesn't really support "nested functions", as in defined only in the scope of the parent function. All functions are defined globally. See the docs.

Lukáš Lalinský
  • 40,587
  • 6
  • 104
  • 126
20

Not sure what the author of that code wanted to achieve. Definining a function inside another function does NOT mean that the inner function is only visible inside the outer function. After calling x() the first time, the y() function will be in global scope as well.

Anti Veeranna
  • 11,485
  • 4
  • 42
  • 63
  • 3
    Also, definining y() inside x() means that x() can only be called once. You will get the 'function already defined' fatal error when calling x() the second time. – Anti Veeranna Oct 27 '09 at 16:08
8

This is useful concept for recursion without static properties , reference etc:

function getRecursiveItems($id){
    $allItems = array();
    
    function getItems($parent_id){
       return DB::findAll()->where('`parent_id` = $parent_id');
    } 
   
    foreach(getItems($id) as $item){
         $allItems = array_merge($allItems, getItems($item->id) );
    }

    return $allItems;
}
geisterfurz007
  • 5,292
  • 5
  • 33
  • 54
d.raev
  • 9,216
  • 8
  • 58
  • 79
  • Explain? Looks recursive but I don't see it. Is "getReqursiveItems" inside of getItems()? – Josiah Feb 26 '15 at 18:50
  • 1
    @Josiah not sure what my original idea was here .. but I edited the answer to show a possible usecase. – d.raev Feb 27 '15 at 12:00
  • 4
    this doesn't look to be recursive at all, but rather it returns all the *grandchildren* of the original `$id` – KnightHawk Nov 06 '15 at 21:32
  • If you replace first `getItems` in `foreach` with `getItemms` and second `getItems` with `getReqursiveItems` you will get recursive function. – przemo_li Nov 05 '18 at 16:35
  • [Recursive nested function](https://stackoverflow.com/a/75870226/6243352) without polluting the global scope. – ggorlen Mar 28 '23 at 19:37
5

It is possible to define a function from inside another function. the inner function does not exist until the outer function gets executed.

echo function_exists("y") ? 'y is defined\n' : 'y is not defined \n';
$x=x(2);
echo function_exists("y") ? 'y is defined\n' : 'y is not defined \n';

Output

y is not defined

y is defined

Simple thing you can not call function y before executed x

Nanhe Kumar
  • 15,498
  • 5
  • 79
  • 71
3

Your query is doing 7 * 8

x(4) = 4+3 = 7 and y(4) = 4*2 = 8

what happens is when function x is called it creates function y, it does not run it.

Mike Valstar
  • 3,499
  • 5
  • 24
  • 32
  • 1
    Just wanted to add that this isn't really good practice. You will also notice that if you use $y = y($y)*x($y); that the code will die because x hasn't been created. Coding like this will just lead to errors and confusion. – Michael Mior Oct 27 '09 at 15:26
  • yes thats true; personally I have only ever used this once in practice to case in for different styles of magic quotes. although that was in an if statement instead of a function () – Mike Valstar Oct 27 '09 at 15:28
3

function inside a function or so called nested functions are very usable if you need to do some recursion processes such as looping true multiple layer of array or a file tree without multiple loops or sometimes i use it to avoid creating classes for small jobs which require dividing and isolating functionality among multiple functions. but before you go for nested functions you have to understand that

  1. child function will not be available unless the main function is executed
  2. Once main function got executed the child functions will be globally available to access
  3. if you need to call the main function twice it will try to re define the child function and this will throw a fatal error

so is this mean you cant use nested functions? No, you can with the below workarounds

first method is to block the child function being re declaring into global function stack by using conditional block with function exists, this will prevent the function being declared multiple times into global function stack.

function myfunc($a,$b=5){
    if(!function_exists("child")){
        function child($x,$c){
            return $c+$x;   
        }
    }
    
    try{
        return child($a,$b);
    }catch(Exception $e){
        throw $e;
    }
    
}

//once you have invoke the main function you will be able to call the child function
echo myfunc(10,20)+child(10,10);

and the second method will be limiting the function scope of child to local instead of global, to do that you have to define the function as a Anonymous function and assign it to a local variable, then the function will only be available in local scope and will re declared and invokes every time you call the main function.

function myfunc($a,$b=5){
    $child = function ($x,$c){
        return $c+$x;   
    };
    
    try{
        return $child($a,$b);
    }catch(Exception $e){
        throw $e;
    }
    
}

echo myfunc(10,20);

remember the child will not be available outside the main function or global function stack

Aylian Craspa
  • 422
  • 5
  • 11
2

After using second time function with nested we get "redeclare" error. But.. We can use this like that:

function x( $some_value ){

$nested_function = function($x){ return $x*2; };

return $nested_function( $some_value );

}
chainable
  • 99
  • 1
  • 3
1

Lukas is correct, they are globally defined even when wrapped in a method, thus doesn't make much sense:

<?php

class C {

  function parent() {
    function child($a,$b) {
      return $a * $b;
    }
    return child(3,4);
  }
}

$O = new C;

echo $O->parent();
echo ',';
//this unfortunately works..
echo child(3,4);  
//now throw a redeclare error...
echo $O->parent();
Teson
  • 6,644
  • 8
  • 46
  • 69