Accessing variables in included file through helper method in PHP - php

I am trying to build a php class that will let me include other php "template" files to display. I want all of the variables that are in the scope that it is called from to be available in the included file. Here's the catch, I want to pass off the actual including to a helper method to keep the code DRY. Here is what I have so far:
/* TemplateLoader.php */
class TemplateLoader {
public function foo() {
$var = "FOO!";
//Works
include 'template.php';
}
public function bar() {
$var = "BAR!";
//Doesn't work
$this->render("template");
}
private function render( $name ) {
include $name . '.php';
}
}
/* template.php */
<?php echo $var; ?>
My question is: How can I accomplish the behaviour of including the template directly in the original method while still using helper method to actually do the "heavy lifting"? I would really appreciate any help you can give!

This is what first came to my mind - I'm not sure I like it too much, but I'm not sure of a generic alternative. This captures all of the current variables with get_defined_vars(), and passes them to render(), where they are they extract()ed, and thus accessible by the included file.
You could probably filter the return from get_defined_vars() before you pass it to render(), but I should work.
public function bar() {
$var = "BAR!";
$this->render("template", get_defined_vars());
}
private function render( $name, &$vars) {
extract( $vars);
include $name . '.php';
}
You could probably filter the return from get_defined_vars() before you pass it to render(), but it should work.

Related

PHP - architecture, issue with variables scope

I'm writing now an app, which is supposed to be as simple as possible. My controllers implement the render($Layout) method which just does the following:
public function render($Layout) {
$this->Layout = $Layout;
include( ... .php)
}
I don't really understand the following problem: in my controller, I define a variable:
public function somethingAction() {
$someVariable = "something";
$this->render('someLayout');
}
in the php view file (same I have included in the render function) I try to echo the variable, but there is nothing. However, if I declare my action method like this:
public function somethingAction() {
global $someVariable;
$someVariable = "something";
$this->render('someLayout');
}
and in my view like this:
global $someVariable;
echo $someVariable;
it does work. Still it is annoying to write the word global each time.
How can I do this? I'd rather not use the $_GLOBAL arrays. In Cake PHP, for example, there is a method like $this->set('varName', $value). Then I can just access the variable in the view as $varName. How is it done?
From the PHP Manual on include:
When a file is included, the code it contains inherits the variable scope of the line on which the include occurs. Any variables available at that line in the calling file will be available within the called file, from that point forward. However, all functions and classes defined in the included file have the global scope.
When you do
public function somethingAction() {
$someVariable = "something";
$this->render('someLayout');
}
the $someVariable variable is limited to the somethingAction() scope. Calling your render() method will not magically make the variable available in render(), because the render() method has it's own variable scope. A possible solution would be to do
public function somethingAction() {
$this->render(
'someLayout',
array(
'someVariable' => 'something'
)
);
}
and then change render() to
public function render($Layout, array $viewData) {
$this->Layout = $Layout;
include( ... .php)
}
You will then have access to $viewData in the included file, given that you are not trying to use it in some other function or method, e.g. if your included file looks like this:
<h1><?php echo $viewData['someVariable']; ?></h1>
it will work, but if it is looks like this:
function foo() {
return $viewData['someVariable'];
}
echo foo();
it will not work, because foo() has it's own variable scope.
However, a controller's sole responsibility is to handle input. Rendering is the responsibility of the View. Thus, your controller should not have a render() method at all. Consider moving the method to your View class and then do
public function somethingAction() {
$view = new View('someLayout');
$view->setData('someVariable', 'something');
$view->render();
}
The render() method of your View object could then be implemented like this:
class View
…
$private $viewData = array();
public function setData($key, $value)
{
$this->viewData[$key] = $data;
}
public function render()
{
extract($this->viewData, EXTR_SKIP);
include sprintf('/path/to/layouts/%s.php', $this->layout);
}
The extract function will import the values of an array in the current scope using their keys as the name. This will allow you to use data in the viewData as $someVariable instead of $this->viewData['someVariable']. Make sure you understand the security implications of extract before using it though.
Note that this is just one possible alternative to your current way of doing things. You could also move out the View completely from the controller.
Using global you're implicitly using the $_GLOBALS array.
A not recommended example, because globals are never a good thing:
function something() {
global $var;
$var = 'data';
}
// now these lines are the same result:
echo $_GLOBALS['var'];
global $var; echo $var;
Why don't you use simply the $this->set function?
public function render($Layout) {
$this->Layout = $Layout;
include( ... .php)
}
public function somethingAction() {
$this->set('someVariable', "something");
$this->render('someLayout');
}
// in a framework's managed view:
echo $someVariable;
I'm sorry not to know your framework in detail, but that makes perfectly sense.
Actually how it's done: There is a native extract function, that loads an associative array into the current symbol table:
array( 'var0' => 'val0', 'var1' => 'val1')
becomes
echo $var0; // val0
echo $var1; // val1
That's most likely 'what happens'.

