(Solved) Use session in static method (cakephp 4)

Examples simplified here:
If I try this is a class with static methods:

    public static function chkLog($role)
    {
        $userrole = $this->getRequest()->getSession()->read('userrole');
       return $userrole;
       /// more code

And even tried:

    public static function chkLog($role)
    {
        $userrole = Session::read('userrole');
       return $userrole;
       /// more code

All I get is errors:

Error: [Error] Using $this when not in object context

I even have at the top of the class with the static methods:

<?php

namespace App\Helpers;

use App\Controller\AppController;
use Cake\Core\Configure;
use Cake\Http\Session;
use Cake\Event\Event;


class ChkAuth extends AppController

In case the AppController is needed for session.

However

If I change the methods to instance methods:

    public function chkLog($role)
    {
        $userrole = $this->getRequest()->getSession()->read('userrole');
       return $userrole;
       /// more code

And

And call this method through a __callStatic method:

<?php

namespace App\Helpers;

use App\Helpers\ChkAuth;

class Auth
{
    public static function __callStatic($method, $params)
    {
        $instance = ChkAuth::class;
        $c = new $instance;
        return $c->$method(...array_values($params));
    }
}

Of course all works with no errors as expected…

Two questions:
How do you use session inside a static method. And why did the above not work?

Seems like you’re confused as to the nature of static functions in general? $this is a reference to the current object; when a function is static, there is no current object. Hence the error you get. Is there some reason you want/need to make the chkLog function static? You don’t show how it’s ultimately being used, so hard to tell whether it’s a requirement.

Inside of a static function, you can only access static data plus the variables passed in. One possibility would be to pass in the session or request object; middleware generally gets the request passed to it, for example. Another would be to somehow initialize a static member in the class. Lastly, as a last-ditch option if no other way works, you can use Router::getRequest() from pretty much anywhere to get that request object, from which you can get the session.

I know the difference between static and instance. static uses self, instance uses $this. I have written frameworks. My problem is I cannot figure out how to get session to work in a static method in cakephp.

In laravel I can:

    public static function userRole($role = null)
    {
        $userrole = Auth::user()->role;
        $checkrole = explode(',', $userrole);
        if (in_array($role, $checkrole)) {
            return $role;
        }
        return false;
    }

Usage:

    public function scopegetPets($query, $petsearch = '')
    {
        $petsearch = $petsearch . "%";
        $query = Pet::where('petname', 'like', $petsearch);
        if (ChkAuth::userRole('admin') === false) {
            $userid = Auth::user()->id;
            $query->where('ownerid', '=', $userid);
        }
        $results = $query->orderBy('petname', 'asc')->paginate(5);
        return $results;
    }

Just that easy:

However in cakephp I had to write this class to call:

<?php

namespace App\Helpers;

use App\Helpers\ChkAuth;

class Auth
{
    public static function __callStatic($method, $params)
    {
        $instance = ChkAuth::class;
        $c = new $instance;
        return $c->$method(...array_values($params));
    }
}

In order to call statically this class

<?php

namespace App\Helpers;

use App\Controller\AppController;

class ChkAuth extends AppController
{

    public function userRole($role = null)
    {
        $userrole = $this->getRequest()->getSession()->read('userrole');
        $checkrole = explode(',', $userrole);
        if (in_array($role, $checkrole)) {
            return $role;
        }
        return false;

        ///  Other methods
    }

usage

    public function getPets($offset = "", $rowsperpage = "", $petsearch = "")
    {
        $pagingQuery = "LIMIT {$offset}, {$rowsperpage}";
        $petsearch = $petsearch . "%";
        if (Auth::chkRole('admin') === true) {
            return DB::select("SELECT * FROM " . PREFIX . "pets " . $pagingQuery);
        }
        $authid = Auth::authId();
        return DB::select("SELECT * FROM " . PREFIX . "pets WHERE ownerid = :authid " . $pagingQuery, ["authid" => $authid]);
    }

As my original post says, I tried also.

$userrole = Session::read('userrole');

There should be an easy way to use session within a static method.

And I do not have $this in a static method, I only tried:

$userrole = $this->getRequest()->getSession()->read('userrole');

Since it’s in the docs that way.

In a static method I use:

$ismatch = self::userRole($role, $userrole);

Note

All works with the __callStatic class. But still session should easily work inside a static method without special work a rounds.

I know the difference between self and this.

Apologies, but I’m not sure why anyone that’s written frameworks would even consider using $this in a static function. So it seemed like a bit of a novice question.

Did you try Router::getRequest()->session(), as I suggested?

I will give it a try as soon as I can.

I cannot figure out why cakephp uses $this to get session data anyway.

Router did not work. I tryed

$session = new \Cake\Http\Session();
$userrole = $session->read('userrole');
return $userrole;

That works, but don’t want that.

Funny the docs state that:

Session:: read ( $key )

That’s right from the docs.

Solved:

$userrole = \Cake\Routing\Router::getRequest()->getSession()->read('userrole');

Funny if you go to Sessions - 4.x and search
for Routing or Router on that page, nothing.

Thanks for your help.

Session::read($key) is from the section of the manual describing the API provided by the Session class, not shown anywhere as actual code to use. Immediately after that, it gives $session->read('Config.language'); as an example, which assumes that you have the session object.

The section on that same page titled Accessing the Session Object shows $this->getRequest()->getSession()->read() as a “basic example”. So you can tell it’s accessed through the request object, and not hard from there to find how to get that.

No mention of using router anywhere. It’s like they hid that part somewhere.

I’m experienced with frameworks, and I couldn’t find it, how is someone new to cake suppose to find Router::getRequest()->getSession()->read

Router to get session, really. Come on.

Why not Session-> to actually get session. A word that matches a word.

Just me I guess.

In laravel you can:

use Illuminate\Support\Facades\Session;  // have use statement

And right in a static method:

$userrole = Session::get('thisrole');

Router to get session, ???

The router has nothing to do with the session. The session is related to the request, and the request can be accessed through the router (as the primary singleton in the system).

Anyone is free to update the documentation if they think it can be improved.

Is that statement or that knowledge anywhere in any of the documentation if so will you please give a reference to it at least.

And I mean where the docs actually say use router to get session.

I just never heard of using router to get session. But it does work.

Funny if you go to https://book.cakephp.org/4.0/en/development/sessions.html and search
for Routing or Router on that page, nothing.

That was really potlite of you to point it, you do know 4 is in dev, right?

Now coming to your query…

$session = $this->getRequest()->getSession()
is the right way to go and then

$session->read() or $session->write();

Laravel Facade is like a Request “Handler” and thus this.

Why would you prefer singleton style? Just curious :slight_smile:

For future reference… To make it easier for you… Facade and getRequest are siblings.

$this->getRequest()->getSession()->write

3 arrows?

I came up with a better solution since then, I just use the session class direct via
a SessionHelper I wrote:

<?php

namespace App\Helpers;

use Cake\Http\Session;

class SessionHelper
{
    public static function __callStatic($method, $params)
    {
        $instance = Session::class;
        $c = new $instance;
        return $c->$method(...array_values($params));
    }
}

Then to use, just place a use statement at top of class:

use App\Helpers\SessionHelper as session;

And usage:

$userrole = session::read('userrole');

Expanded example:

    public static function chkRole($role = null)
    {
        $userrole = session::read('userrole');
        $checkrole = explode(',', $userrole);
        if (in_array($role, $checkrole)) {
            return true;
        }
        return false;
    }

Same helper then of course works in an instance or static method.

To me the The Scope Resolution Operator as Taylor says is a

terse, expressive syntax

You are internally calling the instance :expressionless:

I do not see a performance gain. Yes, the code is cleaner I agree but using singleton just to avoid 3 arrows, not a reason I have heard.

Cake\View\Helper\SessionHelper is effecient.

I respect your opinion. Have it that way.

I wasn’t trying to avoid 3 arrows, I needed to use it in a static method is the main reason.

I like static methods in helpers.

I could have used

Router::getRequest()->getSession()->read('userrole');

But is that technique getting at times more than needed?

Or is my static call getting the same amount from the request? That part is where I’m unsure.