在php中,确定是否设置私有属性的好方法是什么?[关闭]

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

提问人:Leo Galleguillos 提问时间:12/25/2022 最后编辑:Leo Galleguillos 更新时间:12/25/2022 访问量:228

问:


想改进这个问题吗?更新问题,以便可以通过编辑这篇文章用事实和引文来回答。

11个月前关闭。

请考虑以下用户类:

<?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();

我们想得到 object 的属性。但是,该属性并不总是设置。如果我们运行并且尚未设置该属性,则会收到以下警告:$name$user$name$user->getName()$name

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

我们怎样才能在不遇到错误的情况下调用?$user->getName()

使用__isset

我们应该使用神奇的方法吗?例如,我们可以将以下方法添加到类中:__isset__isset

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

然后,从课堂外,我们可以运行:

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

但是,我们的代码包含事先知道该属性是私有的,这似乎很奇怪。此外,由于我们经常调用该属性,因此我们会在代码中的许多(可能数百个)位置添加此语句。$user->name$name$nameif

默认$name为 null?

例如,我们可以更新我们的类:

class User
{
    private ?string $name = null;

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

    // ...
}

然后,从课堂外,我们可以运行:

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

但是,默认为 since 不是某人姓名的有效值似乎很奇怪。“未初始化”的值似乎更合适。而且,我们还需要在代码周围添加大量语句。$namenullnullif

任何建议或反馈将不胜感激。谢谢。

php 私有 isset

评论

0赞 Peppermintology 12/25/2022
简单的解决方案是通过构造函数强制提供有效(如何定义有效取决于您)。nameUsers
0赞 Nigel Ren 12/25/2022
Null 通常用于表示未知值,因此这是一个合理的选择。
0赞 Leo Galleguillos 12/25/2022
@Peppermintology谢谢。但是,我们的业务逻辑不保证设置了该属性。另外,只是一个例子。同样的问题也适用于任何其他可选属性,如 或 等。$name$name$nickname$city
0赞 Peppermintology 12/25/2022
@LeoGalleguillos 您是否无法通过构造函数修改以需要这些属性?或者您的意思是,无论出于何种原因,您并不总是获得与 ?UserUser
2赞 Robert 12/25/2022
保持简单,让 getName 返回以下内容:$this->name ?? '';

答:

1赞 Skip 12/25/2022 #1

我想到了四种方式:

  • 定义时初始化$name
  • 强制构造函数初始化$name
  • 签入getName()
  • 添加帮助程序方法
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
}

1赞 Ярослав Рахматуллин 12/25/2022 #2

使用非类型化属性,或在构造函数中初始化它,或者使用属性的声明来初始化它。

整个要点或类型化属性是避免与 null 进行比较(嗯,这是要点之一)。此外,null 不是字符串。因此,使用 null/or-string 类型或多或少会破坏整个观点 - 您也可以改用非类型化属性。

在php中,确定是否设置私有属性的好方法是什么?

恕我直言,你的心态是错误的。在 OOP 范式中,你有责任正确地初始化你的对象,而不是在实例化后“我的对象是否初始化了?

所以答案是:正确初始化它,或者声明一个默认值。

GLHF系列

评论

0赞 Dmitry K. 12/25/2022
可以按照建议使用具有空值或 null 值的类型化属性@Peppermintology
0赞 Ярослав Рахматуллин 12/26/2022
@DmitryK。是的,但问问自己:如果你正在编写新代码,为什么你希望某些东西是 null 或字符串,而不是总是字符串?根据经验和直觉,我的感觉是你没有。我邀请您(和其他读者)考虑与对象实例化、验证、null 指针异常(或取消引用 null 属性)、值对象和简单的比较操作相关的问题。也。。我已经承认它可以使用,但我的论点是这样做(几乎)毫无意义。
2赞 Peppermintology 12/25/2022 #3

避免在整个系统中使用条件检查可能很困难,因为您的系统似乎需要值,但这些值并不总是可用,并且取决于调用您的位置使用返回值的方式。validgetters

如果你愿意进行一些修改,那么你可以按照你的建议,使某些属性可以为空,或者用合适的值初始化它们(,,任何适合你的业务/用例)。正如Nigel 任在他的评论中提到的,根据场景,一个值可以是完全有效的(尽管它可能并不理想)。Usernullemptynull

以下内容可能对您有所帮助,尽管它并不能完全减轻执行条件检查的要求。它确实提供了一些(可以说)更简单的帮助程序方法来执行此类检查并返回一些默认值。

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()
    )
);

上述结果为:

array(2) {
  [0]=>
  string(4) "name"
  [1]=>
  string(8) "nickname"
}

array(2) {
  [0]=>
  string(4) "name"
  [1]=>
  string(8) "nickname"
}

评论

0赞 Robert 12/25/2022
这里“重复自己”的代码太多了,这时候你不妨用魔法。尽管如此,此代码会删除原始帖子中的类型返回值。__get()
0赞 Peppermintology 12/25/2022
@Robert确实如此。这只是一个遵循 OP 使用的模式的说明性示例。我们也不知道在系统的其余部分或外部系统中,这些方法被用于何处。可能无法重构 。getProperty__get()