Wtf!

Today i observed some obscure behavior in an application. I’m sure many of you have similar stories to tell. Why not collec them here in this thread?

[size="2"]Here goes mine:[/size]

In one of my apps we planned a new feature. The feature should not be launched before a specific date. But still we had to test it on our stage machine and also didn’t want to block other waiting deployments on production. So I thought, let’s use an application parameter as feature flag, to turn on/off the feature on every installation through the config file. I added a menu entry in the main menu like this:




<?php $this->widget('zii.widgets.CMenu', array(

    'items' => array(

        array(

            'label' => 'Some feature',

            'visible' => Yii::app()->params['somefeature'],

        ),



So the menu item would not be enabled unless i add ‘somefeature’ => true to the params section of my configuration file. I tested locally, set the ‘somefeature’ flag to true and false and everything worked. Then i deployed to the live site and soon after got a phone call, why I already activated the new feature there. I thought that’s impossible: I did not add anything at all to the config file there. So i double checked: Indeed, no ‘somefeature’ in the params section in the live config.

So why the heck was the menu item visible?

I debugged locally and removed the ‘somefeature’ param. Yii::app()->params[‘somefeature’] gave null and null should be equivalent to false, right?

Wrong! Not in this case. I had to dig into the source of CMenu.php to find out what’s going on:




<?php


    protected function normalizeItems($items,$route,&$active)

    {

        foreach($items as $i=>$item)

        {

            if(isset($item['visible']) && !$item['visible'])

            {

                unset($items[$i]);

                continue;

            }

            ...



So the condition in the if seem to evaluate to false, even though there’s a $item[‘visible’]? Well, the problem is that the value is null and isset() returns false for null values. So i first tried to typecast to bool with b Yii::app()->params[‘somefeature’][/b] - but that still didn’t help (no clue, why). Only this (kind of ugly) solution worked in the end:





        array(

            'label' => 'Some feature',

            'visible' => Yii::app()->params['somefeature'] ? true : false,

        ),



Case closed :)

I think that




        array(

            'label' => 'Some feature',

            'visible' => Yii::app()->params['somefeature'] ? true : false,

        ),



it’s not completely correct.

Infact "somefeature" key could not be setted.

So i think that correct code should be:




        array(

            'label' => 'Some feature',

            'visible' => isset(Yii::app()->params['somefeature']) ? (Yii::app()->params['somefeature']===true) : false,

        ),



or you can use




array_key_exists('somefeature', Yii::app()->params) 



instead




isset(Yii::app()->params['somefeature'])



Well, it was not set in my case. Yii allows you to look for parameters that don’t exist - no error in this case. It only gives you null. That’s why i prefer the shorter code in my solution.

I think that it’s not so, because you are calling direcly params array, without passing for an Yii method.

So you should check if key exists. I think that you are missing warning display errors.

Warnings are turnded on :). If you access Yii::app()->params you get back an CAttributeCollection which extends from CMap and implements ArrayAccess. Thus you can use it like an array, but it does not necessarily have the same error behavior. In the above case it calls offsetGet() which in turn calls itemAt() - and this simply returns null if a key does not exist. No error. So it’s valid to access non existant keys in the params array. You’ll just get null back.

Ok, then you are right. i I thought that params was an array and not and object.

I love this wiki article.

The Comedy of Errors

The latest “comedy” (or “wtf!”) has been mine. :lol:

I quote completely! :rolleyes:

Heh, didn’t know that. Nice :)