Question:
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
. Prevent access to$this
andself
Tests
Taking into account the case mentioned above, I realized 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, as the include
would be directly in the class method, I could access a protected
property and assign it an unexpected value for the framework!
In other frameworks like Zend
, $this
can also be accessed in the view
.
Questions
So, I had a few questions:
-
What are other possible problems caused by an
include
inside the class and having access to$this
– besides the ones already mentioned? -
In an MVC structure, in the class that represents the
view
, should I leave free access to$this
, or use another class to "be the$this
" of the view, or something else? -
When would it be recommended to enclose the
include
function and when not (always, never or in most cases)?
Answer:
Answering my second question, I would vote in favor of changing the context of the included file, when it comes to the view
in MVC
.
And I explain:
As for the context applied to the include
given within a class that represents a view
, I had already developed a solution a little while ago – which can be improved by the community 🙂
Problem
- I have a class, responsible for rendering the
view
. However, I want to prevent the members of this class from being accessed through$this
, but at the same time I want the template that will be rendered to have its "own$this
".
Solution
- When rendering, use a
Closure
to enclose the included template and, at the same time, define another object as a context for theClosure
.
I worked it out 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 [simple] template class, View
takes as a parameter the name of the template and the data that will be sent to the template.
The snippet where the "magic" regarding accessing $this
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, through the bindTo
method, defined which object will be used as $this
inside this Closure
. This Closure
is responsible for including the file.
The object passed in bindTo
is a simple stdClass
, which could be any object. In this case, I defined 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']);
No template tpl.php
:
<?php echo $nome ?>
<?php print_r($this) ?>
Print:
Wallace
stdClass Object
(
[Html] => stdClass Object
(
[scalar] => Simulate instance of Helper
)
)