Search code examples
phpclassanonymous-class

Access outer variables from anonymous class


I was trying an alternative way of doing this :

public function index()
{
    $faker = Faker\Factory::create('fr_FR');

    $ideas = [];

    for ($i = 1; $i <= rand(10, 50); $i++) {
        $idea = new \stdClass;
        $idea->id = $i;
        $idea->author = $faker->name;
        //...

        $ideas[] = $idea;
    }
}

Instead of creating object and assigning attributes in the loop, I would like to create the object from a class, and populate $ideas[] with the array_pad() function :

public function index()
{
    $faker = Faker\Factory::create('fr_FR');

    $ideas = [];

    $idea = new class {
        private $id;
        private $author;

        function __construct() {
            $this->id = count($ideas) + 1;
            $this->author = $faker->name;
        }
    };

    array_pad($ideas, rand(10, 50), new $idea);
        
}

So I need to access $faker and $ideas from the anonymous class. I tried to pass them to the class like this :

$idea = new class($ideas, $faker) {

    private $id;
    private $author;

    private $ideas
    private $faker

    function __construct($ideas, $faker) {
        $this->id = count($ideas) + 1;
        $this->author = $faker->name;
    }
};

But I get a

Too few arguments to function class@anonymous::__construct(), 0 passed


Solution

  • Sad news: you cant use array_pad for this.

    Here the fixes you need to apply to get rid of the error:

    // array_pad($ideas, rand(10, 50), new $idea);
    array_pad($ideas, rand(10, 50), $idea); // remove new
    

    Since you did the new already here:

    $idea = new class($ideas, $faker) {
    

    Even though this will fill $ideas. It will store the same reference to your $idea over and over. Which means if you alter one element this change will be on all elements (i guess this is not desired).

    In order to get this working you will have to use a loop, which creates a new $idea for every entry:

    $faker = Faker\Factory::create('fr_FR');
    
    $ideas = [];
    
    for ($i = rand(10, 50); $i > 0; $i--) {
        $ideas[] = new class($ideas, $faker) {
            private $id;
            private $author;
    
            function __construct($ideas, $faker) {
                $this->id = count($ideas) + 1;
                $this->author = $faker->name;
            }
        };
    }
    

    Working example.

    Additional information

    Instead of doing this

    for ($i = 1; $i <= rand(10, 50); $i++)

    better do this

    for ($i = rand(10, 50); $i > 0; $i--)

    The reason is the comparison is getting called on every loop, so you generating a new random number on every loop. Example

    This is problematic because you tend to get way more low numbers like this. For example to get 50 loops the random has to return > $i every time - which is very unlikely.

    One more thing: array_pad returns the filled array, so you'd have to write

    $ideas = array_pad(...