I have Googled for the past couple hours trying to figure out how to do this. I just want to be clear that my issue is not this issue or that issue, because I am not trying to check inside the script if the variables are set. I am trying to check outside it, to see if they're set / passed to the included file before they're interpreted, or at least meaningfully interpreted to the point where an error is thrown. Let me explain.
A little Background
I am creating a utility package for internal usage at the company I work for. I have chosen to render templates one of two ways: including them or outputting the rendered string.
public function render($context = array()) {
do_action(self::TAG_CLASS_NAME.'_render_view', $this, $context);
if ( empty( $this->html ) ) {
ob_start();
$this->checkContext($context);
extract( $context );
require_once $this->getFullPath();
$renderedView = ob_get_contents();
ob_end_clean();
$this->html = $renderedView;
return $renderedView;
} else {
return $this->html;
}
}
public function includeView($context = array()) {
do_action(self::TAG_CLASS_NAME.'_include_view', $this, $context);
extract( $context );
include $this->getFullPath();
}
The problem
Inside of the render
method, I start some output buffering. This is so I can have the interpreter evaluate the code and output the HTML as a string (without taking the eval()
hit. Inside my unit tests, I experiemented with what would happen if I left out a context
that was inside the template itself. For example: If I have a context
array that looks like:
$context = array(
'message' => 'Morning'
);
And an associated template that looks like this:
<?php echo "Hello ".$name."! Good ".$message; ?>
Or this
<p>Hello <?php echo $name; ?>! Good <?php echo $message; ?></p>
Doesn't matter how it's formatted, as long as the context
vars are passed to it correctly. Anyway, leaving out the $name
in the context
will result in a "Undefined variable: $name"
E_NOTICE message. Which makes sense. How do you 'capture' that undefined variable before it creates the notice?
I have tried to use:
$rh = fopen($this->getFullPath(), 'r');
$contents = fread($rh, filesize($this->getFullPath()));
fclose($rh);
Where $contents
outputs:
"<?php echo sprintf("Hello %s, Good %s.", $name, $greeting); ?>"
The next logical step (for me anyway, thus the question) is to extract the vars in that string. So I briefly started down the road of creating a regex to match on that string and capture the variables, but ended up on here, because I felt like I was duplicating work. I mean, the PHP interpreter already does this effectively, so there must be a way to utilize the built-in functionality. Maybe?
All this to say, I want to do something similar to this psuedo code:
protected function checkContext($context) {
require $filename;
$availVars = get_defined_vars()
if ( $availVars !== $context ) {
setUnDefinedVar = null
}
}
Having said that, this may not even be the right way to do it, but what is? Do I let the interpreter fail on an undefined variable upon inclusion of the file? If I let it fail, am I exposing myself to any security vulnerabilities? Note: I am not setting any variables in templates via $_GET
or $_POST
.
Any answers are much appreciated. Thank you ahead of time.