I have a DTO
with typed PHP variables:
class CreateMembershipInputDto extends BaseDto
{
public bool $is_gift;
public int $year;
public string $name;
public \DateTime $shipping_date;
public ContactInputDto $buyer;
public ?ContactInputDto $receiver;
}
I am trying to make some kind of automapper, which fills the properties, but I need to check the type of the variable but that seems to be impossible.
class BaseDto
{
public function __construct($json)
{
$jsonArray = json_decode($json, true);
foreach($jsonArray as $key=>$value){
$type = gettype($this->$key);
if($type instanceof BaseDto)
$this->$key = new $type($value);
else
$this->$key = $value;
}
}
}
ContactInputDto:
class ContactInputDto extends BaseDto
{
public string $firstname;
public string $lastname;
public string $street_housenumber;
public string $postal_code;
public string $place;
public string $country;
public string $email;
public string $phone;
}
Is it somehow possible to make that line "gettype($this->$key)"
work, without php throwing the following error:
Typed property App\Dto\CreateMembershipInputDto::$is_gift must not be accessed before initialization
While the manual does not seem to currently document it, there is a method added to ReflectionProperty
to allow you to get the type. This is actually specified in the RFC for typed properties
Here's how you would use it:
class CreateMembershipInputDto extends BaseDto {
public bool $is_gift;
public int $year;
public string $name;
public \DateTime $shipping_date;
public ContactInputDto $buyer;
public ?ContactInputDto $receiver;
}
class BaseDto
{
public function __construct($json)
{
$r = new \ReflectionClass(static::class); //Static should resolve the the actual class being constructed
$jsonArray = json_decode($json, true);
foreach($jsonArray as $key=>$value){
$prop = $r->getProperty($key);
if (!$prop || !$prop->getType()) { continue; } // Not a valid property or property has no type
$type = $prop->getType();
if($type->getName() === BaseDto::class) //types names are strings
$this->$key = new $type($value);
else
$this->$key = $value;
}
}
}
If you want to check if the type extends BaseDto
you will need (new \ReflectionClass($type->getName()))->isSubclassOf(BaseDto::class)
Note that getName
refers to to ReflectionNamedType::getName
. Prior to PHP 8 this was the only possible instance you could get $prop->getType()
however starting PHP 8 you may also get a ReflectionUnionType
which contains multiple types