How can I pass variables to my views in php

I have a class which contains a method to load a view. This works perfect and the page is loaded correctly! The problem I'm having is that I can't seem to figure out a way to pass variables to the view that my method currently tries to load. How can I go by passing variables to the page? Is there a way I can manipulate my current code to make this work? Or am I going in the complete opposite direction?
Controller Class:
<?php
class Controller {
private $args = array();
public function view($page, $args = null) {
if ($args !== null) {
$this->args = $args;
extract($this->args);
}
$page = VIEWS . $page . '.php';
$template = $this->getTemplate($page);
echo $template;
}
private function getTemplate($file) {
ob_start();
include($file);
$template = ob_get_contents();
ob_end_clean();
return $template;
}
}
?>
Controller:
<?php
class Page extends Controller {
public function person($age) {
$data['age'] = $age;
$this->view('person', $data);
}
}
?>
View Contents:
</h1>My name is Dennis. My age is: <?php echo $age; ?></h1>
The end result is an undefined variable ($age).
The variables which you extract in view() are not available in getTemplate(). It is a different scope. You should instead extract them in the same method that does the rendering.
Also, what you have there is not an MVC view. It's just a template. A view in MVC is an instance which is responsible for UI logic. It should be requesting the information, that it needs, from model layer and then, if a response is not simply a HTTP location header, creating the HTML using multiple templates.
Try this:
</h1>My name is Dennis. My age is: <?php echo $data['age']; ?></h1>
The easiest way is to pass down the $args variable to getTemplate, and do the extract method in the getTemplate method
private function getTemplate($file, $args = null) {
if ($args !== null) {
extract($args);
}
ob_start();
include($file);
$template = ob_get_contents();
ob_end_clean();
return $template;
}
when you include and capture the the output, that php file is executed, so before inclusion all the variables must be present
If you dont want to pass down variables you can use $this->args inside of the template
You extract the variables in a different method than where you include the template. If you want to use plain variables in your templates, make sure that they are extracted in the same scope.

Passing variables to an included file

I have a template class which goes like this:
class Template{
public $pageTitle = "";
public function __construct($pageTitle){
$this->pageTitle = $pageTitle;
}
public function display(){
include "/includes/head.php";
include "/includes/body.php";
}
}
Now, I'm used to Java's workings but I don't understand why I'm getting an undefined variable ($pageTitle) error. The included files need to be able to access that variable. I know it's going to be very simple but I actually haven't found out why.
What do I need to do to allow includes to see this variable?
The includes will also have to use $this->pageTitle. Effectively, the include makes them part of the method body.
The included file lives in the same scope as your object. So you need to call:
$this->pageTitle
If you don't want to do the whole $this->, you can do something like the following... The extract function will extract the array $this->data into variables with names respective to the key/index of each item. So now you will be able to do echo $pageTitle;
class Template{
public $data = Array();
public function __construct($pageTitle){
$this->data['pageTitle'] = $pageTitle;
}
public function display(){
extract($this->data);
include "/includes/head.php";
include "/includes/body.php";
}
}
Scope is a little different in PHP than in java.
For your specific case you need
$this->pageTitle
To access a variable declared outside the class, you'll need to make it global
global $myVar;

how can i include php file in php class

