8

我刚刚发现关于 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类型。这当然是有道理的。问题是,如果它们以这样的方式结束,您将无法遍历它们,它们将被简单地省略 - 没有错误,没有任何东西,正如您在第二个示例中看到的那样。所以我只是无法访问它们。

这是有道理的,但想象一下你有一个这样的自定义请求/数据类:

namespace App\Request\Request\Post;

use App\Request\Request\Request;

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

你看到那个丑陋的字符串赋值了吗?如果我想让我的类上的属性可迭代,那么我必须:

  • 掉落类型
  • 分配虚拟值

我可能想要一个具有类型化属性的对象,其中没有任何值来循环它们并在有意义的情况下填充它们。

有没有更好的方法来做到这一点?是否有任何选项可以保留类型并保持可迭​​代而不必执行这种虚拟值可憎?

4

4 回答 4

10

如果你想让一个类型化的属性可以为空,你可以简单地?在类型之前添加一个并给出NULL这样的默认值:

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

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

演示


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

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"
// }
于 2020-03-29T12:56:24.687 回答
2

在我们开始之前 - 我认为我接受并由 Casimir 提供的答案比我想出的更好、更正确(这也适用于评论)。

我只是想分享我的想法,因为这在某种程度上是一个可行的解决方案,至少我们可以称之为答案。

这是我为满足我的特定需求而提出的,只是为了好玩。我很好奇我可以做些什么来让它更像我想要的那样,所以不要害怕它;我认为这是一个非常干净的解决方法——但我知道它并不完美。

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 可空运算符控制。

当然,如果有意义的话,可以将其更改或扩展为更特定于类型。

于 2020-03-29T14:25:04.540 回答
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);
            }
        }
于 2020-07-02T10:12:56.547 回答
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
于 2020-03-29T15:39:51.030 回答