Search code examples
phpzend-frameworkphp-7object-oriented-analysis

php class method within method


If I want to access the public method, I can do that easily. But if I want to access the property within method, what should I do, and is it recommended??

Can I do something like this in php?

class Auth {
   public function check($user = false){
    $project = false; //make it somehow public
     if($user == 'user1'){
       $this->project = 1;
     }
   }
}

and than in some other place

$auth = new Auth();
$auth->check('user1')->project;

Just so you people know its possible here is the Zend framework code from

Zend-Authentication

 if ($result->isValid()) {
            $this->getStorage()->write($result->getIdentity());
        }

Solution

  • I believe your question is basically regarding Fluent Interfaces or Method Chaining in conjunction with the magic method __get

    Attempting to run this:

    <?php
    class Auth {
       public function check($user = false){
        $project = false; //make it somehow public
         if($user == 'user1'){
           $this->project = 1;
         }
       }
    }
    
    $auth = new Auth();
    $auth->check('user1')->project;
    

    Results in:

    Notice: Trying to get property of non-object in /in/Hi5Rc on line 13

    because $auth->check('user1') returns NULL (or void) and NULL doesn't have a project property.

    The first thing we require is for $auth->check('user1') to return something useful. Given that $project is a boolean and $this->project is an integer, it makes the most sense to just return $project and get the value.

    <?php
    class Auth {
       public function check($user = false){
        $project = false; //make it somehow public
         if($user == 'user1'){
           $this->project = 1;
         }
        return $project;
       }
    }
    
    $auth = new Auth();
    print_r($auth->check('user1'));
    

    which results in :

    bool(false)

    But that doesn't address your question about how to fluently access a nonpublic field or parameter.

    It appears that you are operating under the misconception that these projects are taking method scoped variables like $project in your check() class and making them accessible. They are not.

    Not even in your example of the Zend-Authentication.

    The field $storage itself is protected, but it has public (fluent) getters/setters.

    So, $this->getStorage() returns an instance of new Storage\Session() which has a public write().

    Thus $this->getStorage()->write() works.

    So lets take your example class and modify it a bit to demonstrate.

    <?php
    class Project{
        /**
         * @var string
         */
        private $name;
        /**
         * @var bool
         */
        private $active;
        /**
         * @var string
         */
        private $description;
    
        public function __construct($name = 'Default', $active = false, $description = '')
        {
            $this->name = $name;
            $this->active = $active;
            $this->description = $description;
        }
    
        /**
         * @param string $name
         *
         * @return Project
         */
        public function setName(string $name): Project
        {
            $this->name = $name;
    
            return $this;
        }
    
        /**
         * @param bool $active
         *
         * @return Project
         */
        public function setActive(bool $active): Project
        {
            $this->active = $active;
    
            return $this;
        }
    
        /**
         * @param string $description
         *
         * @return Project
         */
        public function setDescription(string $description): Project
        {
            $this->description = $description;
    
            return $this;
        }
    
        /**
         * @return string
         */
        public function getName(): string
        {
            return $this->name;
        }
    
        /**
         * @return bool
         */
        public function isActive(): bool
        {
            return $this->active;
        }
    
        /**
         * @return string
         */
        public function getDescription(): string
        {
            return $this->description;
        }
    
        public function toArray(){
            return [
                'name' => $this->name,
                'active' => $this->active,
                'description' => $this->description
            ];
        }
    
        public function toJson(){
            return json_encode($this->toArray());
        }
    
        public function __toString()
        {
            return $this->toJson();
        }
    }
    
    
    
    class Auth {
    
        /**
         * @var Project
         */
        private $project;
    
        public function __construct($project = Null)
        {
            $this->project = is_null($project)? new Project() : $project;
        }
    
        public function check($user = false){
            if($user == 'user1'){
                $this->project->setName("Project: $user")->setActive(true)->setDescription("This project belongs to $user");
            }
            return $this;
        }
    
        /**
         * @param Project $project
         *
         * @return Auth
         */
        public function setProject(Project $project): Auth
        {
            $this->project = $project;
    
            return $this;
        }
    
        /**
         * @return Project
         */
        public function getProject(): Project
        {
            return $this->project;
        }
    
    }
    
    $auth = new Auth();
    echo $auth->check('user1')->getProject();
    

    now results in:

    {"name":"Project: user1","active":true,"description":"This project belongs to user1"}

    However, you wanted to access the private field as if it were a public field without using a defined getter/setter. So lets make some more changes to the Auth class.

    class Auth {
    
        /**
         * @var Project[]
         */
        private $private_project;
    
        public function __construct($project = Null)
        {
            $this->private_project = is_null($project)? new Project() : $project;
        }
    
        public function check($user = false){
            if($user == 'user1'){
                $this->private_project->setName("Project: $user")->setActive(true)->setDescription("This project belongs to $user");
            }
            return $this;
        }
    
        public function __get($name)
        {
            if ($name === 'project'){
                return $this->private_project;
            }
        }
    
    }
    

    Now you can fluently access the field as you requested:

    $auth = new Auth();
    echo $auth->check('baduser')->project;
    echo  "\n";
    echo $auth->check('user1')->project;
    

    results in:

    {"name":"Default","active":false,"description":""}

    {"name":"Project: user1","active":true,"description":"This project belongs to user1"}

    Laravel's Eloquent models make great use of the __get()function for accessing model fields dynamically. Laravel also makes great use of the __call() magic method for fluency.

    I hope that helps bring some clarity.