Search code examples
phparraysreturnnotice

How to stop PHP notice: Undefined index | array_map | return


I always get a PHP notice in my error log because of a little language function I am using. Somehow I can't get it right...

My languages array looks like this (EDITED):

    $dict = [
        'title' => [
            'en' => 'Welcome',
            'de' => 'Willkommen'
        ],
        'description' => [
            'en' => 'Hello',
            'de' => 'Hallo'
        ],
    ];

And this is my language function (EDITED):

function lang($phrase){
    global $l, $dict;

    return $dict[$phrase][$l];
}

$l is defined before the $dict array (as "de" or "en").

In my document I am doing:

<?php echo lang('title');?>

And the PHP notice refers to the "return $dict[$phrase][$l];" part. I don't really understand why. I tried different things and nothing works.

EDIT:

Oh, damn. After posting I finally realized I where using a phrase which is not in my language array anymore. Somehow I was blind. Nevertheless the post was good to clean up my function. Thanks!


Solution

  • As noted my comments on the original post, after fixing the missing bracket (assuming typo) in your array declaration, the function works correctly, if called correctly as echo lang('title'). If you get a notice on Undefined index, it means the $phrase you're looking for doesn't exist in the $dict array. (If you only get the notice in your error logs, you may want to turn on on-screen error reporting when you're developing to catch these in real time!)

    Basic debugging aside, let me help you tidy up this code a bit. First, $l or language can be passed in as a function argument. Second, the $dict doesn't need to be a variable, assuming you're not modifying it on the fly. You can declare it as a constant and avoid globals. Third, your array mapping is unnecessary overhead, when you can simply call the relevant indexes in the array. A minimal clean-up would then look something like this:

    const DICT = [
        'title' => [
            'en' => 'Welcome',
            'de' => 'Willkommen'
        ],
        'description' => [
            'en' => 'Hello',
            'de' => 'Hallo'
        ]
    ];
    
    function lang($phrase, $lang) {
        // If you're using PHP 7, the null-coalescing operator is handy here:
        return DICT[$phrase][$lang] ?? "Phrase {$phrase} Unknown!";
    }
    

    You'd use it like this:

    // Sample usage: Output title in English and description in German:
    
    echo lang('title', 'en') . ' ' . lang('description', 'de');
    
    // Or, if the language variable is pre-defined, then e.g.:
    
    $l = 'en';
    echo lang('title', $l) . ' ' . lang('description', $l);
    

    Here, the use of a constant for your dictionary is a basic but very efficient way to handle a global need for pre-defined data. Constants are parsed at compile-time, while variable declarations are parsed at runtime. They are available in all contexts. And you definitely don't want to have the whole $dict inside your function just to avoid a global declaration!

    A more elegant way to handle this would be to create a class, which will allow you to avoid the language function parameter. (Class constants and properties are like "local globals", available to all methods in the class.) Here's a sample application of your case as a class, where the dictionary is a class constant, and the language is a class property:

    class Dict {
    
        const DICT = [
            'title' => [
                'en' => 'Welcome',
                'de' => 'Willkommen'
            ],
            'description' => [
                'en' => 'Hello',
                'de' => 'Hallo'
            ]
        ];
        
        public static $lang = 'en'; // for a default language
        
        public static function phrase($phrase)
        {
            return self::DICT[$phrase][self::$lang] ?? "Phrase {$phrase} Unknown!";
        }
    
    }   
    
    Dict::$lang = 'de'; // Define language
    
    echo Dict::phrase('title') . ' ' . Dict::phrase('description');
    // : Willkommen Hallo
    

    If you expect to have a large dictionary that needs to be accessed in other contexts, or simply wish to centralize your language reference, then you may want to define it as a regular constant outside the class, as in the first example. I've used a class constant here mostly for educational purposes. Hope this helps you forward and towards cleaner code.