在使用输出缓冲的 PHP 函数中使用 extract()

Using extract() in PHP function that uses output buffering

我目前正在上一门编码初学者课程,主要侧重于 PHP,在一个练习中,我们正在更改我们的代码,从通过正常命名空间包含模板改为通过函数包含它,以便我们可以使用输出缓冲。为了让它工作,我们使用了 extract() ,虽然我理解 extract 是如何工作的,但我很难理解为什么我们需要使用它来使 include 工作。在通过函数 运行 之前,我们不需要发送或提取新变量。有人能够解释这背后的原因吗?

函数如下所示:

const TEMPLATE_EXTENSION = '.phtml';
const TEMPLATE_FOLDER = 'templates/';
const TEMPLATE_PREFIX = 'cart_view_';

function display($template, $variables, $extension = TEMPLATE_EXTENSION) {
    extract($variables);

    ob_start();
    include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
    return ob_get_clean();
}

我们是这样称呼它的:

<?php echo display('user', ['users' => $users, 'cart' => $cart]); ?>
<?php echo display('item', ['new_item' => $new_item]); ?>
<?php echo display('items', ['cart' => $cart]); ?>

下面是我们包含的模板中的内容:

<h2>New Item</h2>
    <p><?php printf($new_item['name']);?> is $<?php printf($new_item['price']);?></p>

<h2>User: <?php printf($cart['user']); ?></h2>
    <p>ID: <?php printf($users[$cart['user']]['id']); ?></p>
    <p>Email: <?php printf($users[$cart['user']]['email']); ?></p>

<h2>Cart</h2>

<?php foreach ($cart['items'] as $item) {

    printf("<p>%s is $%d</p>\n", $item['name'], $item['price']);

} ?>

变量在索引中已包含的另一个文件中定义。 以前在使用函数缓冲之前,我们只需要这样:

<?php include 'templates/cart_view_user.phtml'; ?>
<?php include 'templates/cart_view_item.phtml'; ?>
<?php include 'templates/cart_view_items.phtml'; ?>

函数确实有自己的变量范围。因此,当您创建 display() 函数时,它看不到它之外有哪些变量可用。在 Variable Scopes in PHP 上查看更多信息。您正在使用 extract() 以便将数组转换为您从例如函数中调用它的范围内的变量。

你的第一个解决方案可能是这样的(我正在弥补变量):

$users = [];
$cart = [];

// you are including the template in the same scope as the variables are defined, aka it will "see"/have access to those variables
include 'templates/cart_view_user.phtml'; 

现在您已经重构了代码并将包含逻辑移动到函数中。这个函数有自己的本地作用域。

$users = [];
$cart = [];

function display($template, $variables, $extension = TEMPLATE_EXTENSION) {
    // you don't have access to $users and $cart here as those are defined in the global scope
    extract($variables); // <-- after this call you will have new variables created in the local function scope based on your $variables array

    ob_start();
    include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
    return ob_get_clean();
}

所以现在你有两个选择,如果你知道你想在函数中使用哪些变量,你可以使用 global 关键字,但我不鼓励使用它,它可能会导致奇怪的错误你不明白为什么你的变量会被改变(ps 之后你将继续使用 类 而你不会为全局变量而头疼)。

// you can drop $variables from the function signature
function display($template, $extension = TEMPLATE_EXTENSION) {
    global $users, $cart, $new_item;
    // no extract needed, but please try not using global, it can lead to weird bugs

    ob_start();
    include TEMPLATE_FOLDER . TEMPLATE_PREFIX . $template . $extension;
    return ob_get_clean();
}

或者您可以使用 extract 在函数范围内创建变量,这样当您包含文件时,它们就可以访问这些变量。这里要注意的是,extract 将使用数组键作为它创建的变量名。这就是您在此调用后传入 display('user', ['users' => $users, 'cart' => $cart]); 的原因,extract 将在函数调用内创建一个 $users 和一个 $cart 变量。如果您使用不同的数组键调用它,例如:display('user', ['u' => $users, 'c' => $cart]); 包含的文件会抱怨找不到变量 $users$cart.

我希望这对您有所帮助,如果我哪里不清楚,请随时询问更多。