skip to Main Content

Consider the following user class:

<?php

class User
{
    private string $name;

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }
}

$user = new User();

We would like to get the $name property of object $user. However, the $name property has not always been set. If we run $user->getName() and the $name property has not been set, then we get the following warning:

PHP Warning:  Uncaught Error: Typed property User::$name must not be accessed before initialization

How can we call $user->getName() without running into errors?

Use __isset?

Should we use the __isset magic method? For example, we could add the following __isset method to the class:

public function __isset($name): bool {
    return isset($this->$name);
}

And then, from outside the class, we could run:

if (isset($user->name)) {
    // $user->getName() can now be called
}

However, it seems odd that our code would contain $user->name knowing in advance that the $name property is private. Also, since we call the $name property often, we would be adding this if statement in numerous (perhaps hundreds of) places in our code.

Default $name to be null?

For example, we could update our class:

class User
{
    private ?string $name = null;

    public function getName(): ?string
    {
        return $this->name;
    }

    // ...
}

And then, from outside the class, we could run:

if (isset($user->getName())) {
    // Do stuff ...
}

However, it seems odd that $name would default to null since null is not a valid value for someone’s name. An "uninitialized" value seems more appropriate. And, we would again need to add numerous if statements around our code.

Any suggestions or feedback would be very much appreciated. Thank you.

3

Answers


  1. Four ways come to my mind:

    • Initialize $name upon definition
    • Force the constructor to initialize $name
    • Check within getName()
    • Add a helper method
    class User
    {
        // either pre-initialize
        private string $name = '';
    
        // or enforce setting by constructor
        public function __construct(string $name = '')
        {
            $this->name = $name;
        }
    
        public function getName(): string
        {
            // or check within getName(), null-coalescing operator added as suggested by Robert in a comment
                return $this->name ?? '';
            }
        }
    
        public function setName(string $name): self
        {
            $this->name = $name;
            return $this;
        }
    
        // or add a public helper function
        public function isNameSet(): bool
        {
            return (isset($this->name));
        }
    
    
    }
    
    $user = new User();
    
    if ($user->isNameSet()) {
        // Do things
    }
    
    
    Login or Signup to reply.
  2. Use a non-typed property, or initialize it in the constructor, or with the declaration of the property.

    The whole point or a typed property is to avoid comparison with null (well, one of the points). Also null is not a string. So using a null/or-string type defeates the whole point more or less – you may just as well use a non-typed property instead.

    In php, what is a good way to determine whether private property is set?

    You have the wrong frame of mind imho. In the OOP paradigm, it’s your responsibility to initialize your objects properly, as opposed to asking "are my objects initialized propery?" after instantiation.

    So the answer is: initialize it properly, or declare a default value.

    GLHF

    Login or Signup to reply.
  3. Avoiding the use of conditional checks throughout your system could be difficult as it appears your system expects valid values but these are not always available and, depending on how the places your getters are called use the return value.

    If your User is open to some modification, then you could as you’ve suggested make certain properties nullable or initialize them with suitable values (null, empty, whatever works for your business/use case). As Nigel Ren has mentioned in his comment a null value can be perfectly valid depending on the scenario (although it might not be ideal).

    The following might be helpful to you, although it doesn’t entirely mitigate the requirement to perform conditional checks. It does provide some (arguably) easier helper methods for performing such checks and returning some default values.

    class User
    {
        private ?string $name = null;
        
        private ?string $nickname = '';
        
        private ?string $city = 'New York';
        
        public function getName()
        {
            return $this->name !== null && !empty($this->name) 
                ? $this->name 
                : "Not Provided";
        }
        
        public function getNickName()
        {
            return $this->nickname !== null && !empty($this->nickname) 
                ? $this->nickname
                : "Not Provided";
        }
        
        public function getCity()
        {
            return $this->city !== null && !empty($this->city) 
                ? $this->city
                : "Not Provided";
        }
        
        public function uninitialised()
        {
            $uninitialized = [];
            foreach (get_object_vars($this) as $property => $value) {
                if (!isset($value) || empty($value)) {
                    $uninitialized[] = $property;
                }
            }
            
            return $uninitialized;
        }
        
        public function checkForUninitialised(array $properties)
        {
            return array_intersect($properties, $this->uninitialised());
        }
    }
    
    $user = new User();
    
    var_dump($user->uninitialised());
    
    echo PHP_EOL;
    
    var_dump(
        $user->checkForUninitialised(
            ['name', 'nickname', 'city'], 
            $user->uninitialised()
        )
    );
    

    The output of the above would be:

    array(2) {
      [0]=>
      string(4) "name"
      [1]=>
      string(8) "nickname"
    }
    
    array(2) {
      [0]=>
      string(4) "name"
      [1]=>
      string(8) "nickname"
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search