Немного про замыкания в PHP?

Добрый день. Хотелось уточнить некоторые моменты в замыканиях на PHP, А именно с использованием амперсанда на переменных. см код ниже
<?php
// Базовая корзина покупок, содержащая список добавленных
// продуктов и количество каждого продукта. Включает метод,
// вычисляющий общую цену элементов корзины с помощью
// callback-замыкания.
class Cart
{
    const PRICE_BUTTER  = 1.00;
    const PRICE_MILK    = 3.00;
    const PRICE_EGGS    = 6.95;

    protected $products = array();
    
    public function add($product, $quantity)
    {
        $this->products[$product] = $quantity;
    }
        
    public function getTotal($tax)
    {
        $total = 0.00;
        
        $callback =
            function ($quantity, $product) use ($tax, &$total)
            {
                $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                    strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };
        
        array_walk($this->products, $callback);
        return round($total, 2);
    }
}

$my_cart = new Cart;

// Добавляем несколько элементов в корзину
$my_cart->add('butter', 1);
$my_cart->add('milk', 3);
$my_cart->add('eggs', 6);

// Выводим общую сумму с 5% налогом на продажу.
print $my_cart->getTotal(0.05) . "\n";
// Результатом будет 54.29
?>


Я так понимаю, что &$total ссылается на переменную внутри функции, но какова область видимости такой ссылки, можно ли использовать эту переменную вне функции getTotal ? Ибо в JS если переменная в функции не найдена, она ищет её еще выше вверх по ирархии до Windows. А каковы границы тут ?

UPD: И ясное дело, что сумма сохраняется при каждой итерации array_walk и сбрасывается до 0.00 при создании нового объекта.
  • Вопрос задан
  • 3400 просмотров
Решения вопроса 1
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
А каковы границы тут ?


А тут никто никуда не ходит вообще. У вас есть скоуп функции и наружу вы ходить не можете. Вы можете снаружи прокидывать нужные переменные в скоуп вашего замыкания (use ($tax, &$total)). То есть вы просто даете вашему замыканию попользоваться этими переменными.

Вернемся к "амперсанту". По умолчанию же все переменные передаются по значению, что означает что наша анонимная функция не может их изменить, туда просто скопируются значения. Амперсант же означает, что переменная $total будет передана внутрь замыкания по ссылке. В итоге вы меняеете эту переменную.

p.s.
Но в вашем случае в этом нет ровным счетом никакого смысла. Если вам надо просто сумму посчитать - вам стоит использовать функцию array_reduce, правда тогда придется изменить еще и способ, как вы храните ваши "продукты". А еще вот эта штука:

$pricePerItem = constant(__CLASS__ . "::PRICE_" .
                    strtoupper($product));


Попахивает "дурными решениями". Пощадите тех, кто будет работать с вашим кодом через пол года (даже себя самого). Код должен быть не "коротким" а "понятным". Код чаще читают чем пишут. Все должно быть не двусмысленно, выражать что вы хотели сделать и не требовать комментариев (а все что требует комментариев выносится в приватные методы с адекватным названием).
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы