如何区分 PHP 属性 是否未定义或设置为 NULL

How to distinguish if a PHP property is undefined or set to NULL

所以我正面临这个问题。我有一个 class 表示我的数据库中的一条记录(本例中为用户)。 class 的属性与数据库 table 的列一样多。为简单起见,我的示例中只有三个:

  1. $id - 用户 ID(对于注册用户必须设置为正整数,对于尚未保存在数据库中的用户对象可能设置为 0)
  2. $name - 用户名(必须为每个用户设置,但在从数据库加载之前可能未定义)
  3. $email - 用户的电子邮件地址(如果用户未提交电子邮件地址,则可能为 NULL)

我的(简体)class 看起来像这样:

<?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
}

现在终于解决了我的问题。如您所见,可以在未设置所有属性的情况下创建此 class 的实例。如果我想 e,那将是一个问题。 G。在名称未知时调用 getName()(它不是通过 initialize() 方法设置的,也没有调用 load())。为此,我编写了方法 isFullyLoaded() 检查所有属性是否已知,如果不知道,则应调用 load()(从调用 isFullyLoaded() 的方法开始)。问题的核心是,一些变量可能是空字符串 ('')、零值 (0) 甚至 null(如 $email 属性)。所以我想区分具有任何值集(包括 null)和那些从未被赋予任何价值的人。

具体示例:我要实现这段代码:

$user1 = new User(1);
$user1->initialize('Vic', 'nerd.from.klamath@fallout2.com');
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());

输出这个:

bool(true)
bool(true)
bool(false)

TL:DR 如何区分PHP中未定义的变量和已赋值为NULL的变量?

使用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 exists\n";
}

if(property_exists($a, 'unset_var')) {
    echo "unset_var property exists\n";
}

// procedural:

$null_var = null;

if(array_key_exists('null_var', $GLOBALS)) {
    echo "null_var variable exists\n";
}

if(array_key_exists('unset_var', $GLOBALS)) {
    echo "unset_var variable exists\n";
}

// output:
// null_var property exists
// null_var variable exists

PHP 没有像 javascript 那样未定义的值。但它不是严格类型的,所以如果你没有找到更好的解决方案,这里有一个自定义类型 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();

这是一个可能更好的版本,它使用 instanceof 而不是 get_call 和 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();

这里是另一种引入自定义未定义class(作为单例)的方法。此外,请确保键入您的 class 属性:

class Undefined
{
    private static Undefined $instance;

    protected function __constructor()
    {
    }

    protected function __clone()
    {
    }

    public function __wakeup()
    {
        throw new Exception("Not allowed for a singleton.");
    }

    static function getInstance(): Undefined
    {
        return self::$instance ?? (self::$instance = new static());
    }
}

class Person
{
    private int $age;

    public function getAge(): int|Undefined
    {
        return $this->age ?? Undefined::getInstance();
    }
}

$person = new Person();

if ($person->getAge() instanceof Undefined) {
    // do something
}

但是使用单例模式有一个缺点,因为您应用中的所有 undefined 对象都将严格相等。否则,每个返回 undefined 值的 get 操作都会产生副作用,即另一块已分配的 RAM。