So I am facing this problem. I have a class representing a record in my database (User in this example). The class has as many properties as the database table has columns. For simplicity, I have just three in my example:
$id
– ID of the user (must be set to a positive integer for registered user, might be set to 0 for user objects that aren’t saved in the database yet)$name
– Name of user (must be set for every user, but before loading it from the database might be undefined)$email
– E-mail address of the user (might be NULL in case the user didn’t submit an e-mail address)
My (simplified) class looks like this:
<?php
class User
{
private $id;
private $name;
private $email;
public function __construct(int $id = 0)
{
if (!empty($id)){ $this->id = $id; }
//If $id === 0, it means that the record represented by this instance isn't saved in the database yet and the property will be filled after calling the save() method
}
public function initialize(string $name = '', $email = '')
{
//If any of the parameters isn't specified, prevent overwriting curent values
if ($name === ''){ $name = $this->name; }
if ($email === ''){ $email = $this->email; }
$this->name = $name;
$this->email = $email;
}
public function load()
{
if (!empty($this->id))
{
//Load name and e-mail from the database and save them into properties
}
}
public function save()
{
if (!empty($this->id))
{
//Update existing user record in the database
}
else
{
//Insert a new record into the table and set $this->id to the ID of the last inserted row
}
}
public function isFullyLoaded()
{
$properties = get_object_vars($this);
foreach ($properties as $property)
{
if (!isset($property)){ return false; } //TODO - REPLACE isset() WITH SOMETHING ELSE
}
return true;
}
//Getters like getName() and getId() would come here
}
Now finally to my problem. As you can see, the instance of this class can be created without all properties set. That’s a problem in case I want to e. g. call getName()
while the name isn’t known yet (it wasn’t set via the initialize()
method and load() wasn’t called). For that, I wrote method isFullyLoaded()
which checks if all properties are known and if not, load()
should be called (from the method calling isFullyLoaded()
. And the core of the problem is, that some variables might be empty strings (”), zero values (0) or even null (like the $email
property). So I want to distinguish variables that have any value set (including null) and those who have never been assigned any value.
Specific example: I want to achieve this code:
$user1 = new User(1);
$user1->initialize('Vic', '[email protected]');
var_dump($user1->isFullyLoaded());
$user2 = new User(2);
$user2->initialize('Cassidy', null); //No e-mail was specified during the registration
var_dump($user2->isFullyLoaded());
$user3 = new User(3);
$user3->initialize('Myron'); //E-mail isn't known yet, but might be saved in the database
var_dump($user3->isFullyLoaded());
to output this:
bool(true)
bool(true)
bool(false)
TL:DR How do distinguish undefined variable and variable which has been assigned NULL in PHP?
Use property_exists()
:
<?php
error_reporting(E_ALL);
// oop:
class A {
public $null_var = null;
}
$a = new A;
if(property_exists($a, 'null_var')) {
echo "null_var property existsn";
}
if(property_exists($a, 'unset_var')) {
echo "unset_var property existsn";
}
// procedural:
$null_var = null;
if(array_key_exists('null_var', $GLOBALS)) {
echo "null_var variable existsn";
}
if(array_key_exists('unset_var', $GLOBALS)) {
echo "unset_var variable existsn";
}
// output:
// null_var property exists
// null_var variable exists
PHP has not the value undefined like javascript. But it is not strict typed so if you do not find a better solution here is one with an custom type UNDEFINED
<?php
class UNDEFINED { }
class Test {
var $a;
function __construct( $a='' ) {
$this->a = new UNDEFINED();
if( $a !== '' ) {
$this->a = $a;
}
}
function isDefined() {
$result =true;
if(gettype($this->a) === 'object'){
if(get_class($this->a) === 'UNDEFINED') {
$result=false;
}
}
echo gettype($this->a) . get_class($this->a);
return $result;
}
}
$test= new Test();
$test->isDefined();
Here is a may be litte better version which used instanceof instead of get_call and getType
<?php
class UNDEFINED { }
class Test {
var $id;
var $a;
var $b;
function __construct( $id) {
$this->id = $id;
$this->a = new UNDEFINED();
$this->b = new UNDEFINED();
}
function init( $a = '' , $b = '') {
$this->a = $this->setValue($a,$this->a);
$this->b = $this->setValue($b,$this->b);
}
function setValue($a,$default) {
return $a === '' ? $default : $a;
}
function isUndefined($a) {
return $a instanceof UNDEFINED;
}
public function isFullyLoaded()
{
$result = true;
$properties = get_object_vars($this);
print_r($properties);
foreach ($properties as $property){
$result = $result && !$this->isUndefined($property);
if ( !$result) break;
}
return $result;
}
function printStatus() {
if($this->isFullyLoaded() ) {
echo 'Loaded!';
} else {
echo 'Not loaded';
}
}
}
$test= new Test(1);
$test->printStatus();
$test->init('hello');
$test->printStatus();
$test->init('', null);
$test->printStatus();