【问题标题】:PHP - architecture, issue with variables scopePHP - 架构,变量范围的问题
【发布时间】:2013-03-03 10:33:57
【问题描述】:

我现在正在编写一个应用程序,它应该尽可能简单。我的控制器实现了 render($Layout) 方法,它只执行以下操作:

public function render($Layout) {
  $this->Layout = $Layout;
  include( ... .php)
}

我不太明白以下问题:在我的控制器中,我定义了一个变量:

public function somethingAction() {
  $someVariable = "something";
  $this->render('someLayout');
}

在 php 视图文件中(与我在渲染函数中包含的相同)我尝试回显变量,但什么也没有。但是,如果我这样声明我的操作方法:

public function somethingAction() {
  global $someVariable;
  $someVariable = "something";
  $this->render('someLayout');
}

在我看来是这样的:

global $someVariable;
echo $someVariable;

确实有效。每次都写global这个词还是很烦人。

我该怎么做?我宁愿不使用 $_GLOBAL 数组。例如,在 Cake PHP 中,有一个类似 $this->set('varName', $value) 的方法。然后我可以以 $varName 的形式访问视图中的变量。它是如何完成的?

【问题讨论】:

  • 您是否将控制器包含在视图中并将模型包含在控制器中?

标签: php global-variables scope


【解决方案1】:

来自PHP Manual on include:

当一个文件被包含时,它包含的代码继承了包含发生的行的变量范围。从那时起,调用文件中该行的任何可用变量都将在被调用文件中可用。但是,包含文件中定义的所有函数和类都具有全局范围。

当你这样做时

public function somethingAction() {
  $someVariable = "something";
  $this->render('someLayout');
}

$someVariable 变量仅限于 somethingAction() 范围。调用render() 方法不会神奇地使render() 中的变量可用,因为render() 方法有它自己的变量范围。一个可能的解决方案是做

public function somethingAction() {
  $this->render(
      'someLayout', 
      array(
          'someVariable' => 'something'
      )
    );
}

然后将render()改为

public function render($Layout, array $viewData) {
    $this->Layout = $Layout;
    include( ... .php)
}

然后您将可以访问包含文件中的$viewData,前提是您没有尝试在其他功能或方法中使用它,例如如果您包含的文件如下所示:

<h1><?php echo $viewData['someVariable']; ?></h1>

它会起作用,但如果它看起来像这样:

function foo() {  
    return $viewData['someVariable']; 
}
echo foo();

它不会起作用,因为foo() 有它自己的变量范围。

但是,a controller's sole responsibility is to handle input。渲染是视图的责任。因此,您的控制器根本不应该有 render() 方法。考虑将该方法移至您的 View 类,然后执行

public function somethingAction() {
  $view = new View('someLayout');
  $view->setData('someVariable', 'something');
  $view->render();
}

View 对象的render() 方法可以这样实现:

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

extract 函数将使用它们的键作为名称在当前范围内导入数组的值。这将允许您将viewData 中的数据用作$someVariable 而不是$this-&gt;viewData['someVariable']。不过,请确保在使用之前了解 extract 的安全含义。

请注意,这只是您当前做事方式的一种可能替代方案。您也可以将视图完全从控制器中移出。

【讨论】:

  • 是的,您的解决方案非常好,我就是这样做的。我只是通过使用 foreach 循环 $viewData 并执行 foreach ($viewData as $k => $v) { $$k = $v; }。所以加倍美元以使变量可以通过其名称访问。谢谢。
  • @BarthZalewski 你的foreach 做了extract 做的事情。
  • @BarthZalewski 检查extract 上的文档,使用前缀参数'$' 你可能不需要额外的for 循环。为@Gordon +1,你赢了 ;)
【解决方案2】:

使用global,您隐含使用$_GLOBALS 数组。

一个不推荐的例子,因为全局变量从来都不是一件好事:

function something() {
  global $var;
  $var = 'data';
}

// now these lines are the same result:
echo $_GLOBALS['var'];
global $var; echo $var;

你为什么不简单地使用 $this->set 函数?

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;

很抱歉没有详细了解您的框架,但这完全有道理。

实际上是怎么做的:有一个原生的extract函数,它把一个关联数组加载到当前符号表中:

array( 'var0' => 'val0', 'var1' => 'val1')

变成

echo $var0; // val0
echo $var1; // val1

这很可能是“发生了什么”。

【讨论】:

    猜你喜欢
    • 2016-08-30
    • 2011-04-10
    • 2011-08-11
    • 2013-01-14
    • 1970-01-01
    • 1970-01-01
    • 2011-11-21
    • 2011-04-13
    • 2011-10-20
    相关资源
    最近更新 更多