简体   繁体   中英

Accessing parent object variables from within the scope of the child object in php

So I have been researching templating engines and how to create my own simple templating engine. From a pure learning perspective, I read through a couple of them like this one here .

Using a little modified version of the class mentioned in the above link, I thought of testing it out but encountered a problem.

When calling an instances of the same template class for inner HTML and then assigning that as a var/value pair to the parent instance, I'm unable to access the main parent's variables within the the HTML (child object).

Confusing?

Maybe the following code will help.

So if I instanciate the template as so ( the template class is the same as the one mentioned in the above link) -

$_page = new tplEngine();
$_page->load(TPLFILES_DIR . "/root.php");

and then instanciate header.html as a new instance of the tplEngine class, and assign it as a variable to the 1st instance as follows -

$_header = new tplEngineChild(TPLFILES_DIR . "/common/header.html");
$_page->set("header", $_header->parse());

where...

root.php
---------------

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <title><?php print $this->title; ?></title>
        <meta name="keywords" content="<?php print $this->meta_keywords; ?>" />
    <meta name="description" content="<?php print $this->meta_description; ?>" />
        <?php foreach($this->styles as $stylepath) : ?>
        <link rel="stylesheet" type="text/css" href="<?php print $stylepath; ?>" />
        <?php endforeach; ?>
    </head>
    <body>
        <div class="body-wrap">
            <div class="header">
                <?php print $this->header; ?>
            </div>
            <div class="content-wrap">
                <?php var_dump($this->mid_content); ?>
            </div>
            <div class="footer">
                <?php print $this->footer; ?>
            </div>
        </div>
    </body>
</html>

and

header.html
-----------------

<div class="mainHeader">
    <div class="logo">
        webTrack.in'
    </div>
    <div class="dashboard">

        <?php if($this->get(isLoggedIn) == false) : ?>
        <p class="greeting">Hello <span class="username"><?php echo this->username; ?></span></p>
        <a class="logout">logout</a>
        <?php else : ?>
        <p class="greeting">Hello <span class="username"><?php echo $this->username; ?></span></p>
        <p><a onclick="showLogin()">Login</a></p>
        <form id="loginForm" class="login form" action="" method="post">
            <input type="text" name="username" value="Username" />
            <input type="password" name="password" value="Password" />
        </form>
        <?php endif; ?>
    </div>
</div>
<nav>
    <ul class="headerNav">
        <li><a href="/">Home</a></li>
        <li><a href="/pricing">Plans and Pricing</a></li>
        <li><a href="/aboutUs">About Us</a></li>
    </ul>
</nav>

(In the above case $this->get(isLoggedIn) and this->username are variables assigned to the $_page instance) I encounter a problem where, in the header.html file, I am unable to access the variables set under the $_page instance of the tplEngine class.

What would be the best approach to solve this problem?

Everything worked fine when I set the $_page instance as global in header.html. But is that a right approach?

About object inheritance

A class is a template for objects, and defines object properties and methods, while object is instance of a class. When you extend a class, the child class inherits properties and methods from a parent.

In your case, there is no inheritance (parent-child relationship), $_header as a separate object is only property of a $_page . To enable a 'communication' between those two objects, $_header must have a reference to a $_page object.


Template class

This is a modified version of the Template class you are using. When you dynamically create properties, __set() and __get() magic methods should be used. It also uses __toString() method, so that the template objects can be treated as a string. All variables that template file uses, should be assigned to template object. By using a class defined like this, all templates are rendered simultaneously.

class tplEngine {
    private $template = '';

    public function __set( $var, $content )
    {
        $this->$var = $content;
    }

    public function __get( $var )
    {
        return ( isset($this->$var) ? $this->$var : null );
    }

    public function __construct( $template )
    {
        // is_readable() - tells whether a file exists and is readable
        if ( !is_readable( $template ) ) throw new IOException( "Could not find or access file: $template" );
        $this->template = $template;
    }

    public function __toString() 
    {
        ob_start();
        require ( $this->template );
        $content = ob_get_clean();
        return $content;
    }
}

// usage:
$_page = new tplEngine( TPLFILES_DIR . "/root.php" );

$_header = new tplEngine( TPLFILES_DIR . "/common/header.html" );
$_header->isLoggedIn = true;
$_header->username = 'some-username';

$_page->header = $_header;

// in root.php 
echo $this->header;

Accessing parent variables

Parent property

One way to access variables in 'parent' object is to add parent property to template class through constructor:

public function __construct( $template, $parent = null )
{
    // is_readable() - tells whether a file exists and is readable
    if ( !is_readable( $template ) ) throw new IOException( "Could not find or access file: $template" );
    $this->template = $template;
    $this->_parent = $parent;
}   

Access parent properties in templates like:

$this->_parent->username;

Make parent properties local

