Include within class and access to $ this, self or static

19

I found this little code inside the Composer/ClassLoader.php folder, in a project where I use composer .

 /**
 * Scope isolated include.
 *
 * Prevents access to $this/self from included files.
 */
function includeFile($file)
{
    include $file;
}

The translated comment could be:

  

Isolated scope for include . Prevents access to $this and self

Testing

Considering the above case, I noticed that in Laravel 4, I have access to $this in any view!

And see what happens in the following code in any view:

@extends('layout.default')
<?php
   $this->compiler = 1;
?>

The following error is generated:

  

Call to a member function isExpired () on a non-object

And the interesting detail is that since include would be directly in the class method, I could access a property of type protected and give it an unexpected value for the framework!

In other frameworks, such as Zend , $this can also be accessed in view .

Questions

So, then some questions popped up:

  • What other possible problems are caused by a include within the class and have access to $this - in addition to those already mentioned?

  • In an MVC structure, in the class representing view should I leave the free access to $this , or use another class to "be the $this " of the view, or something else?

asked by anonymous 22.07.2015 / 15:06

2 answers

5

In response to my second question, I would vote for a change in the context of the included file, for view and MVC .

And I explain:

As far as the context applied to include given within a class representing view , had already been worked out a solution a short time ago - that can be improved by the community:)

Problem

  • I have a class, which is responsible for rendering view . However, I want to prevent members of this class from being accessed through $this , but at the same time I want the template to be rendered to have its own $this .

Solution

  • At the time of rendering, use Closure to lock the included template and at the same time set another object as the context for Closure .

I did this as follows:

class View
{
    protected $file;

    protected $data = [];

    public function __construct($file, array $data = [])
    {
        $this->file = $file;

        $this->data = $data;
    }

    public function render()
    {
        return $this->createClosureContext();
    }

    protected function createClosureContext()
    {
        // Cria parâmetros de fachada apenas para obrigar o tipo de argumento

        $caller = function ($file, array $data) {

            unset($file, $data);

            ob_start();

            // Não passa variável, para não colidir com "extract"

            extract(func_get_arg(1));

            include_once func_get_arg(0); 

            return ob_get_clean();
        };

        return $caller->bindTo($this->createObjectContext())->__invoke($this->file, $this->data);
    }

    protected function createObjectContext()
    {
        $object = new StdClass;

        $object->Html = (object)'Simulate instance of Helper';

        return $object;
    }
}

Explanation

Like any template class, View is given the template name and the data that will be sent to the template.

The section where the "magic" in relation to $this access is here:

protected function createClosureContext()
{
    // Cria parâmetros de fachada apenas para obrigar o tipo de argumento

    $caller = function ($file, array $data) {

        unset($file, $data);

        ob_start();

        // Não passa variável, para não colidir com "extract"

        extract(func_get_arg(1));

        include func_get_arg(0); 

        return ob_get_clean();
    };

    return $caller->bindTo($this->createObjectContext())->__invoke($this->file, $this->data);
}

I created a closure called $caller and, using the bindTo method, set which object will be used as $this within that Closure . This Closure is responsible for adding the file.

The object passed in bindTo is a simple stdClass , which could be any object. In this case, I set it inside the View::createObjectContext() method.

When we instantiate, we can conclude by testing that the context of $this (for the template included by the view) has been properly changed.

Example:

new View('tpl.php', ['nome' => 'Wallace']);

In the template tpl.php :

<?php echo $nome ?>
<?php print_r($this) ?>

Prints:

Wallace 
stdClass Object
(
    [Html] => stdClass Object
        (
            [scalar] => Simulate instance of Helper
        )

)
    
05.08.2015 / 14:17
3

In fact, Composer uses the autoloader to include files based on a directory structure. For example, the Modules\Db\Adapter class would be in a directory with the Modules\Db\Adapter.php PHP file. The include function would only be used explicitly within a file if it was an old code that did not support the autoloading structure, where each file is a class itself.

Accessing a method / variable by means of self (for static variables) and $this (for variables as an object of the class itself), is only by inheritance ( extends ) or if method / variable to belong to the class itself.

The methods accessed by the view through $this , for example, are methods that are public or protected visibility of the same class as View or some method of the same visibility of a class inherited by it.

    
04.08.2015 / 17:10