PHP 7.4 Типизированная итерация свойств - PullRequest
4 голосов
/ 29 марта 2020

Я только что нашел что-то "немного странное" в 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 тип. Что, конечно, имеет смысл. Проблема, однако, в том, что если они окажутся такими, как вы, вы не сможете l oop над ними, они просто будут опущены - без ошибок, без всего, что вы можете видеть во втором примере. Поэтому я просто теряю к ним доступ.

Это имеет смысл, но просто представьте, что у вас есть собственный класс запросов / данных, подобный этому:

namespace App\Request\Request\Post;

use App\Request\Request\Request;

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

Видите ли вы это уродливое строковое назначение? Если я хочу сделать свои свойства в классе итеративными, то мне нужно:

  • отбросить типы
  • назначить фиктивные значения

Я мог бы захотеть иметь объект с типизированными свойствами без каких-либо значений в них до l oop над ними и заполнять их, если это имеет смысл.

Есть ли лучший способ сделать это? Есть ли возможность сохранять типы и поддерживать их повторяемость, не прибегая к этим мерзким мерзостным значениям?

Ответы [ 3 ]

5 голосов
/ 29 марта 2020

Если вы хотите, чтобы типизированный атрибут был обнуляемым, вы можете просто добавить ? перед типом и задать 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 голос
/ 29 марта 2020

Прежде чем мы начнем - я думаю, что ответ, принятый мной и предоставленный Казимиром, лучше и правильнее, чем тот, который я придумал (это также относится и к комментариям).

Я просто хотел поделиться мои мысли, и поскольку это в какой-то степени рабочее решение, по крайней мере, мы можем назвать это ответом.

Это то, что я придумал для своих конкретных потребностей c и просто для удовольствия. Мне было любопытно, что я могу сделать, чтобы сделать его таким, каким я его хочу, так что не волнуйтесь об этом; PI думаю, что это довольно чистый обходной путь - хотя я знаю, что он не идеален.

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

Недостатком этого решения является то, что вы всегда должны делать типы обнуляемыми в своем классе. Однако для меня и моих спецификаций c нужно , что вполне нормально. Я не возражаю, в любом случае они в конечном итоге обнулятся, и это может быть хорошим обходным решением для ситуации с коротким сроком, если кто-то спешит.

Он по-прежнему сохраняет исходную PHP не инициализированную ошибку, хотя, когда тип не обнуляется. Я думаю, что сейчас это действительно круто. Вы можете сохранить все вещи: классы Slim и Lean, ошибка PHP, указывающая на истинную природу проблемы и возможность l oop над типизированными свойствами, если вы согласны сохранять их обнуляемыми. Все регулируется родным PHP 7 обнуляемым оператором.

Конечно, это может быть изменено или расширено, чтобы быть более специфичным для типа c, если это имеет смысл.

0 голосов
/ 29 марта 2020

Возможно, не то, что вы хотите, но вы можете использовать отражение:

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