Solucionado (ver solução)
Solucionado
(ver solução)
1
resposta

foreach e escopos

Há alguma diferenciação entre escopos dentro de um laço foreach? Estou encontrando um comportamento inesperado no seguinte código:

<?php

function saque($conta,$saque){
    if($conta['saldo']>=$saque) {
        echo " - Sacado";
        $conta['saldo']-=$saque;
    }else
        echo ' - saldo insuficiente.';
    return $conta['saldo'];
};

$contas = [
    '022.011.333-12'=>[
        'nome'=>'Alberto',
        'saldo'=>500
    ],
    '356.722.8332-12'=>[
        'nome'=>'Julia',
        'saldo'=>200
    ],
    '963.720.639-08'=>[
        'nome'=>'Rodrigo',
        'saldo'=>8000
    ],
];
foreach ($contas as $cpf=>$conta){
    echo $cpf." - Nome: ".$conta['nome']." / Saldo R$: ".$conta['saldo'].PHP_EOL;
};

echo "------------------------------------------------------------------------".PHP_EOL;

foreach ($contas as $conta){
    echo "Iterado para ".$conta['nome'];
    $conta['saldo'] = saque($conta,500);
    echo " / Saldo: ".$conta['saldo'].PHP_EOL;
};

echo "------------------------------------------------------------------------".PHP_EOL;

foreach ($contas as $cpf=>$conta){
    echo $cpf." - Nome: ".$conta['nome']." / Saldo R$: ".$conta['saldo'].PHP_EOL;
};

Onde, após a manipulação dos saldos, dentro do segundo laço, os valores 'resetam' para os originais do array:

php global.php
022.011.333-12 - Nome: Alberto / Saldo R$: 500
356.722.833212 - Nome: Julia / Saldo R$: 200
963.720.639-08 - Nome: Rodrigo / Saldo R$: 8000
------------------------------------------------------------------------
Iterado para Alberto - Sacado / Saldo: 0
Iterado para Julia - saldo insuficiente. / Saldo: 200
Iterado para Rodrigo - Sacado / Saldo: 7500
------------------------------------------------------------------------
022.011.333-12 - Nome: Alberto / Saldo R$: 500
356.722.833212 - Nome: Julia / Saldo R$: 200
963.720.639-08 - Nome: Rodrigo / Saldo R$: 8000

Por que isto está acontecendo? No terceiro laço os valores impressos deveriam ser os já manipulados e não os originais como está ocorrendo, ou esta é uma avaliação errada de minha parte?

Eu sei que poderia resolver esta situação declarando o segundo laço desta forma (após alterar o retorno da função):

foreach ($contas as $cpf=>$conta){
    echo "Iterado para ".$conta['nome'];
    $contas[$cpf] = saque($conta,500);
    echo " / Saldo: ".$contas[$cpf]['saldo'].PHP_EOL;
};

Porém gostaria de entender as razões para a interpretação do código se comportar desta forma.

1 resposta
solução!

Oi Leonardo, beleza?

Então... Esse comportamento é esperado por que o PHP realiza a passagem por valor e não por referência com os arrays. Para explicitar que você quer realizar a passagem por refêrencia e assim mudar o valor original do array você deve usar o simbolo "&".

Só que eu não sugiro trabalhar com referencias, principalmente dentro de foreach, por que o PHP tem alguns comportamentos bem estranhos nesse casos, fazendo-se necessário o artíficio de algumas gambiarras no código.

Mas caso tenha a curiosidade deixe seu código com essas alterações:


<?php

function saque(&$conta,$saque){
    if($conta['saldo']>=$saque) {
        echo " - Sacado";
        $conta['saldo']-=$saque;
    }else
        echo ' - saldo insuficiente.';
    return $conta['saldo'];
};

$contas = [
    '022.011.333-12'=>[
        'nome'=>'Alberto',
        'saldo'=>500
    ],
    '356.722.8332-12'=>[
        'nome'=>'Julia',
        'saldo'=>200
    ],
    '963.720.639-08'=>[
        'nome'=>'Rodrigo',
        'saldo'=>8000
    ],
];
foreach ($contas as $cpf=>$conta){
    echo $cpf." - Nome: ".$conta['nome']." / Saldo R$: ".$conta['saldo'].PHP_EOL;
};

echo "------------------------------------------------------------------------".PHP_EOL;

foreach ($contas as &$conta){
    echo "Iterado para ".$conta['nome'];
    $conta['saldo'] = saque($conta,500);
    echo " / Saldo: ".$conta['saldo'].PHP_EOL;
};

unset($conta);
echo "------------------------------------------------------------------------".PHP_EOL;

foreach ($contas as $cpf=>$conta){
    echo $cpf." - Nome: ".$conta['nome']." / Saldo R$: ".$conta['saldo'].PHP_EOL;
};

Caso você se pergunte do por que eu tive que usar um unset na variável conta tente executar o mesmo código sem o unset rsrs. Para mais informações tem essa ótima referência: https://schlueters.de/blog/archives/141-references-and-foreach.html