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
)
)