<?php
error_reporting(E_ALL);
class DtoException extends Exception
{
}
abstract class Dto
{
private static $propertyNamesByClass = array();
private function __construct(array $propertiesKeyValues)
{
foreach($propertiesKeyValues as $propertyName=>$propertyValue)
if(Static::hasPropertyName($propertyName))
$this->set($propertyName,$propertyValue);
}
public function __call($method, array $arguments)
{
$getOrSet = substr($method, 0, 3);
if($getOrSet != 'get' && $getOrSet != 'set')
throw new DtoException('"'.get_class($this).'" has no method "'.$method.'"');
$propertyName = strtolower(substr($method, 3));
if(!Static::hasPropertyName($propertyName))
throw new DtoException('"'.get_class($this).'" has no property "'.$propertyName.'"');
$getOrSetMethod = array($this,$getOrSet);
array_unshift($arguments,$propertyName);
return call_user_func_array($getOrSetMethod, $arguments);
}
public function __get($propertyName)
{
if(!Static::hasPropertyName($propertyName))
throw new DtoException('"'.get_class($this).'" has no property "'.$propertyName.'"');
return $this->get($propertyName);
}
public function __set($propertyName, $propertyValue)
{
if(!Static::hasPropertyName($propertyName))
throw new DtoException('"'.get_class($this).'" has no property "'.$propertyName.'"');
return $this->set($propertyName, $propertyValue);
}
public static function createFromArray($propertiesKeyValues)
{
return new Static($propertiesKeyValues);
}
public function set($propertyName, $propertyValue)
{
$this->$propertyName = $propertyValue;
}
public function get($propertyName)
{
return $this->$propertyName;
}
public static function getPropertyNames()
{
$className = get_called_class();
if(isset(self::$propertyNamesByClass[$className]))
return self::$propertyNamesByClass[$className];
$reflected = new ReflectionClass($className);
$properties = $reflected->getProperties(
ReflectionProperty::IS_PUBLIC
);
$propertyNames = array();
foreach($properties as $property) {
$propertyNames[] = $property->getName();
}
self::$propertyNamesByClass[$className] = $propertyNames;
return $propertyNames;
}
public static function hasPropertyName($propertyName)
{
$propertyNames = Static::getPropertyNames();
return in_array($propertyName,$propertyNames);
}
public function asArray()
{
$values = array();
foreach (Static::getPropertyNames() as $propertyName) {
$values[$propertyName] = $this->get($propertyName);
}
return $values;
}
public function __sleep()
{
$propertyNames = self::getPropertyNames();
foreach ($propertyNames as $propertyName) {
$propertyValue = $this->get($propertyName);
$this->set($propertyName, $propertyValue);
}
return $propertyNames;
}
public function __wakeup()
{
$propertyNames = self::getPropertyNames();
return $propertyNames;
}
}
class Person extends Dto
{
public $name;
public $surname;
}
class Test
{
const PERSON_NAME = 'name';
const PERSON_SURNAME = 'surname';
public function testProvideDtoPropertyNames()
{
$propertyNames = Person::getPropertyNames();
$expectedProperties = array(
self::PERSON_NAME,
self::PERSON_SURNAME,
);
$this->assertEquals(
$expectedProperties,
$propertyNames
);
}
public function testProvidePropertyViaGeneralGetter()
{
$dto = Person::createFromArray(array(
self::PERSON_NAME => 'Simone',
));
$this->assertEquals(
'Simone',
$dto->getName()
);
}
public function testDTOAcceptOnlyItsOwnProperties()
{
$dto = Person::createFromArray(array(
self::PERSON_NAME => 'Simone',
'non existent property' => 'Simone',
));
$expectedProperties = array(
self::PERSON_NAME => 'Simone',
self::PERSON_SURNAME => null,
);
$this->assertEquals(
$expectedProperties,
$dto->asArray()
);
}
public function testSerializationKeepSameProperties()
{
$properties = array(
self::PERSON_NAME => 'Simone',
self::PERSON_SURNAME => null,
);
$dto = Person::createFromArray($properties);
$serialized = serialize($dto);
$unserialized = unserialize($serialized);
$this->assertEquals(
$dto->asArray(),
$unserialized->asArray()
);
$this->assertEquals(
$properties,
$unserialized->asArray()
);
}
public function assertEquals($a,$b)
{
$serializeA = serialize($a);
$serializeB = serialize($b);
echo ($serializeA==$serializeB?'Pass':'FAIL').' : '.$serializeA.' ; '.$serializeB."\n";
}
}
$test = new Test();
$test->testProvideDtoPropertyNames();
$test->testProvidePropertyViaGeneralGetter();
$test->testDTOAcceptOnlyItsOwnProperties();
$test->testSerializationKeepSameProperties();
- Output for 5.6.0 - 5.6.40, 7.0.12 - 7.0.33, 7.1.0 - 7.1.33, 7.2.0 - 7.2.34, 7.3.0 - 7.3.33, 7.4.0 - 7.4.33, 8.0.0 - 8.0.30, 8.1.0 - 8.1.28, 8.2.0 - 8.2.18, 8.3.0 - 8.3.4, 8.3.6
- Pass : a:2:{i:0;s:4:"name";i:1;s:7:"surname";} ; a:2:{i:0;s:4:"name";i:1;s:7:"surname";}
Pass : s:6:"Simone"; ; s:6:"Simone";
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
- Output for 8.3.5
- Warning: PHP Startup: Unable to load dynamic library 'sodium.so' (tried: /usr/lib/php/8.3.5/modules/sodium.so (libsodium.so.23: cannot open shared object file: No such file or directory), /usr/lib/php/8.3.5/modules/sodium.so.so (/usr/lib/php/8.3.5/modules/sodium.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
Pass : a:2:{i:0;s:4:"name";i:1;s:7:"surname";} ; a:2:{i:0;s:4:"name";i:1;s:7:"surname";}
Pass : s:6:"Simone"; ; s:6:"Simone";
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
- Output for 7.0.0 - 7.0.11
- Pass : a:2:{i:0;s:4:"name";i:1;s:7:"surname";} ; a:2:{i:0;s:4:"name";i:1;s:7:"surname";}
Pass : s:6:"Simone"; ; s:6:"Simone";
Pass : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;}
Pass : a:0:{} ; a:0:{}
FAIL : a:2:{s:4:"name";s:6:"Simone";s:7:"surname";N;} ; a:0:{}
preferences:
249.31 ms | 402 KiB | 300 Q