phpdependency-injectionlambdapimple

understanding pimple php source code


Pimple is a simple dependency injection container in php used in silex framework. I was going through the source code here. In the documentation the function offsetGet returns the same instance of the class that is attached to the dependency container. the relevant code for offsetGet is :

public function offsetGet($id)
{
    if (!isset($this->keys[$id])) {
        throw new InvalidArgumentException(sprintf('Identifier "%s" is not defined.', $id));
    }

    if (
        isset($this->raw[$id])
        || !is_object($this->values[$id])
        || isset($this->protected[$this->values[$id]])
        || !method_exists($this->values[$id], '__invoke')
    ) {
        return $this->values[$id];
    }

    if (isset($this->factories[$this->values[$id]])) {
        return $this->values[$id]($this);
    }

    $this->frozen[$id] = true;
    $this->raw[$id] = $this->values[$id];

    return $this->values[$id] = $this->values[$id]($this);
 }

Here, if the object is in the factories Object Store(SplObjectStorage type), it returns a new instance of the class with id $id. then in the last return again $this->values[$id] is set to a new instance of the object and that new instance is returned.

return $this->values[$id] = $this->values[$id]($this).

This is the line I fail to understand. How is this line supposed to return the same instance for different calls of offsetGet for the same $id. Won't it return a new instance every time? Please help me. I tried a lot but I don't get it.


Solution

  • I looked at the source code of pimple and found out that once the object is instantiated and kept in $this->values[$id], the next call of offsetGet will return from the second if condition. i.e this if condition:

    if (
        isset($this->raw[$id])
        || !is_object($this->values[$id])
        || isset($this->protected[$this->values[$id]])
        || !method_exists($this->values[$id], '__invoke')
    ) {
        return $this->values[$id];
    }
    

    Looking at the unit tests, i found out that the objects without the magic method __invoke can be shared . If the object has a magic method __invoke(i.e the object can be treated as a function) , a new instance is returned everytime. So, you can see that the first, second and third condition on the above if statement return false . but the fourth condition returns true and hence the $this->values[$id] returns the same instance every time.