We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Custom DI not working as expected

Hello,

I've been messing around with Phalcon for a bit now and I thought it was time to have a custom DI in my application.

When a user is logged in to my application, I want the users model to be available all over my application so I have it handy when I need it.

In the end I want to achieve something like $this->user->id, $this->user->isLoggedIn() etc

I have a multi-module application and in my Module.php I have the $di set like so:

$di->set('user', function(){
    return new WebUser();
});

Pretty straight-forward.

In my WebUser this is the main part that should be responsible for returing me User model:

class WebUser extends \Phalcon\Mvc\User\Plugin
{

    public $user = null;

    const AUTH_UNKNOWN = 1;
    const AUTH_SUCCESS = 2;
    const AUTH_WRONG_CREDENTIALS = 3;
    const AUTH_ATTEMPTS_EXCEEDED = 4;
    const AUTH_BANNED = 5;
    const AUTH_NOT_FOUND = 6;

    public function __construct(){
        if($this->user == null)
            $this->user = $this->register();

        return $this->user;
    }

    public function register(){
        if($this->session->has('vdi') && $this->session->has('vcf')){
            if(!$user = $this->check($this->session->get('vdi'), $this->session->get('vcf'))){
                $this->logger->notice("Session intergity check FAIL");
                $this->session->destroy();
                $this->persistent->user = null;
            } else {
                $this->logger->debug("Session intergity check OK");
                $this->persistent->user = $user;
            }
        }

        return $this->persistent->user;
    }
    ...

The problem though is that the user model is accessible if I do $this->user->user->id which is not what I'm really wanting. I'm assuming this is due to the public $user.

How could I have the user modal accessible without the extra user between and access it like $this->user->id and also be able to use all my functions in WebUser?

Thank-you in advance

You could do it with the magic __get method:

class WebUser extends \Phalcon\Mvc\User\Plugin
{
    private $user = null;

    public function __get($name) {
        return $this->user->{$name};
    }

    public function register(){
        if($this->session->has('vdi') && $this->session->has('vcf')){
            if(!$user = $this->check($this->session->get('vdi'), $this->session->get('vcf'))){
                $this->logger->notice("Session intergity check FAIL");
                $this->session->destroy();
                $this->persistent->user = null;
            } else {
                $this->logger->debug("Session intergity check OK");
                $this->persistent->user = $user;
            }
        }

        return $this->persistent->user;
    }

This is not really different from public $user, but allows you to do $this->user->id instead of $this->user->user->id

How about

$di->set('user', function(){
    $result = new WebUser();
    return $result->user;
});

How about

$di->set('user', function(){
   $result = new WebUser();
  return $result->user;
});

With this solution he will not be able to call the methods of the WebUser class

Thank-you on the info on the __get() magic method. Though I seem to be struggling on implementing it.

This is how I have it currently:

class WebUser extends \Phalcon\Mvc\User\Plugin
{

    private $user = null;

    const AUTH_UNKNOWN = 1;
    const AUTH_SUCCESS = 2;
    const AUTH_WRONG_CREDENTIALS = 3;
    const AUTH_ATTEMPTS_EXCEEDED = 4;
    const AUTH_BANNED = 5;
    const AUTH_NOT_FOUND = 6;

    public function __construct(){
        if($this->user == null)
            $this->user = $this->register();

        return $this->user;
    }

    public function __get($name){
        return $this->user->{$name};
    }

    public function register(){
        if($this->session->has('vdi') && $this->session->has('vcf')){
            if(!$user = $this->check($this->session->get('vdi'), $this->session->get('vcf'))){
                $this->logger->notice("Session intergity check FAIL");
                $this->session->destroy();
                $this->persistent->user = null;
            } else {
                $this->logger->debug("Session intergity check OK");
                $this->persistent->user = $user;
            }
        }

        return $this->persistent->user;
    }

There seems to be the issue that it is trying to pass $this->session->has() through it as well.

Call to a member function has() on a non-object

I think it's actually trying to do $this->user->session->has(). How could I have my other Injectable functions available?

You could do it with the magic __get method:

class WebUser extends \Phalcon\Mvc\User\Plugin
{
   private $user = null;

   public function __get($name) {
       return $this->user->{$name};
   }

   public function register(){
       if($this->session->has('vdi') && $this->session->has('vcf')){
           if(!$user = $this->check($this->session->get('vdi'), $this->session->get('vcf'))){
               $this->logger->notice("Session intergity check FAIL");
               $this->session->destroy();
               $this->persistent->user = null;
           } else {
               $this->logger->debug("Session intergity check OK");
               $this->persistent->user = $user;
           }
       }

       return $this->persistent->user;
   }

This is not really different from public $user, but allows you to do $this->user->id instead of $this->user->user->id

__construct() cannot return anything. Jimmy's suggestion of returning $result->user should work.

Just to clarify - the WebUser class is not the actual user object, it's just a class that generates a User model object - correct?

Correct. The idea of WebUser is to have the User model of the logged in user always available as well as give helper functions such as isLoggedIn().

I removed the return statement from __construct() but it still seems to try to load the session DI from WebUser.

I tried with

$this->getDI()->get(\session\)

And that worked so I thought of converting all of the DI calls to that. But how would I go about getting access to $this->persistent? I'm not sure if this would be the optimal way to go. Maybe I'm missing something.

I really appreciate all the suggestions you guys are sharing.