【问题标题】:Templating system scoping issue模板系统范围问题
【发布时间】:2010-11-12 17:53:06
【问题描述】:

我正在尝试用 PHP 构建一个骨架视图系统,但我不知道如何让嵌入式视图接收其父级的变量。例如:

查看类

class View
{
    private $_vars=array();
    private $_file;

    public function __construct($file)
    {
        $this->_file='views/'.$file.'.php';
    }

    public function set($var, $value=null)
    {
        if (is_array($var))
        {
            $this->_vars=array_merge($var, $this->_vars);
        }
        else
            $this->_vars[$var]=$value;

        return $this;
    }

    public function output()
    {
        if (count($this->_vars))
            extract($this->_vars,  EXTR_REFS);
        require($this->_file);
        exit;
    }

    public static function factory($file)
    {
        return new self($file);
    }
}

test.php(顶层视图)

<html>
    <body>
        Hey <?=$name?>! This is <?=$adj?>!
        <?=View::factory('embed')->output()?>
    </body>
</html>

embed.php(嵌入在test.php中

<html>
    <body>
        Hey <?=$name?>! This is an embedded view file!!
    </body>
</html>

代码:

$vars=array(
    'name' => 'ryan',
    'adj' => 'cool'
);
View::factory('test')->set($vars)->output();

输出:

Hey ryan! This is cool! Hey [error for $name not being defined] 
this is an embedded view file!!

问题是我在顶层视图中设置的变量没有传递给嵌入式视图。我怎样才能做到这一点?

【问题讨论】:

    标签: php templates scope


    【解决方案1】:

    所以,我并没有完全回答你的问题,但这是我的超级简单的手工制作的模板系统。尽管界面不同,但它支持您尝试执行的操作。

    // Usage
    $main = new SimpleTemplate("templating/html.php");
    $main->extract($someObject);
    $main->extract($someArray);
    $main->name = "my name";
    $subTemplate = new SimpleTemplate("templating/another.php");
    $subTemplate->parent($main);
    $main->placeholderForAnotherTemplate = $subTemplate->run();
    echo $main; // or $main->run(); 
    
    // html.php
    <html><body><h1>Title <?= $name ?></h1><p><?= $placeHolderForAnotherTemplate ?></p></body></html>
    
        <?php
    // SimpleTemplate.php
    function object_to_array($object)
    {
        $array = array();
        foreach($object as $property => $value)
        {
            $array[$property] = $value;
        }
    
        return $array;
    }
    
    class SimpleTemplate
    {
        public $source;
        public $path;
        public $result;
        public $parent;
    
        public function SimpleTemplate($path=false, $source=false)
        {
            $this->source = array();
            $this->extract($source);
            $this->path($path);
        }
    
        public function __toString()
        {
            return $this->run();
        }
    
        public function extract($source)
        {
            if ($source)
            {
                foreach ($source as $property => $value)
                {
                    $this->source[$property] = $value;
                }
            }
        }
    
        public function parent($parent)
        {
            $this->parent = $parent;
        }
    
        public function path($path)
        {
            $this->path = $path;
        }
    
        public function __set($name, $value)
        {
            $this->source[$name] = $value;
        }
    
        public function __get($name)
        {
            return isset($this->source[$name]) ? $this->source[$name] : "";
        }
    
        public function mergeSource()
        {
            if (isset($this->parent))
                return array_merge($this->parent->mergeSource(), $this->source);
            else
                return $this->source;
        }
    
        public function run()
        {
            ob_start();
            extract ($this->mergeSource());
            include $this->path;
            $this->result = ob_get_contents();
            ob_end_clean();
            return $this->result;
        }
    }
    

    【讨论】:

    • 谢谢。这让我意识到,即使在大型框架中,您也应该在控制器中渲染视图,然后将渲染视图在变量中传递给顶层视图。我试图在另一个视图中渲染视图。
    【解决方案2】:

    好吧,您创建了一个新的类实例,因此嵌入式模板中没有定义任何变量。您应该尝试复制该对象,而不是创建一个新对象。

    编辑:我说的是工厂方法

    【讨论】:

      【解决方案3】:

      主要问题是您的观点彼此之间没有直接的了解。通过调用这个:

      <?=View::factory('embed')->output()?>
      

      在您的“父”视图中,您创建并输出一个模板,该模板不知道它位于另一个模板中。

      我可以在这里推荐两种方法。

      #1 - 关联您的模板。

      通过使您的嵌入式模板成为父模板的“子模板”,您可以允许它们在output() 时间访问父模板的变量。我在自己构建的 View 系统中使用了这种方法。它是这样的:

      $pView = new View_Parent_Class();
      $cView = new View_Child_Class();
      $pView->addView($cView);
      

      $pview-&gt;render() 时间,子视图可以轻松访问父视图。

      此方法可能需要对您进行大量重构,因此我将省略脏细节,并进入第二种方法。

      #2 - 传递父变量

      考虑到您目前采用的方法,这可能是最容易实现的方法。为你的输出方法添加一个可选参数,并稍微重写它,如下所示:

      public function output($extra_vars = null)
      {
          if (count($this->_vars))
              extract($this->_vars,  EXTR_REFS);
          if (is_array($extra_vars)) extract($extra_vars, EXTR_REFS);
          require($this->_file);
          exit;
      }
      

      如果你也添加一个简单的 getter 方法:

      public function get_vars()
      {
          return $this->_vars;
      }
      

      然后您可以将文件嵌入到对父变量的有效读取访问权限中:

      <?=View::factory('embed')->output($this->get_vars())?>
      

      $this 将是对当前模板的引用,即。父母。请注意,由于两个 extract 调用,您可能会通过此方法发生变量名冲突。

      【讨论】:

        【解决方案4】:

        您可以使您的 $_vars 属性静态,不是特别优雅,但可以为您想要实现的目标工作。

        附带说明...您在 set() 函数中的 array_merge() 是错误的,请交换您的 2 个变量。

        【讨论】:

        • 怎么会出错呢?它将 2 个数组合并在一起。这就像说 2+3 != 3+2
        • 传递给 set() 函数的变量不应该覆盖旧的吗?目前,如果你调用 $view->set(array('title' => 'Title1'))->set(array('title' => 'Title2'))->output(); // 标题将是 Title1
        猜你喜欢
        • 2012-05-01
        • 1970-01-01
        • 2010-12-24
        • 2015-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多