5

I have quite a lot of PHP view files, which I used to include in my controllers using simple include statements. They all use methods declared in a view class which they get like $view->method(); However I recently decided that it would be better if the including would also be done by this view class. This however changes the scope of the included file so that $view is no longer defined. Here is a code example:

in someViewFile.php (BOTH siuations)
    <html>
    <head><title><?php echo $view->getAppTitle(); ?></title>
    etc.
OLD SITUATION in controller:
    $view = new view;
    include('someViewFile.php'); //$view is defined in someViewFile.php
NEW SITUATION in controller:
    $view = new view;
    $view->show('someViewFile'); //$view is not defined in someViewFile.php

Right now I hacked my way around the problem using this in the view class:

public function show($file){
    $view = &$this;
    include($file.".php");
}

Is there anyway to declare the scope of the inluded file or is this the best way of solving the problem?

These examples are off coarse simplified.

tereško
  • 58,060
  • 25
  • 98
  • 150
Pim Jager
  • 31,965
  • 17
  • 72
  • 98

7 Answers7

13

Here' a simplified but functional view class that I've seen around quite a lot and use quite a lot.
As you can see in the code below: you instantiate a view with the filename of the template file.
The client code, probably a controller, can send data into the view. This data can be of any type you need even other views.
Nested views will be automatically rendered when the parent is rendered.
Hope this helps.

// simple view class
class View {
    protected $filename;
    protected $data;

    function __construct( $filename ) {
        $this->filename = $filename;
    }
    
    function escape( $str ) {
        return htmlspecialchars( $str ); //for example
    }
    
    function __get( $name ) {
        if( isset( $this->data[$name] ) ) {
            return $this->data[$name];
        }
        return false;
    }
    
    function __set( $name, $value ) {
        $this->data[$name] = $value;
    }
    
    function render( $print = false ) {
        ob_start();
        include( $this->filename );
        $rendered = ob_get_clean();
        if( $print ) {
            echo $rendered;
            return;
        }
        return $rendered;
    }
    
    function __toString() {
        return $this->render();
    }
}

Usage

// usage
$view = new View( 'template.phtml' );
$view->title = 'My Title';
$view->text = 'Some text';

$nav = new View( 'nav.phtml' );
$nav->links = array( 'http://www.google.com' => 'Google', 'http://www.yahoo.com' => 'Yahoo' );

$view->nav = $nav;

echo $view;

The templates

//template.phtml
<html>
    <head>
        <title><?php echo $this->title ?></title>
    </head>
    <body>
        <?php echo $this->nav ?>
        <?php echo $this->escape( $this->text ) ?>
    </body>
</html>

//nav.phtml
<?php foreach( $this->links as $url => $link ): ?>
    <a href="<?php echo $url ?>"><?php echo $link ?></a> 
<?php endforeach ?>
schellmax
  • 5,678
  • 4
  • 37
  • 48
meouw
  • 41,754
  • 10
  • 52
  • 69
3

You can't change include() scope, no, so your method is probably about as good as it gets in the situation you're describing. Though I'd skip the ugly PHP4-compat ampersand, myself.

chaos
  • 122,029
  • 33
  • 303
  • 309
1

An even more simplified version of a View class:

Class View {

    protected $filename;

    function __construct($filename){
        $this->filename = $filename;
    }

    function display(){
        global $session; //put global variables here

        include Cfg::ROOT . '/views/' . $this->filename;    
    }

    function assign($key, $val){
        $this->$key = $val;
    }

}

In controller:

$view = new View('homepage.php');
$view->assign('page_title',$page_title); //assign all needed variables from controller
$view->display();

In the template/view file:

echo $this->page_title;
Andrew
  • 18,680
  • 13
  • 103
  • 118
1

What i do, ( i don't know how OK it is) is simply

extract($GLOBALS);
include $view.".php";

Sure as hell works

Cheers!

Pedro
  • 1,001
  • 11
  • 16
0

Expanding on what Quano has to offer:

Take it one step further and include variables in the global scope of execution (that would otherwise have to be called with global on the internals of include). The idea is that you want the include to work as if it was included in your main application scope, or a scope of your definition.

Consider this class:

class PHPInclude {
    private static $globalsList = array(
            'GLOBALS','_SERVER','_GET','_POST','_FILES',
            '_REQUEST','_SESSION','_ENV','_COOKIE'
        );
    public static function phpInclude($file,$variables=array()) {
        if ( file_exists($file) ) {
            $globs = array();
            foreach ( $GLOBALS as $key => $value ) {
                if ( !in_array($key,sefl::$globalsList) ) {
                    array_push($globs,$key);
                }
            }
            foreach ( $globs as $key ) {
                global $$key;
            }
            foreach ( $variables as $variable ) {
                global $$variable;
            }
            unset($globs,$key,$value);
            ob_start();
            include $file;
            return ob_get_clean();
        }
        return '';
    }
}

Use it as follows:

$foo = 'bar';
function testScope() {
    $woo = 'sah';
    PHPInclude::phpInclude('file.phtml',array($woo));
}

The example will automatically include $foo since it is in global scope, and $woo is added since it is in local scope.

I haven't tested $this as an arbitrary variable, but my spider senses tell me it won't work (warning).

zamnuts
  • 9,492
  • 3
  • 39
  • 46
  • darn, answer is at http://stackoverflow.com/questions/2211877/giving-php-included-files-parent-variable-scope – zamnuts Feb 15 '13 at 17:26
0

Maybe this has to do with my php-settings or something, but if I include a file inside a function, that included file will only have the scope of that function's variables.

$a = 1; // This variable isn't visible for the included file

function render()
{
    $b = 2; // This variable is
    include('template.php');
}


// ----------------------------------------
// template.php

<html>
<body>
<?=$a?> // wont show
<?=$b?> // will show
</body>
</html>

I think I'd go with something like this:

function render($template, $variables)
{
    extract($variables);
    include($template);
}

Usage:

render('templates/mytemplate.php', array(a=>1, b=>2));
quano
  • 18,812
  • 25
  • 97
  • 108
0

I found a great solution. Just create an anonymous function and call it. Only for PHP 7+.

$arg1=1;
call_user_func(function()use($arg1,$arg2,...){
 $values=$arg1+$arg2;
 // $values is limited in scope inside this function
}

For example:

$values='HEY!';

//included code
$arg1=1;
$arg2=2;
call_user_func(function()use($arg1,$arg2,...){
 $values=$arg1+$arg2;
 // $values is limited in scope inside this function
}

echo $values;
// echos HEY!
Maciek Semik
  • 1,872
  • 23
  • 43