i have php with an array i.e
$var = array(
"var" => "var value",
"var2" => "var value1"
);
and have another file with a class i.e
class class1{
function fnc1(){
echo $var['var2'];
//rest of function here
}
}
now how can i get $var['var'] in class file in function fnc1()
You can pass it as an argument, or use the global keyword to put it in the current scope.
However, using global is discouraged, try passing it as an argument.
Pass it as an argument?
class class1{
function fnc1($var) {
echo $var['var2'];
}
}
And in your other file call this class method with your array as an argument.
From: http://php.net/manual/en/function.include.php
When a file is included, the code it contains inherits the variable scope of the line on which the include occurs. Any variables available at that line in the calling file will be available within the called file, from that point forward. However, all functions and classes defined in the included file have the global scope.
So you could do
class class1
{
function fnc1()
{
include 'thefile.php'
echo $var['var2'];
//rest of function here
}
}
but like others pointed out before, you dont want to do that, because it introduces a dependency on the filesystem in your class. If your method requires those variables to work, then inject them as method arguments or pass them into the constructor and make them a property (if you need them more often). This is called Dependency Injection and it will make your code much more maintainable in the long run, e.g. do
class class1
{
private $data;
public function __construct(array $var)
{
$this->data = $var;
}
function fnc1()
{
echo $this->data['var2'];
//rest of function here
}
}
and then do
$obj = new class1($var);
echo $obj->fnc1();
or require the data to be passed into the method on invocation
class class1
{
function fnc1(array $var)
{
echo $var['var2'];
//rest of function here
}
}
and then
$obj = new class1;
$obj->fnc1($var);
You might use global $var in your included file, but it's really a bad practice, as another script, before your included file, might redefine the value/type of $var.
Example :
class class1{
function fnc1(){
global $var;
echo $var['var2'];
//rest of function here
}
}
A better solution, is to pass your $var as a parameter to your fnc1(), even to your class1::__construct()
#Vindia: I'd prefer the argumant-style too, but would recommend either using type hint or a simple check to avoid warnings when accessing *non_array*['var2']:
// acccepts array only. Errors be handled outside
function fnc1(Array $var) {
echo $var['var2'];
}
// accepts any type:
function fnc1(Array $var) {
if (is_array($var)) {
echo $var['var2'];
}
}
class class1{
function fnc1(){
include 'otherFile.php';
echo $var['var2'];
//rest of function here
}
}

alias to include

i have alias to include() in my functions.php file:
function render_template($template)
{
include($template);
}
and simple template.html :
Hello, <?php echo $name ?>
But unfortunately, alias function gave me this output:
require 'functions.php';
$name = "world";
include('template.html'); // Hello, world
render_template('template.html'); // PHP Notice:Undefined variable: name
why is that? and how i can fix this?
thanks.
You have two more options to make it work around the scope issue. (Using global would require localising a long list of vars. Also it's not actually clear if your $name resides in the global scope anyway and always.)
First you could just turn your render_template() function into a name-resolver:
function template($t) {
return $t; // maybe add a directory later on
}
Using it like this:
$name = "world";
include(template('template.html'));
Which is nicer to read and has obvious syntactical meaning.
The more quirky alternative is capturing the local variables for render_template:
$name = "world";
render_template('template.html', get_defined_vars());
Where render_template would require this addition:
function render_template($template, $vars)
{
extract($vars); // in lieu of global $var1,$var2,$var3,...
include($template);
}
So that's more cumbersome than using a name-resolver.
The variable $name is not visible at the point where include is called (inside function render_template). One way to fix it would be:
function render_template($template)
{
global $name;
include($template);
}
Its a Scope Problem, you can set the variable to global, or encapsulate the whole thing a little bit more, for example like that:
class view{
private $_data;
public function __construct(){
$this->_data = new stdClass();
}
public function __get($name){
return $this->_data->{$name};
}
public function __set($name,$value){
$this->_data->{$name} = $value;
}
public function render($template){
$data = $this->_data;
include($template);
}
}
$view = new view;
$view->name = "world";
$view->render('template.html');
template.html :
Hello <?php print $data->name; ?>
If $name is in the global scope then you can get around it by declaring the variable global with the function. But a better solution would be to re-architect the code to require passing the relevant variable value into the function.
This is expected.
Look here.
You would be interested in the following quote, "If the include occurs inside a function within the calling file, then all of the code contained in the called file will behave as though it had been defined inside that function. So, it will follow the variable scope of that function. An exception to this rule are magic constants which are evaluated by the parser before the include occurs".

Categories