PHP 警告已弃用:不推荐创建动态属性

PHP Warning Deprecated: Creation of dynamic property is deprecated

提问人:Johnny 3653925 提问时间:12/22/2022 最后编辑:ADysonJohnny 3653925 更新时间:11/3/2023 访问量:72994

问:

我越来越多地看到这种情况,我不确定我需要做些什么来阻止此警告:

已弃用:创建动态属性...已弃用

这是我的班级:

class database {

    public $username = "root";
    public $password = "password";
    public $port = 3306;

    public function __construct($params = array())
    {
        foreach ($params as $key => $value)
        {
            $this->{$key} = $value;
        }
    }
}

这就是我实例化它的方式。

$db = new database(array(
    'database' => 'db_name',
    'server' => 'database.internal',
));

这给了我两条信息:

已弃用:创建动态属性数据库::$database 荒废的

已弃用:创建动态属性 database::$server 是 荒废的

PHP的

评论

0赞 jbrahy 12/22/2022
您收到的确切警告是什么?
0赞 Johnny 3653925 12/22/2022
我得到这样的几个,“未知错误类型:[8192] 动态属性数据库::$server的创建已弃用,一个也用于$server”
0赞 jbrahy 12/22/2022
稍后在创建该$database对象时,必须传入其他参数。
1赞 Alex Howansky 12/22/2022
你可以在 setter 前面写这样的话:它不会修复你的代码,但它至少会让你在确定其他违规行为的过程中有一个钩子。if (!property_exists($this, $key)) { throw new Exception('Unknown property'); }
1赞 IMSoP 12/22/2022
@AlexHowansky 消息已命名正在创建的属性;OP 刚刚将其编辑掉。

答:

13赞 jbrahy 12/22/2022 #1

因此,警告来自添加动态类属性的构造函数。如果你不必动态地、真正地传入这些字段,那么你似乎确实把一些简单的事情复杂化了,那么试试这样吧。

class database {

    public $username = "root";
    public $password = "pasword";
    public $port = 3306;
    public $database = 'db_name';
    public $server = 'database.internal';
}


$db = new database();

您需要动态参数是有原因的吗?您也可以这样做:

class database {

    public $username = "root";
    public $password = "pasword";
    public $port = 3306;
    public $database;
    public $server;

    public function __construct($params = array())
    {

        foreach ($params as $key => $value)
        {
            $this->{$key} = $value;
        }
    }
}

如果提前添加参数,它们就不是动态的,而只是为已经存在的东西赋值。

这现在应该可以在没有任何警告的情况下工作。


$db = new database(array(
    'database' => 'db_name',
    'server' => 'database.internal',
));

评论

0赞 Nigel Ren 12/22/2022
这也存在以下问题:这肯定是一个解决方案,但它会阻碍代码完成
1赞 Johnny 3653925 12/22/2022
@NigelRen呢?它将 $db->database 和 $db->server 添加到代码完成选项中。
0赞 Nigel Ren 12/22/2022
@Johnny3653925,那么何时使用它有助于代码完成?new database()
1赞 Johnny 3653925 12/22/2022
@NigelRen,当我创建 $db = new database() 的实例时,我可以执行 $db->,我的 ide 会向我显示我的所有公共成员。
1赞 jbrahy 12/22/2022
@Johnny3653925听起来像一面编码镜。哦,伙计,对不起,坏笑话。
3赞 Nigel Ren 12/22/2022 #2

另一种解决方案,特定于此示例,因为参数非常容易识别。数据库具有引用的公共参数。

使用 PHP 8 命名参数和构造函数属性提升,允许您在构造函数中指定所有可能的参数(这有点冗长,但非常适合代码完成),然后在创建实例时,参数名称是固定的......

class database
{
    public function __construct(
        private string $database = '',
        private string $server = '',
        private string $port = '',
        private string $user = '',
        private string $password = ''
    ) {
    }
}

然后用

$db = new database(
    database: 'db_name',
    server: 'database.internal',
);

评论

