Why Yii Calls Login() Method, When Creating Login Model

Following my previous question, I discovered, that in every of my Yii project (where login code was auto-generated by Yii upon initial application generation and not changed by me), Yii executes login() method from Login form model, when this model is created. Why? What is the purpose?

I’ve analysed step-by-step execution of application flow (with Netbeans + XDebug + NetBeans Connector) and it seems, that when creating Login model / class ($model = new Login;) Yii autoloads two classes (Login and CFormModel) and right after loading the second one, it jumps directly to login() method execution. No steps in between.

This is the application flow, that I observed (I’m always using Step Into (F7) in Netbeans):


1. $model = new Login; in actionLogin() (since Yii::app()->user->isGuest is true).


2. if(isset(self::$classMap[$className])) in autoload() in YiiBase.php (line 396).


3. if(strpos($className,'\\')===false) in the same method (line 403).


4. if(self::$enableIncludePath===false) as above (line 405).


5. { below the begining of class Login extends CFormModel definition.


6. Same as point 2. above.


7. { below the begining of class CFormModel extends CModel definition.


8. return true; in autoload($className) in YiiBase.php (line 435).


9. return class_exists($className,false) || interface_exists($className,false); in the same method (line 433).


10. if($this->_identity === NULL) in login() method in Login model (class).

(all lines referes to YiiBase.php class)

Can someone explain me, why Yii is executing login() method upon creation of Login form model?

In more details – what is the purpose of attempting to login user, if there no data, that can be used in login process – $this->login and $this->password are null, there was no form ever generated, so user hasn’t got chance to enter anything, no POST data was ever submitted ($_POST is empty, $_POST[‘Login’] is undefined)?

The best part goes after that. If you run application (this or any auto-generated by Yii with unchanged login code), nothing happens – Yii correctly displays login form, though login attempt, that proceeded render for sure failed. But, if you debug application step-by-step, no form is ever rendered, application flow is terminated and script execution dies on: Property "CWebApplication._identity" is not defined and Property "CHttpSession._identity" is not defined exceptions. Something, that we could expect, since CWebApplication._identity is undefined in this context.

Can someone explain me, what is going on here?

Well I don’t see it in my LoginForm Model what version are you using? may be if you can paste your code that might help

I don’t think it’s Yii, seems more like a debugger problem/bug: the debugger probably doesn’t recognize that Login class inherits __construct() method from CFormModel and mistakenly identifies the method with the same name as the class (login) as constructor.

When the code runs without debugger __construt() method has priority over login() and everything works as expected.

Hi Trajder

On your first line you wrote that the login code is autogenerated by Yii and that you did not change it at all… but from your code


$model = new Login;

I would say you changed something there as Yii by default uses the code


$model = new LoginForm;

Sorry, for not pointing this out. I’m using Yii 1.1.12. And here you go. entire code.

MainController.php controller’s actionLogin() action:


public function actionLogin()

{

	$this->layout = '//layouts/backend/login_inner';


	if(Yii::app()->user->isGuest)

	{

    	$model = new Login;


    	if(isset($_POST['ajax']) && $_POST['ajax'] === 'login-form')

    	{

        	echo(CActiveForm::validate($model));


        	Yii::app()->end();

    	}


    	if(isset($_POST['Login']))

    	{

        	$model->attributes = $_POST['Login'];


        	if($model->validate() && $model->login()) $this->redirect(Yii::app()->user->returnUrl);

    	}


    	$this->render('login', array('model'=>$model));

	}

	else

	{

    	Yii::app()->user->setFlash('info', 'Użytkownik jest już zalogowany! Kliknij '.(CHtml::link('tutaj', array('/main/logout'))).', by się wylogować...');


    	$this->render('index');

	}

}

Login.php model’s login() method:


public function login()

{

	if($this->_identity === NULL)

	{

    	$this->_identity = new UserIdentity($this->login, $this->password);

    	$this->_identity->authenticate();

	}


	if($this->_identity->errorCode === UserIdentity::ERROR_NONE)

	{

    	$duration = $this->remember ? 3600 * 24 * 30 : 0;


    	Yii::app()->user->login($this->_identity, $duration);


    	return TRUE;

	}

	else return FALSE;

}

}

and UserIdentity.php class’ authenticate() method:


public function authenticate()

{

	$user = Users::model()->find('LOWER(login)=?', array(strtolower($this->username)));


	if($user !== NULL)

	{

    	if($user->password === md5($this->password))

    	{

        	if($user->state > 0)

        	{

            	$this->_id = $user->id;


            	/**

     			* Access this via Yii::app()->user->level and Yii::app()->user->email

     			*/

            	$this->setState('level', $user->level);

            	$this->setState('email', $user->email);


            	$this->errorCode = self::ERROR_NONE;

        	}

        	else $this->errorCode = self::ERROR_USER_INACTIVE;

    	}

    	else $this->errorCode = self::ERROR_PASSWORD_INVALID;

	}

	else $this->errorCode = self::ERROR_USERNAME_INVALID;


	return !$this->errorCode;

}

As you may see, changes are minor and in most cases that means changing names, not the code. Approximately 95% of the code is the same as from auto-generated Yii app.

The debugger has nothing to do with this. This problem can be very easily spotted without using any debugger. Do, what I did:

  1. Put die(‘Hello!’); as first line of your LoginForm.php model’s login() method, run your application and try to request any action, that requires login. Instead of login form you will see your “Hello!” message. Because LoginForm::login() method has been executed in this scenario.

  2. Try to comment line $model = new LoginForm; (fifth) in SiteController.php controller’s actionLogin() method and run your application again. You’ll see an exception, that $model is undefined, but you won’t see your die() message, as LoginForm::login() method hasn’t been executed in this scenario.

When this code runs without debugger, nothing works as expected. See above examples.

If you still don’t believe, do another test, that I did. I changed my users table (removed login column), which is used by login() and authenticate() methods of LoginForm model and UserIdentity. I run my application and instead of seeing login form I saw an exception about incorrect SQL executed, looking for non-existing login column. How do you explain that? Yii should create new model and render login form (all other code from above presented actionLogin() won’t be executed, because $_POST is empty), but instead of that, at some point, somehow, without any reason, is doing a regular login, preparing and executing SQL statement and die on it.

It has nothing to do with the debugger, as it happens in normal application execution, on test or even production server.

Conlusion: Yii is calling login() method, when your code executes $model = new LoginForm. Amen. Nothing to add. Correct me, where I’m wrong.

Hi Maurizio,

Please, don’t kill me! :] That was only a conceptual design changes! I renamed LoginForm.php to Login.php, because I have all my form models in separate subfolder and they don’t need to be named like that. And I renamed SiteController.php with MainController.php, because I don’t like “Site” name at all! :]

It has nothing to do with the code! As you can see in the code examples, that I posted to alirz23’s question, my code is 95-97% similar to Yii-generated one. Some methods are unchanged at all, some has minor, unrelated changes.

See tests / examples, I did and wrote for phtamas’ question. Try to implement them in any of yours Yii application. Either I’m blind or extremely wrong, or Yii executes login() method, when $model = new LoginForm is invoked.

[size="2"]You now my prefered drill in case of any doubt if the error is from Yii or custom code:[/size]

[size="2"]

[/size]

[size="2"]In your case the problem is the so simple class rename from LoginForm to Login… [/size]

[size="2"]p[/size][size="2"]lease check here - [/size][size="2"]http://stackoverflow…me-as-class-has[/size]

The strange thing is that Login inherits __construct() from CFormModel and it should be prioritized over login() by PHP interpreter.

OMFG! :expressionless: It was that obvious? :expressionless: Thanks, anyway…