【问题标题】:Accessing parent object variables from within the scope of the child object in php从php中的子对象范围内访问父对象变量
【发布时间】:2013-04-28 19:37:16
【问题描述】:

所以我一直在研究模板引擎以及如何创建自己的简单模板引擎。从纯粹的学习角度来看,我阅读了其中的几个,就像这个here

使用上面链接中提到的类的小修改版本,我想测试一下,但遇到了问题。

当为内部 HTML 调用同一模板类的实例,然后将其作为 var/value 对分配给父实例时,我无法访问 HTML(子对象)中的主要父级变量。

令人困惑?

也许下面的代码会有所帮助。

所以如果我这样实例化模板(模板类与上面链接中提到的相同) -

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

然后将 header.html 实例化为 tplEngine 类的新实例,并分配 它作为第一个实例的变量如下 -

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

在哪里...

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>

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>

(在上述情况下,$this-&gt;get(isLoggedIn)this-&gt;username 是分配给 $_page 实例的变量) 我遇到了一个问题,在 header.html 文件中,我无法访问在 tplEngine 类的 $_page 实例下设置的变量。

解决这个问题的最佳方法是什么?

当我在 header.html 中将 $_page 实例设置为全局时,一切正常。但这是正确的做法吗?

【问题讨论】:

    标签: php html oop object templating


    【解决方案1】:

    关于对象继承

    类是对象的模板,定义对象的属性和方法,而对象是类的实例。扩展类时,子类会从父类继承属性和方法。

    在您的情况下,没有继承(父子关系),$_header 作为单独的对象只是$_page 的属性。要启用这两个对象之间的“通信”,$_header 必须具有对 $_page 对象的引用。


    模板类

    这是您正在使用的 Template 类的修改版本。动态创建属性时,应使用__set()__get() magic methods。它还使用__toString() 方法,使模板对象可以被视为一个字符串。模板文件使用的所有变量都应该分配给模板对象。通过使用这样定义的类,所有模板都会同时呈现。

    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;
    

    访问父变量

    父属性

    访问“父”对象中的变量的一种方法是通过构造函数将父属性添加到模板类:

    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;
    }   
    

    访问模板中的父属性,例如:

    $this->_parent->username;
    

    将父属性设为本地

    另一种方法是让它们本地化(如果您不想打扰 $this-&gt;_parent 电话,那就是巧妙的技巧):

    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;
    }
    

    其他信息

    Adapter Design Pattern

    PHP Overloading

    Magic Methods

    Smarty Template Engine - variable scopes

    【讨论】:

    • 嗯..你测试过这个吗?喜欢这种方法。以后再挖
    【解决方案2】:

    问题是 $_header 在php class inheritance 的意义上不是 $_page 的孩子,你不希望他们成为真正的 php 父母和孩子。

    改为将 tplEngineChild 构造函数更改为将 $parent 作为附加参数,在本例中为 $_page。

    构造函数可能如下所示:

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

    现在 $header 可以使用 $this->parent->username。确保在您的父类定义中有“public $username”。如果子级中不存在父级属性,您可以使用php method overloading (_get) 自动解析父级属性。

    你也可以传递 $_header 而不是 $_header->publish();到 $_page->set 并将 $_page 模板更改为 header->publish() ?>。这样,标头会在 $_page 发布时发布,而不是在调用 $_page->set() 时发布。

    【讨论】:

    • 是的,这是我的想法,但我仍在研究父>子对象结构是否是正确的方法。
    【解决方案3】:

    有些概念以不同的方式在不同的语言中使用。尽管使用了相同的词,但它们并不相同。这可能会让人很困惑。我认为这是您问题的来源。我将描述模板中使用的三种模式。每种模式都有其特定的父子关系。

    1。区分实现

    模板设计模式具有严格的父子关系。子类是父类的扩展,父类通常是一个抽象类。每个孩子都是父母的不同实现。抽象模板类的一个示例是 Shape。子实现可以是三角形、正方形和圆形。 它们都共享一些公共抽象方法,例如 draw() 和 resize(),它们都具有相同的实现。抽象方法的目的是确保它们都具有该方法的唯一、特征实现(对于那个孩子)。基类也可以有一些非抽象方法。例如 fill() 和 rotate() 等方法。每个孩子都可以覆盖这些方法,但如果默认实现足够,则不需要。孩子将能够使用来自父母的公共/受保护数据和方法,请参阅http://en.wikipedia.org/wiki/Template_method_pattern

    2。可重复的组合

    html-files 中的模板系统服务于类似的概念,但目的不同。 html-files 中模板系统的目的是创建一个灵活的独立块插入系统。每个块都独立于任何其他块,因此您不能假设它们有任何共同点。他们可能有,但这不是强制性的。 您可以将其与抽象类的共享方法进行比较,因为在每个 html 文件中,您必须以与在其他文件中相同的方式实现它。它们是独立的块,以确保设计的最大灵活性。相同的内容块可以在不同的模板文件中使用,没有任何问题。他们应该能够以这种方式运作。这就是复合设计模式。父子关系最好描述为部分-整体关系,请参阅http://en.wikipedia.org/wiki/Composite_pattern

    3。共享数据,独特展示

    在每个 html-fragment 中都使用相同的数据(当前请求和会话的所有数据)。它们共享相同的数据,但不应相互继承任何东西。它们也不共享任何实现特性。您可以有一个内容块,您可以在其中使用寻呼机显示搜索结果的概述。或者你可以有一个带有静态页脚的块。在每个 html 模板中,只有一个 header 和一个 body。然而,可以有更多的菜单,并且特定的菜单可以在不同的地方以不同的形状出现。因此,您应该考虑观察者或 MVC 模式。使用的数据是模型部分。在一个请求期间对数据的所有视图都是相同的。每个块都是模型上的不同视图,并将使用它需要的信息。没有块需要了解任何其他块的状态或数据。我认为这就是您问 $_page 是否应该是全局的原因?由于共享数据,您需要它。不是因为文件的 html 部分。 (这是一个组合问题。)但是,如果您将 root.php 的这两个部分分开并为所有块提供一个公共数据对象,那么它可以在 $_page 不是全局的情况下工作。

    结论:

    模板设计模式中的父子关系是类之间(而不是对象),html-fragments块之间是html-files的部分-整体关系,html-fragments块之间是模型-视图关系。共享数据和任何特定 html 片段所需的数据。类之间的父子关系是固定的,html-fragments之间的部分-整体关系是灵活的,模型-视图关系是标准化的,但不是固定的。

    如果数据对象只创建一次并且在请求期间保持不变,则效率最高。全局可以做到这一点,但方法中的局部变量也可以做到这一点,就像类的静态变量一样。我建议您使用最适合维护您的应用程序的实现。

    【讨论】:

    • +1 感谢 ShalomSam 编辑我的文字。你做得更好了!
    【解决方案4】:

    另一种可能的方法是不区分 tplEngine 和 tplEngineChild。这是我的意思的简单实现。

    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;
        }
    }
    

    所以现在你的调用将是这样的。

    $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");
    

    希望对你有帮助

    【讨论】:

    • 这类似于我已经拥有的..并没有解决问题。在后续子文件中无法访问 VAR 定义的 $t。
    • 恕我直言模板不应设置变量。另外,这可以通过在一个模板中设置$this-&gt;var = "test" 并在以后的模板echo $this-&gt;var 中完成。所有模板都在同一范围内解析,因此它们都可以访问相同的变量。
    【解决方案5】:

    只是指出一些关于模板系统的有趣点。 根据我自己的经验,模板应该是……它们的意思,模板。不是一堆 PHP 代码。

    所有那些著名系统(称为 tpl、magento、oscommerce,列表很长)实际使用的所有模板系统都不是模板。

    为什么要这样说:

    模板应该是一段(您命名语言、HTML、XML、WAP、CSS、JS 等)自然代码,并带有可替换的参数。

    模板中不应包含编程语言,为什么: - 图形设计师或 HTML 播放器通常不理解代码 - 你需要一个程序员来修改模板 - 模板变成了另一段 PHP 代码 - 您需要(通常)丰富的编程语言知识才能修改和维护您的代码。

    模板应该是自然语言代码,其参数易于编程技能低的人理解

    这是一个很好的模板系统,就像 Java 模板、C 模板和其他语言模板一样,因为它没有将 PHP 与本机代码混合:

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

    只需我的 2 美分即可获得更好的编程

    【讨论】:

      猜你喜欢
      • 2020-12-03
      • 2016-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-16
      相关资源
      最近更新 更多