0赞 Johnny 3653925 12/22/2022
如果我这样做,那么我就无法从类外部访问私有变量的值。我必须为每个访问器添加一些访问器。
0赞 Nigel Ren 12/22/2022
@Johnny3653925,如果你愿意,你可以把它们公开,但这通常是违反面向对象的做法的,因为它允许其他类扰乱你所拥有的值。(stackoverflow.com/questions/4361553/... 有一些信息)
0赞 Johnny 3653925 12/22/2022
好吧,这是一个更大的图景。我会做更多的研究。
0赞 IMSoP 12/22/2022
在这个网站上,将某些东西称为“替代解决方案”有点令人困惑,因为答案的顺序可能会改变并添加新的答案。理想情况下,答案应该是独立的,并解释问题是什么,以及如何解决它。
1赞 IMSoP 12/22/2022
哦,在那种情况下,我绝对认为可以更清楚,因为我的第一反应是“替代什么?由于有些人完全误解了错误消息,因此最好解释实际问题以及解决方案。
2赞 Gaios 12/22/2022 #3

您看到的警告消息与使用 PHP 中称为“动态属性”的功能有关。动态属性允许您使用变量名称来设置和获取对象属性,就像在数据库类的方法中所做的那样。__construct

快速修复:

public function __construct(array $params)
{
    $this->username = $params['username'] ?? null;
    $this->password = $params['password'] ?? null;
    $this->port = $params['port'] ?? null;
}

若要修复此警告,可以在代码中删除动态属性的使用,并使用更现代的方式来设置对象属性。

命名参数

class database
{
    public function __construct(
        public string $username,
        public string $password,
        public int $port,
    ) {}
}

$db = new database(
    username: 'root',
    password: 'password',
    port: 3306,
);

或者,也可以使用 和 magic 方法来设置和获取对象属性,如下所示:__set__get

public function __set($key, $value)
{
    $this->$key = $value;
}

public function __get($key)
{
    return $this->$key;
}

这将允许您使用表示法来设置和获取对象属性,而不会触发警告消息。$object->property

评论

0赞 Johnny 3653925 12/22/2022
我将其从 {$key} 更改为仅 $key,但仍然看到相同的警告。
0赞 IMSoP 12/22/2022
有一点误会;在这种情况下,“动态属性”不是指使用变量作为名称访问对象属性,而是指获取或设置尚未声明的属性。 只要在班级的顶部宣布就可以了; 如果在类的顶部声明,则会发出警告。$propName='id'; $this->{$propName}=42;$id$this->id=42;$id
1赞 IMSoP 12/22/2022
只是为了重申投票的人,这个答案具有误导性,因为它使用了错误的动态属性定义。写作不是触发通知的原因,写作会触发完全相同的信息$this->{$key} = $value$this->database - $value
2赞 kmoser 12/22/2022 #4

通过将成员变量存储在单个数组中,可以避免指定成员变量的显式列表,如下所示:

class database {
    public $data;

    public function __construct($params = array())
    {
        foreach ($params as $key => $value)
        {
            $this->data[$key] = $value;
        }
    }
}

然后,您可以使用所需的任何参数实例化对象:database

$db = new database(array(
    'database' => 'db_name',
    'server' => 'database.internal',
    'foo' => 'bar', // etc.
));

当然,您可能希望记录预期的参数是什么,但至少您可以传入任何您想要的内容,并且能够在将来使用它,而无需更改类定义。

评论

0赞 Johnny 3653925 12/22/2022
这绝对是一个解决方案,但它会阻碍代码完成
25赞 IMSoP 12/22/2022 #5

该警告告诉您,您正在尝试设置一个属性,该属性未在类或其任何父类中声明

运行此命令时:

class database {

    public $username = "root";
    public $password = "pasword";
    public $port = 3306;

    public function __construct($params = array())
    {
        foreach ($params as $key => $value)
        {
            $this->{$key} = $value;
        }
    }
}

$db = new database(array(
    'database' => 'db_name',
    'server' => 'database.internal',
));

它大致等同于此:

class database {

    public $username = "root";
    public $password = "pasword";
    public $port = 3306;
}

$db = new database;
$db->database = 'db_name';
$db->server = 'database.internal';

警告是,类定义中没有一行表明 or 存在。$db->database$db->server

目前,它们将动态创建为非类型化公共属性,但将来需要显式声明它们:

class database {
    public $database;
    public $server;
    public $username = "root";
    public $password = "pasword";
    public $port = 3306;

    public function __construct($params = array())
    {
        foreach ($params as $key => $value)
        {
            $this->{$key} = $value;
        }
    }
}

$db = new database(array(
    'database' => 'db_name',
    'server' => 'database.internal',
));

在极少数情况下,您实际上想说“这个类的属性是我决定在运行时添加的任何属性”;在这种情况下,您可以使用该属性,如下所示:#[AllowDynamicProperties]

#[\AllowDynamicProperties]
class objectWithWhateverPropertiesIWant {
    public function __construct($params = array())
    {
        foreach ($params as $key => $value)
        {
            $this->{$key} = $value;
        }
    }
}

评论

3赞 Dan S 6/14/2023
我理解委员会为什么选择这样做,但在我看来,一旦它最终被弃用并开始抛出实际错误,它将比对 PHP 所做的任何其他更改都炸毁更多的应用程序。他们真的应该考虑反其道而行之,并让选项可用于显式拒绝而不是允许动态创建的属性。
0赞 IMSoP 6/14/2023
@DanS 有趣的是,这正是我几年前提出的,但有人认为,从长远来看,默认情况下锁定类对该语言的新用户来说会更好。通常情况下,我可以看到这两个论点。(顺便说一句,“委员会”对于PHP内部社区来说是一个相当宏大的术语;它组织得非常松散,而且是自成一体的,不管是好是坏。
1赞 IMSoP 10/24/2023
新用户须知。这里不是分享你对这个功能/PHP/21 世纪有多可怕/令人惊讶/伟大/令人困惑的看法的地方。评论用于对问题或答案的内容提出澄清或修改建议。查看导览和帮助中心
0赞 99 Problems - Syntax ain't one 12/1/2023
开头应该有一个斜杠:#[\AllowDynamicProperties] php.net/manual/en/class.allowdynamicproperties.php
1赞 IMSoP 12/1/2023
@99Problems-Syntaxain'tone 这取决于 a) 你所在的命名空间,以及 b) 你是否在文件的开头 - 换句话说,它遵循与你提到的任何其他类相同的规则。据我所知,让它过分限定永远不会有什么坏处,所以为了简单的复制和粘贴,我在示例中添加了它。use AllowDynamicProperties;
7赞 Katufo Prem 1/26/2023 #6

在类上方添加 AllowDynamicProperties 将使警告消失:

#[\AllowDynamicProperties]
class database {

评论

1赞 IMSoP 4/18/2023
在这种情况下,我不会称之为“修复”。它将使警告消失,但警告会告诉你一些有用的东西。如果类需要 $database 和 $server属性,它应该定义它们,然后它们就不会是“动态的”,也不会有警告。
0赞 Random Dude 10/2/2023
@IMSoP你能告诉我这有什么用吗?
0赞 IMSoP 10/2/2023
@RandomDude 这不是真正讨论的地方,但我能想到很多原因;例如,排名不分先后: 1)捕获错别字:如果你编写而不是它通常比让语言运行时静默创建一个额外的属性更有助于获得错误,这样现在你已经定义了“数据库”和“数据库”。2) 提供属性可见性(或)和类型。3)在6个月内向读者(包括你自己)“宣传”存在哪些属性,并让IDE自动完成它们。$this->databsae = new DB;$this->database = new DB;privateprotected
0赞 Random Dude 10/9/2023
@IMSoP,如果您不知道将从 150 个列表中发送哪个属性怎么办?
0赞 IMSoP 10/9/2023
@RandomDude 这绝对是一个不寻常的情况——而且与这个问题上显示的情况明显不同。对于这种情况,添加属性是有意义的 - 我并不是说永远不应该使用它,只是说它在这种情况下不合适。另一方面,最好还是将属性存储在单个数组属性中,使用泛型 get/set 方法,或者具有一些访问和类型检查规则的魔术__get和__set,而无需列出所有 150 个属性。
1赞 Ale DC 2/8/2023 #7

如果您只想不显示警告,只需替换以下命令:

class database {

为此:

class database extends \stdClass {

评论

4赞 IMSoP 4/18/2023
在这种情况下,我认为这不是正确的解决方案。它将使警告消失,但警告会告诉你一些有用的东西。如果类需要 $database 和 $server属性,它应该定义它们,然后它们就不会是“动态的”,也不会有警告。
1赞 Ale DC 4/18/2023
@IMSoP你是对的!