PHP 7.4类型属性迭代

时间:2020-03-29 12:47:25

标签: php types iteration php-7 php-7.4

我刚刚发现了有关PHP 7.4的一些“奇怪的东西”,我不确定这是我只是缺少了一些东西,还是可能是一个实际的错误。我通常对您的意见/确认感兴趣。

因此在PHP中,您可以像这样遍历对象属性:

class DragonBallClass 
{
    public $goku;
    public $bulma = 'Bulma';
    public $vegeta = 'Vegeta';
}

$dragonBall = new DragonBallClass();

foreach ($dragonBall as $character) {
  var_dump($character);

}

RESULT

NULL
string(5) "Bulma"
string(6) "Vegeta"

现在,如果我们开始使用像这样的强类型属性:

class DragonBallClass 
{
    public string $goku;
    public string $bulma = 'Bulma';
    public string $vegeta = 'Vegeta';
}

$dragonBall = new DragonBallClass();

foreach ($dragonBall as $character) {
  var_dump($character);

}

我们将得到不同的结果:

string(5) "Bulma"
string(6) "Vegeta"

现在有什么不同:

当您不为强类型属性分配默认值时,它将是Uninitialized类型。哪个当然有意义。问题是,如果它们以这种方式结束,您将无法遍历它们,它们只会被省略-没有错误,没有任何东西,如您在第二个示例中看到的那样。所以我只是无法访问它们。

这是有道理的,但只需想象您有一个自定义的Request / Data类,如下所示:

namespace App\Request\Request\Post;

use App\Request\Request\Request;

class GetPostsRequest extends Request
{
    public string $title = '';
}

您看到那个难看的字符串分配吗?如果要使该类的属性可迭代,则必须执行以下操作之一:

  • 拖放类型
  • 分配虚拟值

我可能想要一个带有类型化属性的对象,其中没有任何值来遍历它们并在可行时填充它们。

有没有更好的方法?是否可以选择保留类型并保持可迭​​代性,而不必进行此伪值可憎的选择?

4 个答案:

答案 0 :(得分:8)

如果您想允许类型化的属性为空,则只需在类型之前添加?,然后将NULL作为默认值,如下所示:

class DragonBallClass 
{
    public ?string $goku = NULL;
    public string $bulma = 'Bulma';
    public string $vegeta = 'Vegeta';
}

在这种情况下,NULL是一个完全合法的值(而不是虚拟值)。

demo


也可以不使用?,而始终将类属性与对象属性列表合并:

class DragonBallClass 
{
    public string $goku;
    public string $bulma = 'Bulma';
    public string $vegeta = 'Vegeta';
}

$dragonBall = new DragonBallClass();

$classProperties = get_class_vars(get_class($dragonBall));
$objectProperties = get_object_vars($dragonBall);

var_dump(array_merge($classProperties, $objectProperties));

// array(3) {
//  ["goku"]=>
//  NULL
//  ["bulma"]=>
//  string(5) "Bulma"
//  ["vegeta"]=>
//  string(6) "Vegeta"
// }

答案 1 :(得分:2)

在开始之前-我认为我接受并由卡西米尔(Casimir)提供的答案比我提出的要好(也适用于评论)。

我只是想分享自己的想法,由于在某种程度上这是一种可行的解决方案,至少我们可以将其称为答案。

这是我出于自己的特定需求而提出的,只是为了好玩。我很好奇我可以做些什么以使其达到我希望的方式,所以不要惊慌; P我认为这是一个非常干净的解决方法-尽管我知道这并不完美。

class MyRequest
{
    public function __construct()
    {    
        $reflectedProperties = (new \ReflectionClass($this))->getProperties();
        foreach ($reflectedProperties as $property) {
            !$property->isInitialized($this) ??
            $property->getType()->allowsNull() ? $property->setValue($this, null) : null;
        }
    }

}


class PostRequest extends MyRequest 
{
    public ?string $title;

}

$postRequest = new PostRequest();

// works fine - I have access here!
foreach($postRequest as $property) {
    var_dump($property);
}

该解决方案的不足之处在于,您始终必须在类中使类型为可空。 但是对于我和我的具体需求 ,这完全可以。我不介意,无论如何它们最终都将成为可空值,如果有人急于在短期限内完成任务,这可能是一个不错的解决方法。

即使类型不可为空,它仍然保留原始PHP未初始化错误。我认为现在这真的很酷。您必须保留所有内容:Slim和lean类,PHP错误指示问题的真实性质,如果同意使它们保持可为空,则有可能循环键入的属性。所有这些都由本机PHP 7可为空的运算符控制。

当然,可以的话,可以更改或扩展为更多特定于类型的类型。

答案 2 :(得分:1)

更新:该答案可能已过时,但注释中包含有趣的讨论。

@Robert的变通办法是越野车;在这一部分:

        foreach ($reflectedProperties as $property) {
            !$property->isInitialized($this) ??
            $property->getType()->allowsNull() ? $property->setValue($this, null) : null;
        }

??必须更正为&&

此外,这是对三元条件的滥用;只需使用经典的if

        foreach ($reflectedProperties as $property) {
            if (!$property->isInitialized($this)
                && $property->getType()->allowsNull()
            ) {
                $property->setValue($this, null);
            }
        }

或:

        foreach ($reflectedProperties as $property) {
            if (!$property->isInitialized($this) && $property->getType()->allowsNull()) {
                $property->setValue($this, null);
            }
        }

答案 3 :(得分:0)

可能不是您想要的,但是您可以使用反射:

<?php

class DragonBallClass 
{
    public string $goku;
    public string $bulma = 'Bulma';
    public string $vegeta = 'Vegeta';
}
$ob        = new DragonBallClass;
$reflector = new ReflectionClass($ob);
foreach($reflector->getProperties(ReflectionProperty::IS_PUBLIC) as $prop) {
    echo $prop->name, ':', ($ob->{$prop->name} ?? 'NOT INITIALISED'), "\n";
}

输出:

goku:NOT INITIALISED
bulma:Bulma
vegeta:Vegeta