当foreach中的函数遇到一个非常好的数值

For some reason I keep getting this notice when I try to loop through an array and change it's values.

For some reason, the array is passing 2 values, the one before and the one after when running the same function that changes the values.

class UnitConverter {

    public $vulgar_to_float = array('½' => '1/2');

    public function replaceUnicode($amount){
        foreach($this->vulgar_to_float as $key => $float){
            if(is_numeric($amount)){
                return $amount;
            } else if($key === $amount){
                return $float;
            }
        }
    }

    public function convertAmount($amount, $from, $to){
        if($from === 'pound' && $to === 'ounce'){
            return $amount * 16;
        } else if($from === 'cup' && $to === 'tablespoon'){

            print_r($amount); // here it's echoing 2 values when it should be 1

            return $this->replaceUnicode($amount) * 16;
        } else {
            throw new \Exception('Unable to convert ' . $from . ' to ' . $to);
        }
    }
}


function convertIngredients($arr){
    foreach($arr as $key => $value){
        if($arr[$key]['unit_name'] === 'pound'){
            $arr[$key]['amount'] = (new UnitConverter())->convertAmount($arr[$key]['amount'], 'pound', 'ounce');
            $arr[$key]['unit_name'] = 'ounce';
        } else if($arr[$key]['unit_name'] === 'cup'){
            $arr[$key]['amount'] = (new UnitConverter())->convertAmount($arr[$key]['amount'], 'cup', 'tablespoon');
            $arr[$key]['unit_name'] = 'tablespoon';
        }
    }

    return $arr;
}


function generateBreakfast(){
    $array = $var = array(
        0 => array( 'amount' => 1, 'ingredient_name' => 'almond flour', 'unit_name' => 'cup' ),
        1 => array( 'amount' => ½, 'ingredient_name' => 'erythritol', 'unit_name' => 'cup' ),
        2 => array( 'amount' => 1, 'ingredient_name' => 'egg', 'unit_name' => 'large' )
    );

    $converted_ingredients = convertIngredients($array);

    return $converted_ingredients;
}

echo '<pre>';
print_r(generateBreakfast());
echo '</pre>';

So in the convertIngredients, we're calling the convertAmount method, but for some reason there.. it's passing this '1½' instead of just calling the method individually with each iteration.

If you take a look here: https://eval.in/944452 , the amount in erythritol is showing 16, but it should be 8 because 1/2 of 16 = 8.

Replace vulgar_to_float with...

public $vulgar_to_float = array('½' => 0.5);

... and it should work.

As it stands, 16 * '1/2' expression is evaluated by the code. You probably hope that PHP automatically 'resolves' the second operand to a correct numeric value. But it doesn't, as the parsing rules (applied to a string in attempt to cast it to a number) don't treat '/' character in any special way - hence it's discarded, along with the rest of string.

That means, value of 16 * '1/2' is essentially equal to 16 * 1 - which is, as you clearly noticed, 16.

The change prevents this error - and makes life for PHP a little bit easier: if you're going to get a float as replaceUnicode return value, use floats in your map right from the start so that the engine won't have to spend time on type juggling.

Kudos to @raina77ow for indicating the necessary correction. I went ahead and reviewed the code and altered it as follows:

<?php

class UnitConverter {

    public $vulgar_to_float = ['½' => 0.5];

    public function replaceUnicode( $amount = 0 ){
        $half = $this->vulgar_to_float['½'];
        return ( is_numeric( $amount ) )? $amount : $half;
    }

    public function convertAmount($amount, $from, $to){

        if($from === 'pound' && $to === 'ounce'){
            return $amount * 16;
        } else if($from === 'cup' && $to === 'tablespoon'){
            return $this->replaceUnicode( $amount ) * 16;
        } else {
            throw new Exception('Unable to convert ' . $from . ' to ' . $to);
        }
    }
}


function convertIngredients( $arr = null){
    $unit_names = array_column($arr, 'unit_name');
    foreach($unit_names as $key => $unit){
        if( $unit === 'pound'){
            $arr[$key]['amount'] = (new UnitConverter())->convertAmount($arr[$key]['amount'], $unit, 'ounce');
            $arr[$key]['unit_name'] = 'ounce';
        } else if($unit === 'cup'){
            $arr[$key]['amount'] = (new UnitConverter())->convertAmount($arr[$key]['amount'], $unit, 'tablespoon');
            $arr[$key]['unit_name'] = 'tablespoon';
        } else {
          continue;
        }
    }

    return $arr;
}


function generateBreakfast(){
    $array = [
        0 => array( 'amount' => 1, 'unit_name' => 'cup', 'ingredient_name' => 'almond flour'  ),
        1 => array( 'amount' => '½', 'unit_name' => 'cup', 'ingredient_name' => 'erythritol' ),
        2 => array( 'amount' => 1, 'unit_name' => 'large', 'ingredient_name' => 'egg' )
    ];
    return convertIngredients($array);
}

echo '<pre>';
print_r(generateBreakfast());
echo '</pre>';

See live code

There was a place in the code that needed quotes around the unicode one-half symbol. Also, I tried to eliminate unnecessary verbosity. One code statement creates a variable and then returns it whereas this code returns the value. I used a ternary statement and I used array_column() to pinpoint the unit names in a more straightforward fashion.

And, for the cooks among us, more straightforward to show the unit name and the amount followed by the ingredient :)