Another way is to make them local ( neat trick if you don't want to bother with the $this->_parent calls ):

public function __toString() 
{
    ob_start();
    if ( $this->_parent ) {
        foreach ( get_object_vars( $this->_parent ) as $key => $value ) $$key = $value;
    }
    require ( $this->template );
    $content = ob_get_clean();
    return $content;
}

Additional info

Adapter Design Pattern

PHP Overloading

Magic Methods

Smarty Template Engine - variable scopes

the problem is that $_header is not a child of $_page in the means of php class inheritance and you dont want them to be true php parent and child.

instead, change the tplEngineChild constructor to take a $parent as additional argument, in this case $_page.

constructor could look like this:

function __construct($parent = null, $template = null)
{
    if(isset($parent))
    {
        $this->parent = $parent;
    }

    if (isset($template))
    {
        $this->load($template);
    }
}

now $ header could use $this->parent->username. Be sure to have "public $username" in your parent class definition. You could use the php method overloading ( _get) to automatically resolve the properties at the parent if it does not exist in the child.

Also you could pass $_header instead of $_header->publish(); to $_page->set and change the $_page template to header->publish() ?>. That way, the header gets published when the $_page does, and not at the time of calling $_page->set().

Some concepts are used in different languages in a different manner. And although the same words are used, they are not the same. It can be quite confusing. This is in my opinion is the source of your question. I will describe three patterns used in templating. Each pattern has its specific relationship between parent and children.

1. differentiating implementations

A template design pattern has strict parent - child relationships. The children are extensions of the parent, which is normally an abstract class. Every child is a different implementation of the parent. An example of an abstract template class is for instance a Shape. Child implementations can be Triangle, Square and Circle. They all share some public abstract methods like draw() and resize() that they all have the same implementation. The purpose of the abstract method is to ensure that they all have an unique, characteristic implementation of that method(for that child). The base class can also have some non abstract methods. Examples are methods like fill() and rotate(). Every child is allowed to override those methods, but it is not needed, if the default implementation suffices. The children will be able to use the public/protected data and methods from the parent, see http://en.wikipedia.org/wiki/Template_method_pattern .

2. repeatable composition

The template system in html-files serves a similar concept, but with a different purpose. The purpose of the template system in html-files is to create a flexible insertion system of independent blocks. Each block is independent of any other block, hence you can not assume that they have anything in common. They might have, but it is not mandatory. You can compare this with the shared methods of the abstract class, because within every html-file you have to implement it in the same way as you would in other files. They are independent blocks to ensure maximum flexibility in design. The same block of content can be used in different template files without any problem. And they should be able to function this way. This is the composite design pattern. The parent-child relationship can be best described as part-whole relationships, see http://en.wikipedia.org/wiki/Composite_pattern .

3. shared data, unique presentation

Within each html-fragment is the same data (all the data of the current request and session) used. They share the same data, but they should not inherit anything from each other. They also do not share any implementation feature. You can have a block of content in which you show an overview of search findings using a pager. Or you can have a block with a static footer. In every html template there is only one header and one body. There can be however more menus and a particular menu can appear in different shapes on different places. Therefor your should think about the Observer or MVC pattern. The data used is the Model part. It is the same for all Views on the data during one request. Each block is a different view on the Model and will use the information that it requires. No block needs to have knowledge about the state or data of any other block. This in my opinion is the reason why you asked if $_page should be a global? You needed it, because of the shared data. Not because of the html part of the file. (That is a matter of composition.) If you however separate those two parts of the root.php and have a common data object for all blocks, then it can work without the $_page being a global.

Conclusion:

Where the parent-child relationships in the template design pattern is between the classes (not the objects), there is the part-whole relationships for html-files between the blocks of html-fragments and the model-view relationships between the shared data and the required data for any particular html-fragment. The parent-child relationship between the classes is fixed, the part-whole relationships between the html-fragments is flexible, the model-view relationship is standardized, but not fixed.

It is most efficient if the data object is only created once and is for the duration of the request unaltered. A global can do that, but a local variable in a method can do that as well, just as a static variable of a class. I would advise you to use that implementation that suits the maintenance of your application best.

Another possible approach is to not differentiate between the tplEngine and the tplEngineChild. Here's a simple implementation of what I mean.

class tmplEngine {

    protected $template_root = "";

    // container for the template variables
    protected $tmpl_vars = array();

    public function __construct($tmpl_dir = null){
         if ($tmpl_dir) $this->setTemplateDir($dir);
    }

    // sets a template root directory
    public function setTemplateDir($dir){
        $this->template_root = $dir;
    }

    // parses an external file from the current scope
    public function parse($file){
        ob_start();
            require($this->template_root.$file);
        return ob_get_clean();
    }

    // magic getter allows access as $tmplEninge->propertyName;
    public function __get($property){
        return array_key_exists($property, $tmpl_vars)?$this->tmpl_vars[$property]:"";    
    }

    // magic setter allow access as $tmplEngine->propertyName = "myVal"
    public function __set($property, $value){
        $this->tmpl_vars[$property] = $value;
    }
}

So Now your invocation would be something like this.

$t = new tmplEngine(TPLFILES_DIR);
$t->header = $t->parse("/common/header.html");
$t->main = $t->parse("/common/article.html");
$t->footer = $t->parse("/common/footer.html");

echo $t->parse("/root.html");

Hope this helps

Just to point out some interesting points about templating system. On my own experience, templates should be .... what they mean to be, templates. Not a bunch of PHP code.

All those templates systems that are actually used by all those famous systems (call it tpl, magento, oscommerce, and the list is very long) are anything but templates.

Why do I say this:

A template is supposed to be a piece of (you name the language, HTML, XML, WAP, CSS, JS, etc) natural code, with parameters made to be replaced into them.

A template should NOT contains programming language into them, why: - The graphic designers, or HTML players then generally does NOT understand the code - You need a programmer to modify the templates - The template becomes just another piece of PHP code - You need (generally) big knowledge of programming language to be able to modify and maintain your code.

A template should be a natural language code with parameters easily understandable by low programing skill people

This is a good template system, like Java templates, C templates, and other languages templates, since it does not mix PHP with native code:

http://www.webability.info/?P=documentacion&wiki=/DomCore/reference/Templates

Just my 2 cents for a better programing

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM